Monday 26 June 2017

Blendo 3

Major Innovations

Blendo makes several innovations over previous efforts. Some of these enhance an author's ability to easily create rich media content. Others make Blendo appropriate for a wide range of applications. Still others give authors tools to produce effects not available in previous systems. Blendo innovates in the following areas:

Surfaces

A surface is a 2 dimensional pixel array upon which one or more assets are rendered. A rendered surface can be used for subsequent compositing with other surfaces and multiple surfaces can be combined using image processing operations. A surface can also be used as the source pixels for simple texturing operations, or as one of a number of assets in a more complex multitexturing operation. All these various operations can be combined to produce richly composed, dynamic imagery in real time. At the base of the surface hierarchy is the SurfaceNode. From this is derived the ImageSurface, containing a static, file based image, the MovieSurface, containing a stream based movie, and a SceneSurface, containing a child scene graph. These provide the basic building blocks for most simple surface operations. A Texture node has a surface field which can contain any of the above surface types. This allows, for instance, a static image to be rendered on the face of a spinning piece of geometry while a movie is rendered on another. The resulting scene graph can be placed in a SceneSurface, which can be used as the texture of a third geometric object. The result is an arbitrary piece of geometry textured with an animated scene.
For more complex operations there is a MatteSurface. This combines two source surfaces into a third surface, using a specified combination operation. This allows the blending of two images using a constant blend operation or the alpha channel from one or both surfaces. By combining MatteSurface nodes, an animated monochrome image sequence can be used to perform a complex wipe between two surface. These DVE effects are very useful for professional quality presentations and can be done easily and efficiently with MatteSurface.
By using surfaces as the sources for a multitexturing operation, a complex combination of blended images and layered textures can be applied to objects for rich, photo realistic effects.
Another use for surfaces is in the ApplicationSurface node. This node takes an arbitrary URL as its field, referencing any one of a number of file or stream formats. If supported, the data contained in reference is rendered into the surface. This can be done using a built-in renderer, or by spawning an external application, setting its rendering destination to the given surface. This presents the possibility of rendering any conceivable data inside the Blendo scene.

Routing

Blendo maintains the notion of routing from VRML97, but adds a new, more compact syntax to the Blendo text format. This new syntax adds the ability to place routes directly in the source or destination node. For instance:
    DEF SENSOR IntervalSensor { ... }
    DEF PI PositionInterpolator {
        key ...
        keyValue ...
        fraction FROM SENSOR.fraction
        value TO T.translation
    }
    DEF T Transform { ... }
in the PositionInterpolator above, the lines:
    fraction FROM SENSOR.fraction
    value TO T.translation
are equivalent to the VRML97 constructs:
    ROUTE SENSOR.fraction TO PI.fraction
    ROUTE PI.value TO T.translation
The compact routing syntax aids the author by being easier to type, easier to see the routing relationship, and allows for fewer DEF names.

Timing model

Blendo has a strong timed media capability. Rather than supporting a set of nodes with related timing semantics (AudioClip, MovieTexture, TimeSensor), Blendo introduces a single TimeBase node. This is included as a field of any timed node and supplies a common set of timing semantics to all media. In fact, through the familiar node instancing mechanism of VRML, the same TimeBase can be used for a number of related media nodes, ensuring perfect synchronization. In addition to the common timed nodes for audio, video and animation, Blendo also has a set of nodes used for sequencing media events. The Score is a timed node and therefore derives it timing from a TimeBase. The Score has list of Cue nodes which emit events at the time specified. For instance, the following example emits a string to pre load an image asset, then performs an animation using that image, then runs a movie:
    Score {
        timeBase DEF TB TimeBase { }
        cue [
            FieldCue {
                cueValue String "image1.png"
                cueOut TO ISURF.url
            }
            IntervalCue {
                delay    0.5
                period   2.5     # 2.5 second animation
                fraction TO PI.fraction
            }
            DEF MC MediaCue {
                offset 2
            }
        ]
    }

    # Slide out image
    DEF T Transform {
        children Shape {
            appearance Appearance {
                texture Texture {
                    surface DEF ISURF ImageSurface { }
                }
            }
            geometry IndexedFaceSet { ... }
        }
    }
    DEF PI PositionInterpolator {
        key ...
        keyValue ...
        value TO T.translation
    }

    # Movie
    Shape {
        appearance Appearance {
            texture Texture {
                surface MovieSurface {
                    url "myMovie.mpg"
                    timeBase USE MC
                }
            }
        }
        geometry IndexedFaceSet { ... }
    }
All Cue nodes in a Score fire relative to the media time of the TimeBase. In the above example, the FieldCue fires as soon as the TimeBase starts because it has default offset and delay fields. The IntervalCue then start 0.5 seconds later and runs for the next 2.5 seconds, increasing its fraction output from 0 to 1. The MediaCue starts 2 seconds after the TimeBase starts, or when the IntervalCue is 1.5 seconds into its animation. This example shows the ability of the Cues to be offset from each other or from the TimeBase and shows that a subsequent Cue can start before the last one has finished. The MediaCue gives a powerful synchronization tool to the author. MediaCue is a form of Cue which behaves exactly like a TimeBase. In fact, a MediaCue can be used anywhere a TimeBase can, as shown in the above example. But since a MediaCue is embedded in a timed sequence of events, an implementation has enough information to request pre loading on an asset. For instance, in the above example, if the implementation knows that a movie takes 0.5 seconds to pre load and play instantly, it can send a "get ready" signal to the MovieSurface 1.5 seconds after the start of the TimeBase. This would give it the required 0.5 seconds to pre load. When the request came in to start playing, the movie would start instantly.
The combination of the TimeBase and media sequencing capabilities make Blendo a powerful tool for creating presentations with complex timing.

Event model

There has been a large body of work on the VRML event model from a number of groups. In its own research, the Blendo team has found that a simple event model is sufficient for the large majority of tasks. Therefore Blendo has very simple built-in event semantics. First of all, Blendo replaces the notion of field, exposedField, eventIn and eventOut with a single concept of field. Any field can be initialized, read or written and routed to or from. The model used for event propagation in Blendo is that of event listeners on output fields. When a field value is changed, its listeners are notified in the order in which they were added. This causes events to follow a depth first order. In cases where multiple events into a node need to be delivered before any processing, an eventsProcessed( ) method as in VRML 97 is used. A node containing an eventsProcessed( ) method is added to an event list upon receipt of the first event to that node in a given cascade. A cascade is ended when the last event resulting from the initiating stimulus is processed. The accumulated eventsProcessed( ) methods are then called in the order in which they were added to the list. These occur in subsequent cascades and therefore each has a unique timestamp. In VRML 97 there are a number of other post cascade events. For instance, a newly added or newly stimulated ProximitySensor produces one of these events. In Blendo, all these events are added to the event list along with eventsProcessed( ) and are executed, each with a unique timestamp, in the order they appear in the list.
The above event model description allows for deterministic behavior across platforms and runtime engines. But there are a small number of cases where the author needs to exert more control over the event propagation. This could be as simple as handling the events going into a single node specially, to producing a breadth first, prioritized event ordering for every node. Blendo provides authoring tools to handle this situation by allowing the default behavior handler to be subclassed. This new handler can then replace the default to obtain the event behavior desired.

Object model

Blendo introduces the concept of a true object model to the system architecture. At the core of the system is the Object, from which all other objects are derived. Unlike VRML both fields and nodes exist in the same object hierarchy. Since all objects are created using the prototyping mechanism (see below), it is possible to create prototypes of fields as well as nodes. This unifying principle allows fields to be fully typed, whether holding node values or more primitive field values. For instance, the Shape node would be described as follows (using the abstract VRML node syntax):
    Shape {
        field Appearance   appearance NULL
        field GeometryNode geometry   NULL
    }
In VRML, the above fields would have both had the type SFNode, and there would have been no semantic way to distinguish the actual node types. In Blendo the above makes it clear that the appearance field must be of type Appearance or one of its derived types. The geometry field would be of type GeometryNode, which is an abstract type. Blendo uses the convention of adding the suffix Node to abstract types to distinguish them from concrete types. An IndexedFaceSet node is derived from GeometryNode, so it would be legal in the geometry field. But the Group node is not derived from GeometryNode, so it would not be allowed. A Blendo object can be derived from multiple objects using a prototype aggregation mechanism. This allows a single object to present the interface of several objects. This capability is important in several situations. For example, the MovieSurface node is derived from both the SurfaceNode and the AudioSourceNode. This allows it to be used in the surface field of a Texture node (allowing the movie to be textured onto the surface of a shape), and in the source field of an AmbientSound node (allowing the sound of the movie to be heard).
The Blendo Node Reference section shows a complete hierarchy of each node in the system.

Prototyping

The ability to create new objects is provided by the prototyping facility. Prototyped objects can be implemented in Blendo directly, or in one of the supported external languages. For instance, the following example shows how to add a new field type, node type and how to use them:
    PROTO PosShape : Field [
        field Vec3f position 0 0 0
        field Shape shape    NULL
    ]
    PROTO ShapeArray : ChildNode [
        field MF PosShape array [ ] {
            G.children.setNum(0); // clear children list
            for (var i = 0; i < array.length(); ++i) {
                var t = new Transform();
                t.translation = array[i].position;
                t.children[0] = array[i].shape;
                G.children[i] = t;
            }
        }
        DEF G Group { }
    }

    ShapeArray {
        array [
            1 2 3 DEF S1 Shape { ... }
            4 5 6 USE S1
            7 8 9 USE S1
            6 5 4 DEF S2 Shape { ... }
            3 2 1 USE S2
        ]
    }
In the example above, PosShape is a new field type containing a Vec3f and a Shape node (or node derived from Shape). This is used as a field type in ShapeArray, which is a node containing an array of shapes positioned by the position field of PosShape. ShapeArray is derived from ChildNode because this is the base class for all nodes allowed at the top level or as children of grouping nodes. Note how Blendo omits the SF prefix from single value field names. For multiple value fields, the prefix MF is used. This allows any built-in or authored object type to be used as a single or multiple value field and resembles the familiar VRML 97 style. This ability to aggregate the capabilities of fields as well as nodes gives Blendo both a symmetry which is easy to understand and a powerful tool to construct clear application specific data structures.

Built-in scripting

In the example in the previous section, the ShapeArray node specifies an implementation for the array field, using the built-in scripting language. This language is a simplified version of ECMAScript. Features of ECMAScript missing include the ability to create arbitrary objects and arrays. Also missing are the complex scoping rules of ECMAScript and the with statement. All these omissions allow the language interpreter to be much smaller while allowing the author to make use of existing ECMAScript experience. In Blendo, object instances with initialized fields receive initial events with the given values on those fields. In the example above, when the node is initialized, the array field receives an event with 5 PosShape values. It uses these to construct Transform nodes, to hold the position and shape. These are then made children of the Group node. This demonstrates the simplicity and power afforded by the built-in scripting language.

Extensibility

In addition to built-in scripting, a Blendo prototype can be expressed in any supported external language. For instance, a Java implementation would appear as follows:
    EXTERNPROTO MyNode "mynode.class"

    MyNode {
        aField 1 2 3
        bField "Hello, world"
    }
This would instantiate MyNode, which has a pure Java implementation. Note that the EXTERNPROTO syntax does not include a specification for the field types. This makes authoring much simpler and since Blendo performs node initialization in a separate pass from parsing, the syntax is legal. In this case, before MyNode is instantiated the Java class is loaded and initialized. This initialization defines the fields of the node type by creating a prototype for the MyNode object type. An instance of MyNode is then created, the prototype is used to look up the field names, and the fields are initialized. Any errors, such as misspelled or mistyped fields, are detected at this point. The implementation of the Java class would be quite simple. The Object class is subclassed and the getProto( ) method is implemented. This method instantiates a Proto object and then initializes it with the appropriate field names, types and initial values. It also adds types from which this object is derived. This is necessary even though Java has its own class hierarchy because the derivation tree must be available throughout the Blendo runtime system, and this is done through the Proto objects. When an event is sent to a field of MyNode, a notify( ) method in the Java class is called, the appropriate processing is performed and events are potentially emitted.
This simple mechanism is used for nodes implemented in C++ or any other supported external language.
Blendo also supports an anonymous prototype syntax. If the prototype name is omitted from either a PROTO or EXTERNPROTO statement, then it must be followed by a node instance body (field initializations enclosed within braces). For instance, the above example could be rewritten:
    EXTERNPROTO "mynode.class" {
        aField 1 2 3
        bField "Hello, world"
    }
This form is more compact and easily readable when an object is included or defined which will be used only once. It makes the creation of simple scripts very simple. For instance, to add 1 to the y component of a Vec3f as it goes from a PositionInterpolator to a Transform is simple:
    DEF PI PositionInterpolator {
        key ...
        keyValue ...
        value TO S.in
    }
    DEF S PROTO [
        field SFVec3f out
        field SFVec3f in {
            out = new Vec3f(in.x, in.y+1, in.z);
        }
    ] { }
    DEF T Transform { 
        translation FROM S.out 
    }
Note the use of the FROM keyword in the Transform node. The Blendo compact route syntax allows the specification of routes in both directions for convenience. The anonymous prototyping syntax allow prototypes to be used in place of Script nodes (which have been removed from Blendo), with the same compactness and clarity. For complete consistency, the EXTERNPROTO statement is also used to include Blendo files. In that case, the top level interface of the Blendo file being included becomes the field interface of the prototype. This makes the field storage and access mechanisms very consistent across prototypes contained directly inline, in separate Blendo files, or implemented with an external language. This makes the implementation more compact and consistent in its behavior.

Compatibility

Even though Blendo makes significant architectural changes, it has a high degree of compatibility with VRML 97. The redesigned prototype semantics, removal of the Script node and simplified event model were done with compatibility in mind. Incompatibilities will most likely arise from some poorly defined areas of the VRML specification, especially in the areas of event handling and IS mapping. But it is expected that these will be rare and will be easily fixable. The result will be improved content that will behave consistently across platforms. For syntactic differences, converters will be supplied. In some profiles and levels, these will be invoked automatically to provide transparent VRML 97 compatibility. The biggest syntactic issues arise in the areas of prototypes and Script nodes. As an example of the conversion of these, here is a side-by-side comparison:
 
#VRML V2.0 utf8

PROTO NodeA [
    eventIn      SFFloat  in1
    field        SFVec3f  field1  0 0 0
    exposedField MFNode   efield1 [ ]
    eventOut     SFVec3f  out1
]
{
    DEF T Transform {
        translation IS field1
        children IS efield1
    }
    DEF S Script {
        field    SFFloat last 0
        eventOut SFVec3f out  IS out1
        eventIn  SFFloat in   IS in1
        url "vrmlscript:
            function in(val,ts) {
                v = (val-last);
                out = new SFVec3f(1,v,1);
                last = val;
            }
        "
    }
    ROUTE S.out TO T.scale
}

NodeA { field1 1 2 3 }
#X3D V1.0 utf8

PROTO NodeA [
    field Float   in1     ;
    field Vec3f   field1  0 0 0
    field MF Node efield1 [ ]
    field Vec3f   out1    ;


    DEF T Transform {
        translation FROM field1
        children    FROM efield1
    }
    DEF S PROTO [
        field Float last 0
        field Vec3f out  TO out1
        field Float in   FROM in1

        {
            v = (in-last);
            out = new Vec3f(1,v,1);
            last = val;
        }
    ] { }

    ROUTE S.out TO T.scale
}

NodeA { field1 1 2 3 }
It is clear from the above example that the syntactic differences are minor in most cases. Since Blendo uses a single field notion rather than separate eventIns and eventOuts, a syntactic element, the semicolon has been introduced into the Blendo text format to allow the initial value of fields to be omitted. This can be placed after a field name to indicate that an initializer is not present.
Note that Blendo replaces IS mapping with routing statements. This makes the semantics much more clear, but causes one minor compatibility issue. In VRML 97 it is possible to change the value of an exposedField internally. Since VRML 97 was not very flexible in how exposedFields could be used in a prototype, their use is rare. But in the above example, if the children field of the Transform were changed, an event to efield1 would be generated in some implementations. In the conversion to Blendo, this event would not be sent, since there is only a route from efield1 to children. This conversion was chosen because it is much more common (and more well specified) for an event to efield1 to change children.
It is these sorts of "edge conditions" where compatibility problems will appear. But these are also where the compatibility problems between existing VRML 97 browsers occur. Converters can be written to maximize the compatibility of migrated content. The small amount of remaining worlds can be converted by hand.