wiki:Concepts

AVANGO Concepts - Fields, Containers and Connections

Fields and field containers

Fields are the main concept of AVANGO's programming model. All AVANGO nodes are field containers - which means that they can contain fields. Fields in AVANGO nodes can be seen similar to instance properties in ordinary object-oriented programming. An AVANGO node can have any number of fields. Each field has a name and a value of a specified type. The field values of a node fully describe the node's state. AVANGO offers a set of field types, ranging from simple scalar types to vectors, matrices and other multi-valued structures.

One main difference between ordinary object properties and fields is that AVANGO is aware of fields, which allows to automatically provide a set of services for you:

Consistent programming model
The field mechanism gives you an easy way to define your own nodes. If your node implements a service, make it available through fields. In some cases, this may be unfamiliar in the beginning, but it's always possible. And in many cases, it is more elegant than one might think. AVANGO gives you opportunities to implement your services correspondingly. As a nice side effect, the field interface helps you keeping your service independent from other components.
Automatic serialization
Since the field set of an AVANGO node fully describe its internal state, AVANGO can automatically serialize and unserialize nodes. This mechanism is used, for example, for automatic distribution of partial scenes over a network for collaborative applications.
Core - script bridging
AVANGO fields are available to both low-level (C++) and high-level (Python) programs. AVANGO automatically makes them available to both levels - you don't have to (and should not) do anything about it manually. Fields are registered to the AVANGO runtime during node initialization. Usually one line, that's all.
Field connections
The best feature of fields is that they can be connected. Field connections forward values from one node to another, allowing you to specify a behavior network throughout your scene, independent of the scene graph hierarchy. Field connections are described in detail below.

Dynamic fields

Field containers are dynamic, which means that you can add fields to your nodes at runtime, even after the creation of a node. Fields that were added after node creation used to be called external fields, because they are often used as tags to mark a node for external services. However, there is technically no difference between static and dynamic fields. This mechanism is powerful, but may add runtime complexity to your application. Note: The current AVANGO version does not yet support network distribution of fields created after node initialization. Note 2: Fields can be dynamically added, but there is no facility to remove fields from an instance.

Single field and multifield types

Most AVANGO types come in two variants: Single- and multi-valued. They can be identified by their prefix SF or MF, respectively. Single fields contain may contain a single value whereas multifields contain a variable-length array of the designated type. For example, a field of type SFFloat may contain a single float value, a field of type MFFloat contains multiple float values in an ordered list. In Python, multifield values can be accessed using Python's array facilities.

Fields Connections

Field connections forward values from one field to another, from a node to another node. They provide the glue for combining node's behavior to the overall application interaction behavior. This powerful mechanism allows you to combine simple mechanism to impressively complex structures.

For example, you have written a script node that implements a proximity sensor. It has four fields: Two input position fields, Pos1In and Pos2In for the positions of two objects (type: avango.osg.SFVec3, a three-dimensional vector), a distance threshold input field, DistanceThresholdIn and an output field, ProximityOut (type: avango.SFBool - a boolean value). Whenever the distance of the two input vectors falls below the distance threshold, the output will be set to true, false otherwise.

Without connections, this node does not make any sense, since you cannot see it itself. With field connections, it can be used, for example, to trigger an action when an avatar reaches a certain area in a scene. In Python, the corresponding code could look like this:

point_of_interest = avango.osg.SFVec3(100,0,100)
radius = 50
sensor = avango.nodes.MyProximitySensor()
sensor.Pos1In.connect_from(avatar.Position)
sensor.Pos2In.value = point_of_interest
sensor.DistanceThresholdIn.value = radius
my_action.trigger.connect_from(sensor.ProximityOut)

The code assumes that there is an avatar node with a Position field (that contains the current avatar's position) and a my_action node with a Trigger field (that triggers the action). Whenever the avatar reaches a region of 50 units around point_of_interest the action is triggered.

To connect a field to another, use <destinationfield>.connect_from(<sourcefield>). This will establish a directed connection from source to destination - whenever the value of the source field changes, the new value will be forwarded to the destination field.

The code sample shows that <node>.<fieldname> specifies a field of a node itself. To access the field value, use <node>.<fieldname>.value. It also shows that fields do not need to be connected - you can also just set a static value. Unconnected fields simply remain as they are.

AVANGO does not distinguish between input and output nodes - you can even use fields for both. However, it's a good practice to use fields either for input or output and to give them corresponding names. Using In or Out as suffix is good common practice.

Competing connections

It's fine to use the same field as source for multiple connections. However, using one field as the destination for multiple connections can be problematic because it might lead to problems when multiple connections try to push different values into the destination field. As a consequence, multiple connections leading to one field are disabled by default: Connecting two fields will automatically remove all other connections leading to the destination field. It is possible to change this behavior, but it would still lead to hardly predictable results. The proper solution is to define and insert a merger node that combines the multiple inputs according to the application's requirements.

Evaluation order

AVANGO allows you to immediately react to a field change, using the field_has_changed() method. However, this is not recommended because it can impose deep propagation depth and may cause performance problems. Usage of field_has_changed() can almost always be avoided. You should try to react (lazily) to field changes during your evaluate() implementation, which is executed once per frame, before rendering, when a field value of your node has changed. Since AVANGO knows about the connections between nodes, it knows about internal dependencies and will call the node's evaluate() methods in order of dependency. This will ensure that changes will be quickly propagated throughout the scene. However, in case of cyclic dependencies, order of evaluation is undefined. For more information, see Effective AVANGO

Type conversion

If two fields of different types are connected, AVANGO will perform implicit type conversions for primitive types - between integers, booleans and floats, according to C++ conventions. Unconvertible connections are not allowed. Implicit conversions are convenient, however be aware that some types of conversion may not always yield the results you expect (i.e. converting from float to boolean).