Massive3 App Prog Intro
Massive3 App Prog Intro
Massive3 App Prog Intro
// SimpleAgent.C (A.1)
// Create Network, NetworkEventHandler and Agent
Network network;
NetworkEventHandler eventHandler(&network);
Agent agent(&eventHandler, "SimpleAgent");
// Run main loop
agent.addDefaultMainloopMethods();
agent.mainloop(0, 500000);
// SimpleAgent.C (A.2)
agent.addMainloopMethod(AGENT_CBLEVEL_INTERACTION-1,
exampleFrameCB, (void*)0);
The following code fragment registers a routine to be called as soon as possible after 5 seconds has elapsed.
The callback occurs only once and the timer is then automatically removed.
// SimpleAgent.C (A.3b)
Time delay(5,0);
agent.addMainloopMethod(AGENT_CBLEVEL_PENDING+1,
oneShotTimerCB, (void*)0,
delay, 0);
The following code fragment registers a routine to be executed every second, which is removed (from within
the callback) after 10 iterations. Note that the callback frequency will never be more than once per frame, even
if the frame time is longer than the requested interval.
// SimpleAgent.C (A.4b)
Time interval(1,0);
agent.addMainloopMethod(AGENT_CBLEVEL_PENDING+1,
repeatingTimerCB, (void*)&agent,
interval, 1);
Environments
An Environment is a shared database, and represents all or a portion of a virtual world. One Agent can create
a new Environment (or many Environments) and other Agents can then join that environment.
When an Agent joins an Environment it get a complete local copy of that Environment and its contents. Any
changes to one copy (replica) of an Environment will be reflected in all other copies of the Environment
(provided that they are legal operations, and subject to delays in network communication).
Simple Data Items
An Environment is a set of trees of individual data items. Each data item is an instance of a subclass of
ItemData, and is associated with the Environment via an instance of class EnvironmentItem. The basic data
item types are:
• GeometryData - a filename or URL indicating a 3D geometry to be rendered (currently this must be in the
DGL format, as generated by dgl_convert from AC3D, VRML 1 or some VRML 2 format files).
• EntityData - a 3D homogeneous matrix transformation, plus a cuboid extent (currently little used). A sub-
tree starting from an Entity typically corresponds to a virtual artefact or "thing". To be visible it must have
at least one child (or descendant) item that is a geometry. Entitys can also be nested, for example to create
articulated sub-components. An Entity’s transformation is composed with those of it ancestors (parent and
parent’s parent, etc.) to determine its final position and effect. Note that a homogeneous 3D transform can
represent any combination of translations, rotations, shears and scales. Refer to a text book on 3D graphics
for details.
• AttributeData - a text name and value (in ASCII, currently), which is used to annotate Entitys (e.g.
identify user embodiments) and communicate other information (e.g. audio session details).
• SwitchData - a note containing a single integer, which causes only that child node to be rendered. This
allows an Entity to change between different Geometrys for its appearance. Note that child nodes are
numbered in the order in which they are added, and count from 0.
The four item types can be combined to construct annotated and potentially articulated complex worlds or
portions of worlds.
This program segment could be added to the first example to create some simple contents within the a new
Environment. Note that the Environment is named, so that other Agents can locate it and join it.
env->addNewGeometry(&obj1ID, "star.dgl");
env->addNewAttribute(&obj1ID, "AName", "AValue");
Note that any graphical client program must be able to access the named geometry file in order to draw the
scene correctly.
Item IDs and Updates
Every item has a globally unique item identifier of class ItemID. In order to read, change or delete an item an
Agent must know its ItemID (it must also own the item, which will be considered later). For example, the
following code fragment locates the current value of an item by its ID. Note that the return value will be NULL
if the item is not found. Also, the item’s type must be checked explicitly before any type-specific access should
be performed.
if (itemValue==NULL) {
// error – item is not present
} else if (itemValue->type!=ITEM_ENTITY) {
// error – its type is not what we expect
} else {
// type-specific access
EntityData *entity = (EntityData*)itemValue;
// etc.
}
Note that it is an error to keep references to ItemData object which are part of the Environment; they will be
freed with no warning if the item is updated or deleted. Instead, an application should always keep the item ID,
and loop up the current value afresh each time it is required.
This alternative code fragment locates the EnvironmentItem record which ties the item to the Environment.
This allows the application to access additional internal information (e.g. sequencer values), or to iterate over
the children of the item. Again, note that the return value will be NULL if the item is not present (e.g. has
already been deleted).
// SimpleEnvironment.C - (E.3)
EnvironmentItem *envItem = env->envItemGet(obj1ID);
if (envItem==NULL) {
// error - item is not present
} else if (envItem->getItem()==NULL) {
// error – item has no valid ItemData present
} else {
// access item or EnvironmentItem...
ItemData *itemValue = envItem->getItem();
AgentID owner = envItem->getOwner();
// etc.
}
env->itemDeleteTree(obj2ID);
The following code fragment uses a recursive procedure to perform a depth-first traversal of the item hierarchy
of an environment.
Note that the code fragments, when used immediately after joining an Environment (as shown) may show few
or no items present; this is due to the way that Environments are progressively loaded (streamed) into a joining
application. This is slightly different to the delay in showing objects in a graphical user client, which also has
to progressively load geometry files before they can be rendered.
Changes and Callbacks
To detect when items and/or an Environment change a process must either:
• set up an Agent timer (or frame) callback, and periodically re-check the Environment’s contents, as above;
or
• set up an environment callback, which will call a designated function when a significant change occurs.
In most cases the second approach (environment callback) is preferred for reasons of efficiency and simplicity.
An environment callback is established by instantiating an object of class EnvCallback. Destroying this object
removes the callback. When a callback is created you must specify the events for which it is to be invoked.
This is specified in terms of a number of properties of the event. Each event is characterised by the following
properties:
• the item to which it applies,
• the type of the event (e.g. add item, update item, delete item), and
• the type of the item to which it applies (e.g. Entity, Geometry).
// SimpleEnvironment.C – (E.9)
EnvCallback *callback1 =
new EnvCallback(env, itemChangeCB, (void*)0,
EVENT_UPDATE | EVENT_DELETE | EVENT_DELETE_BOUND,
&obj3ID,
ENV_CALLBACK_OBJECT,
ITEM_ANY);
callback1->freeOnEnvDeleted();
The following code fragment monitors the addition, update and deletion of child items of type UpdateRequest;
these are used (normally with control locked items) to indicate a request to change a locked item (as described
already).
// SimpleEnvironment.C (E.10)
EnvCallback *callback2 =
new EnvCallback(env, itemUpdateRequestCB, (void*)0,
EVENT_ADD_ANY | EVENT_UPDATE |
EVENT_DELETE | EVENT_DELETE_BOUND,
&obj4ID,
ENV_CALLBACK_CHILDREN,
ITEM_UPDATE_REQUEST);
Notifications
As well as creating and manipulating items in the Environment, Agents can also send stateless events to an
Environment. The standard fields of a notification are:
• "type" - a string which identifies what kind of event it is (an empty string is the default – not NULL);
• "qualifier" - an integer which further identifies the kind of event (0 is the default);
• "info" - a string which can contain arbitrary information dependent on the kind of event is (an empty
string is the default – not NULL);
• "agent1" - an AgentID of an Agent named in the event, typically the source or subject;
• "agent2" - an AgentID of another Agent named in the event, typically the destination (if there is one);
• "item1" - an ItemID of an item named in the event, typically the obect of the event;
A callback can be registered for notification events in the same way as above. If item1 is specified then this is
considered to be the item to which the event applies (for the purposes of callback filtering). In addition, the
EnvCallback can include an EventNotificationData object which acts as a filter to select events: if fields of this
object are non-default values then they must be matched by the event before the callback will be triggered.
Type, qualifier, item1, item2, agent2 and agent2 are checked. The type field is checked as a prefix (i.e. it must
only match the start of the event’s type); this allows the use of hierarchically structured event name spaces for
filtering purposes.
The following code fragment establishes a callback which responds to all key press notification events.
// SimpleEnvironment.C – (E.12)
AgentID nullAgent;
ItemID nullItem;
EventNotificationData *keyFilter =
new EventNotificationData("/user/keypress", 0, "",
nullAgent, nullAgent, nullItem,
nullItem, NULL);
EnvCallback *callback3 =
new EnvCallback(env, keyPressCB, (void*)0,
EVENT_NOTIFICATION);