Introduction

Ygdrasil is a framework created for developing networked virtual environments. It is focused on building the behaviors of virtual objects from re-usable components, and on sharing the state of an environment through a distributed scene graph mechanism. It has been used in the construction of numerous artistic and educational applications.

Ygdrasil is built in C++, around SGI's OpenGL Performer visual simulation toolkit and the CAVERNsoft QUANTA networking library. Performer provides a hierarchical representation of the virtual world database, called a scene graph. The scene graph is a tree that encodes the grouping of objects and nesting of 3D transformations, and provides tools for operations such as switching elements on or off.

Ygdrasil focuses on the rapid construction of dynamic, interactive virtual worlds, so in addition to the basic graphical data as used in Performer, its scene graph nodes can have behaviors (i.e. functions to update the state of nodes) attached to them. The system includes a number of nodes that implement common virtual world components - sounds, user avatars, navigation controls, timers, triggers that detect when a user enters an area, etc. An event based message passing facility allows nodes to be tied together to assemble large complex worlds from simple components. Nodes in the virtual world, such as triggers, can generate events to initiate the passing of messages to other nodes (i.e. when(enter,myLight.on)).

Although the included nodes will accomodate a great number of interactions, it may be desirable to create new nodes to modify the existing behavior or bring in external data such as web-based content. Individual nodes are derived from a common class and use a plugin facility based on Dynamic Shared Objects (DSO's) to allow for the rapid modification or the addition of new nodes. New components can be easily added by taking one of the classes, such as a transformation node, and deriving a subclass with the new features.

The Ygdrasil framework is unique in that it seamlessly extends the scene graph to be shared across the network. In a shared system, each participant has his own local copy of the common scene graph. Node data, such as lists of nodes' children, transformation matrices, and model information, are automatically distributed among these participants as the data changes or when participants first join the shared world. Each particular node is considered to be owned by the host that creates it. As a result, specialized node behaviors can be calculated by the owning host and clients can join a world without needing anything beyond the standard, core node types.

Installation

Ygdrasil runs on Linux and IRIX unix based systems. A windows based version is in development. The following installation instructions apply to both IRIX and Linux based systems. Ygdrasil relies on users having the directory /usr/local/bin in their path. The bash shell usually includes this directory in the path of each user.

Performer

Ygdrasil uses SGI's OpenGL Performer simulation library. The software with a permanent demo license can be downloaded from www.sgi.com. Follow the installation instructions provided by SGI. The RPM based installation is recommended over using tar files. Once installed, the installation can be verified by typing

perfly

at the command prompt. If the Performer logo appears to move slowly, you may not be running with hardware acceleration. Type

glxinfo

and look for the phrase "direct rendering". If "no" is indicated then you may need to update your graphics card drive of XWindows configuration.

Bergen

Ygdrasil requires the Bergen Sound Server for playing sample files and other sound related functions. Download the latest version of Bergen from the downloads section. Login as root, and place the tar file in the installation location, usually /usr/local. Untar the file:

tar -xvzf bergen_0.6.X.tar.gz

Change directory into the directory created by the tar file. If installing in a location other than the default, then edit the Makefile to change the variable BERGEN_DIR. Execute the following:

make install

A symbolic link named bergen will be created at /usr/local pointing to the installation directory. And, symbolic links will be placed in /usr/local/bin that point to a number of Bergen executables.

Ygdrasil

Download the latest version of Ygdrasil from the downloads section. Login as root, and place the tar file in the installation location, usually /usr/local. Untar the file

tar -xvzf ygdrasil_0.4.X.tar.gz

Change directory into the directory created by the tar file. If installing in a location other than the default, then edit the Makefile to change the variable YG_DIR. Execute the following:

make install

A symbolic link named yg will be created at /usr/local pointing to the installation directory. And, symbolic links will be placed in /usr/local/bin that point to the Ydrasil executables. Versions of Ygdrasil distributed to the public require a valid CAVElib license to operate. In order to customize the simulator interface for use with the Ygdrasil graphical interface, a number of configuration lines are added to the default cave.config file located in /usr/local/CAVE/etc.

Some prior versions of Ygdrasil were installed directly into the /usr/local/yg directory. If you want to retain your prior version then move it to a version specific directory before starting the installation (i.e. mv /usr/local/yg /usr/local/yg_0.1.X). For installation on the IRIX operating system, add a line in etc/yg.config to set the ARCHITECTURE variable to sgi.

For the Windows version, login as administrator and extract the zip file to the C: drive. This will a create directory under Program Files called Ygdrasil with the yg_0.4.X directory below it. Browse to the win32 directory and double click on the the vcredist_x86.exe executable. For each user, login and browse to the win32 directory and double click on the install.bat batch file. This will create a My Ygdrasil directory under the My Documents directory of the current user.

Quick Start

Ygdrasil creates virtual worlds by interpreting scripts stored in text files. A number of example scripts, known as scene files, are included with the system. You can execute the example scene file named example1.yg by typing

yg example1.yg

at the command prompt. This example file creates a terrain, a spinning banana, and another banana that prints a message to the shell window when you point at it.

The window that appears is called the simulation window and allows you to interact with the world using the mouse and keyboard. Pressing the TAB key alternates between controlling the user hand and the user viewpoint with the mouse. The arrow keys turn the user left and right and move the user forwards and backwards. Pressing the F5 key on the keyboard prints out a list of simulator commands in the shell window.

The application can be quit by pressing the esc key while focused on the simulation window. In addition, a number of commands can be entered directly into the shell window during runtime. Typing graph and hitting return lists the contents of the current scene and typing quit also quits the application.

Ygdrasil also comes with a graphical development environment for creating scene files. The Ygdrasil GUI can be started by typing

yg

without a scene file at the command prompt. Interaction with the interface uses standard point and click mouse interaction. Right clicking brings up a context sensitive menu. There is an interactive tutorial that will walk you through the basic functions of the graphical interface.

Scenes

Ygdrasil scenes are created by defining any number of nodes in a scene file. The following script

ygEnvironment( skycolor(0.1 0.2 0.7) )
ygLight sun()
ygObject( file(ground.pfb) )


creates three nodes; a ygEnvironment node, a ygLight node named sun, and a ygObject node. In this example, skycolor and file are messages that initialize the properties of the environment and object nodes respectively. The three float values (0.1 0.2 0.7) and the string ground.pfb are arguments that set the red, green, and blue of the skycolor and the name of the model file to load respectively.

Nodes within scene files can be arranged in a tree structure with each node having a single parent and any number of children nodes. Replacing the last line of the previous example with the following lines

ygTransform( position(0 5 0) )
    {
    ygObject( file(ground.pfb) )
    ygObject( file(tree.pfb) )
    }


adds a ygTransform node with two object nodes as its children. Like many nodes in Ygdrasil, only the children of a transform are affected by its transformation. As a result, the ground.pfb and tree.pfb models are positioned 5 units down the Y axis from the origin of the world.

Syntax

Each node type created (i.e. environment, light, object, etc.) must be followed by an open parenthesis and a closed parenthesis. The following example creates a transform node:

ygTransform ( )

Each node can optionally be given a name by inserting it between the node type and the parenthesis as follows:

ygTransform myTransform( )

And, optionally, any number of messages can be given to the node by enclosing them within the parenthesis and separating them with commas as follows:

ygTransform myTransform( position(0 5 0), size(2) )

Finally, any number of node definitions such as above can be enclosed in braces to indicate that they are children of the node:

ygTransform myTransform( position(0 5 0), size(2) )
    {
    ygObject( file(ground.pfb) )
    ygObject( file(tree.pfb) )
    }


Throughout this documentation the following notation with be used:

( ) parenthesis groups objects
, comma indicates an AND relationship between objects
| vertical bar indicates an OR relationship between objects
[ ] sqaure brackets indicates objects are optional
{ } braces indicates objects are optional and may be repeated 0 or more times
' ' single quotes indicates that the actual character should be used


Scene file syntax is formally represented as follows:

node_type [node_name] '(' [message] [,message] ')' [ '{' {node_definition} '}' ]

When 2 nodes are given the same node name within a scene file, Ygdrasil will attempt to rename the second occurance by incrementing an integer value at the end of the node name. For example, a second occurance of myObject will be named myObject1 while a second occurance of myObject1 will be renamed myObject2.

Comments can be placed anywhere in the scene file by using either C++ style comments or standard C style comments. The C++ style comment indicator (//) can be placed anywhere in a line and will comment out the remainder of that line. All C++ style comments included in a scene file will be preserved when saving the scene from Ygdrasil. The C style comment indicator (/*) and terminator (*/) must be placed on separate lines, and C style comments will not be preserved when the scene file is saved. In addition, C style include directives (i.e. #include "file.yg") and macro substitution can be used within scene files.

Messages

Messages given to nodes usually consist of a message type followed by any number of arguments inside parenthesis. In the following example

ygEnvironment myEnvironment( skycolor(0.1 0.2 0.7), fog(off),clip )

the skycolor message type is given 3 floating point arguments, the fog message type is given a single string argument, and the clip message has no arguments. Individual message arguments can be separated by either a space or a comma. Each argument in a message has default values which will be used when the full number of arguments are not given. For example, the following message:

skycolor(0.1 0.2)

will set the skycolor to (0.1,0.2,0.0) because the default skycolor values are (0.0,0.0,0.0). The clip message actually takes 2 floats, but calling it without any arguments merely sets the values to the default. The documentation for the ygEnvironment node gives the following description:

fog (off | lin | linear | exp | exp2), three floats, float, float set the fog properties (type, color, onset, opaque)

which states that the first argument is one of five possible strings, followed by 3 floats for the fog color, a single float for the onset distance, and another float for the opague distance. Sending the fog message with only the first argument sets the remaining values to their defaults.

Messages place directly between the parenthesis of a node definition are called initial messages because they set the initial properties of the node when the world is created. By default, messages are directed to the node within which they are placed, but they can optionally be preceded by the name of another node

myEnvironment.skycolor(0.1 0.2 0.7)

in order to direct them to another named node within the scene. In addition, the execution of messages can be delayed by adding a plus sign after the message. In the following examples:

skycolor(0.1 0.2 0.7)+10
skycolor(0.1 0.2 0.7)+
skycolor(0.1 0.2 0.7)++
skycolor(0.1 0.2 0.7)+10f


the first skycolor message will be executed after a delay of 10 seconds, the second message will be delayed a single frame, the third message with be delayed 2 frames, and the final message will be delayed 10 frames.

The formal syntax for messages is as follows:

[node_name.] message_type [ '(' [argument] [ (, | ' ') argument] ')' ] [+delay_time]


Node names and message names are not case sensitive. However, all filesystem related items such as model, sound, and textures filenames are case sensitive.

Messages can be sent to any node during runtime by typing them into the shell window and hitting return or by setting up messages that are sent in response to events.

Events

Events are the means by which the virtual world is changed during runtime. The most basic events involve button presses or entering a designated region of the world. Although initial messages setup the initial properties of nodes, another class of messages callled event messages can be sent in response to an event during runtime. In the following example:

userTrigger( volume(sphere), when( enter, myEnvironment.skycolor(0.1 0.2 0.3) ) )

the when message sets up a message to be sent when the enter event occures. The userTrigger node establishes a region in the shape of a sphere within the world. When the user enters the region an enter event is generated and the attatched message will be sent to the environment node. The documentation for the userTrigger node gives the following description for the enter event:

enter user a user has entered the space

The second box indicates that when the enter event occures that information about the name of the user entering the space will be available. Variables passed with events can be resolved by placing them in the event message preceded by a dollar sign ($). In the following example:

when( enter, print(The user $user has entered), myEnvironment.skycolor(0.1 0.2 0.3) )

the print message is used to print a message to the shell window and the string $user will be replaced with the name of the user that entered the space. The when message allows any number of messages, separated by commas, to be attatched to an event. The following examples are all valid uses of the user variable delivered with the enter event:

when( enter, $user.teleport(0 0 0) )
when( enter, headAvatar$user.draw )
when( enter, myMoveToNode.node($user) )


Event variables can be used anywhere in the construction of messages as long as they have a separator such as a space, period, or parenthesis immediately following them. In addition to the variables passed with each event, the properties of a node are also availlable when an event occures by accessing the arguments of the messages that it can receive. For example, the ygTransform node generates an event called changed each time its properties are updated. We can get the node to print out its position

ygTransform( when( changed, print(the new position is $position0 $position1 $position2) ) )

by sending a message that accesses each argument in the position message. When a message has multiple arguments an index number at the end of the message variable indicates the desired argument.

Nodes

Ygdrasil includes nodes for geometry, matrix transformation, spatialized sound, user avatars, and an array of dynamic elements that include triggers, timers, and geometry modifiers. Each node is documented in the nodes section and has a working example scene that demonstrates its use. For example, the example file for ygObject can be executed by typing

yg ygObject.yg

at the command prompt. All included example scenes can be found in the data/scenes directory located under the /usr/loca/yg installation directory. The core Ygdrasil nodes all have names that begin with yg, yet each can also be created by dropping the first 2 letters in scene files. All other nodes are created from a DSO which allows for easy modification or addition of new nodes. The configuration section covers how to create and add new nodes to the Ygdrasil system.

Media such as models, textures, and sounds are searched for in a path that includes the data/models and data/textures directories in the Ygdrasil installation directory. By default, the current working directory and a subdirectory called data are searched first for content. The configuration section covers how to customize path to search other directories.

Each node in the documentation includes a list of the available messages for that node and a list of the events generated by that node. In addition, there are notes on usage and an outline of the comments included in the source code. The reset method in the comments section lists the default values for each message. The documentation is organized into sections labeled foundation, user, geometry, sound, transformation, selection, attributes, trigger, math, and system.

Foundation

The foundation section lists core nodes from which many other nodes are derived. This includes the ygViewer node that establishes a viewpoint and the ygSoundServer node from which all sound related nodes are derived.

ygNode

All Ydrasil nodes are derived from a single base node called ygNode. The ygNode provides a set of messages that can be sent to any Ygdrasil node. These messages include the print message, for printing to the shell window, and the reset message for setting the node back to the initial properties defined in the scene file. In addition, ygNode also provides the when message which is used for sending event messages when events occure. Although many nodes generate their own events, any Ygdrasil node can be set up to respond to an event and forced to generate that event by sending the message event. For examples of this functionality see the example scene for ygNode.

Messages are also provided by ygNode for creating new nodes and for changing the structure of the Ygdrasil scene graph during runtime. The create message creates a new node of the given node type and makes it a child of the current node. The addChild message can be used to remove existing nodes from the scene graph and make them a child of the current node. The removeChild message can be used to take a node out of the current scene graph.

Because all nodes are derived from ygNode, the documentation for any node only shows the messages that it is responsible for adding. When looking for the documentation on a particular message be sure to follow the links to parent nodes located at the top of the documentation page.

ygWorld

The ygWorld node is the node that manages the local virtual world. A ygWorld node is created automatically for each Ygdrasil application and all nodes created by a scene are children of the ygWorld node. Messages can be sent to the local copy of the ygWorld node by prefixing them with the node name "world". For example, the sending the message

world.quit

at anytime will quit the Ygdrasil application. The ygWorld node provides the load message for loading scene files during runtime. The current scene can also be saved by sending the save message with the name of the desired filename to save to. The delete message allows nodes and all of their children to be deleted from the scene graph during runtime.

ygSpace

The ygSpace node is the base node from which all nodes that establish a region of space are derived. The ygSpace node provides for the creation of rectangular, spherical, and cylindrical shaped volumes of space though the volume message. In addition, volumes can also be designated as infinite, encompassing all space, or as points, encompasing an infinitely small volume that can never be entered.

The userTrigger node and several other trigger nodes are derived from ygSpace. The ygEnvironment node is derived from ygSpace to allow for environmental effects to be limited to regions within the virtual world. In addition, most sound related nodes are also derived from ygSpace.

User

Networked virtual worlds are unique in that the majority of interaction with them happens through interaction between user avatars and the world itself. As a result, a virtual world without a user cannot be explored or interacted with. In addition, your user avatar establishes how you appear to other users with whom you are interacting in the virtual world. The user section lists nodes useful for creating a user presence. A default user avatar scene is loaded automatically whenever a scene is loaded. In order to create a customized version of your user, copy the file ygUser.yg from the scenes directory into your local directory and modify it.

The ygHead and ygWand nodes establish the position of the user head and wand respectively within the virtual world. The userTrigger node detects the entrance of the ygHead node into its volume while the wandTrigger node detects the entry of the ygWand node. Because virtual reality systems rarely track users over a large distance, a method of navigating the user throught the world other than actual walking is necessary. The ygUser node establishes a transform representing the user locatation, and the ygNavigator node is responsible for implementing a method of navigating this transform through the virtual world.

Ygdrasil uses the CAVElib simulation library for interfacing with tracking systems and controller devices such as the Wanda. The ygCAVENavigator node implements a navigation method called point-and-go that moves the user in the direction the wand is pointing. The ygCAVENavigator node also implements the collision detection strategy for walking on floor objects and preventing the user from moving through walls.

The ygCAVETracker node is used to bring in the position and orientation of sensors attatched to the user. When running Ygdrasil in the simulator window, the mouse and keyboard interaface is used to simulate tracking and controller sensor data.

The ygCAVEWand node is derived from ygWand and generates button related events related to the Wanda device. When operating in the simulator window, the ygCAVEWand node generates button1 and buttonUp1 events for the left mouse button. The second and third button events are similiarly mapped to middle and right mouse buttons.

Geometry

Ygdrasil provides nodes that create geometry directly or by loading model files. Most nodes in the geometry section are derived from a node called ygGeometry which provides messages for drawing attributes and collision attributes. The draw message makes geometry visible either to local users, remote users, both, or neither. The floor and wall messages make geometry a surface for walking and a surface that cannot be penetrated respectively. Collision detection with geometry depends on two factors, the status of the geometry related node and the collision or fly status of the user navigator node.

The ygScene node is a powerful node for inlining scene files multiple times. While the C style include directive will simply cut and paste a scene file into place, the ygScene node is capable of renaming references to node names when they are incremented in order to preserve the behaviors within the scene file. Because node names are incremented as they are encountered within a scene file, multiple nodes already having with incremental names within a scene file should be ordered in reverse order in order to prevent ambiguous references (i.e. timer5, timer4, timer3, etc.).

Models

The ygObject node loads models from a number of file formats. Because Ygdrasil is built upon the Performer scene graph library, it can take advantage of the built-in model loaders provided with it. Performer comes with loaders for the following model formats:

OpenGL Performer fast binary format *.pfb
OpenGL Performer ASCII format *.pfa
SGI OpenInvetnor format *.iv
VRML version 2.0 format *.wrl
MultiGen OpenFlight format *.flt
Alias Wavefront format *.obj
AutoDesk 3DStudio binary format *.3ds
AutoDesk AutoCAD ASCII format *.dxf

Several things should be considered when loading models into Ygdrasil:

Some information on specific loaders:

pfb, pfa

This is the native file format of Performer and is the only file format that Performer can save to. Performer includes a utility called pfconv that can be used to convert models into pfb and pfa formtas. All models are converted to pfb as they are loaded, so converting them beforehand can speed up loading times for your scenes. However, some IRIX systems may be using an older version of Performer, so always keep the original model files available in case they need to be converted into an older version of the pfb format. The pfa format is mainly useful for making small changes to models that are originaly in a binary format, but editing pfa files is ony recommended for those who know what they are doing.

The windows versin of Perfomer includes a plug-in that allows Maya 6.0 to export directly to the pfb format. The plug-in is not availlable as a download at this site because the Performer installation makes some configuration changes that allow the plug-in to work.

iv

Because Perfomer and Inventor are both SGI products, the OpenInventor format has a high likeliehood of being loaded correctly. Most recent versions of Maya can export to OpenInventor version 2.0. In Maya you may have to go to the plug-in manager and load the plugin for the exporter. In the export options be sure the normals are included and set the texture to sample the original file. If you allow Maya to resample the texture file, be sure to move the resampled file to the target system. Experience has shown that some larger model files and those containing numerous textures can be problematic for the iv loader. The inventor loader will not work without first installing OpenInventor on your system. The Inventor loader can load gif, jpg, and rgb image files.

flt

The OpenFlight loader has proven a capable format for loading as MultiGen was targeted as a product for Performer users. Most recent versins of Maya can export to OpenFlight. A binary editor such as HexEdit for windows will be necessary to overwrite the absolute path with a relative pathname.

wrl

Performer versions prior to 3.2 come with a loader that can load VRML version 2.0 files. The CosmoWorlds modeler and some other web-oriented modelers can save to the wrl format. Many VRML files downloaded on the web have been compressed using the gzip utility. These files need to be uncompressed before they can be loaded by Performer. Often the size of wrl model ends up very small when loaded into Ygdrasil, so be sure to try sizing the model up if it is not immediately visible. VRML version 1.0 files are actually the same as OpenInventor version 2.0 files. They can be loaded by changing the header to "#Inventor V2.0 ascii" and changing their file extention to *.iv. The VRML loader can only load image files in the rgb format.

obj


The Alias WaveFront file format is reliable for loading geometry alone but I have little experience with using it to load textured models. The obj format uses mtl material files, so be sure that you have the material files for your models.

3ds, dxf

The 3D Studio Max binary format has been used with some success. Because the 3ds format is not updated regularly by SGI, you may need to save your model into an older 3ds format to have it load properly. Be careful about polygon counts with Max because rounding off of corners may look good in a rendering but it produces significantly more polygons. The dxf format is an older format and is usually a second choice when both format exports are availlable.

Textures

The ygTexture and surface nodes can both create a rectangular polygon and load a texture onto it. These nodes allow content to be easily created without the need for modeling skills. The ygTexture node only creates two triangles and is not affected by lighting. The surface node can create a rectangle with a variable number of vertices and is affected by lighting and material changes. Both nodes only load images in the rgb format either run-length compressed or not. Performer tries to optimize the use of texture memory by keeping textures in powers of 2, so be sure to size your textures accordingly (i.e. 256x512,128x128,etc.). These nodes currently do not provide options for mip-mapping so these features but Performer does support them by creating your own geometry in a modeler.

Sound

This section lists several availlable nodes for creating, recording, and monitoring sound. Ygdrasil uses the Bergen Sound Server to play back and record sound using the sound card on the local machine or a remote machine. In combination with Ygdrasil, Bergen can spatialize and directionalize sounds located in the virtual world.

Sounds are spatilaized by adjusting their amplitude based on the distance between the user and the sound source. All sound related nodes are derived from ygSpace and, as such, allow a volume to be defined for them. The volume assigned to a sound node defines the area within which the amplitude is always at the maximum (i.e. a box for a room). Once outside the volume a falloff calculation determines the rate at which the amplitude decreases with distance from the sound. Sounds that should not be spatialized can use the default infinite volume, and sounds that should allways be spatialized (i.e. a bee) can be assigned a point volume.

Sounds are directionalized by playing them at different amplitudes in different speakers. This requires that Bergen be configured with the location of each physical speaker. Bergen can do stereo spatialization on Linux machines and Quad spatializatin on SGI Indigo and O2 machines. Ygdrasil calculates a distance from the center of the volume and Bergen uses this information to spread sound to all speakers when the listener is at the center of the volume. The ygSound node is the primary node for playing sampled sounds from files. Because sounds are directionalized by Bergen, sample files should be mono. And, because the default sampling rate is 22050khz, sound files be stored at a multiple of that frequency (i.e. 44100khz, 16bits, mono).

The soundRecorder and amplitude nodes monitor the microphone input of the sound card. They are not designed to record the sounds being played by Bergen. Spatialization of nodes that monitor sound input acts to decrease the amplitude of the recorded sound when the listener is at a distance from the recording node. Recordings that should not be spatialized in this manner can use the default infinite volume.

The ratSource node can spatialize and directionalize an audio stream using the Robust Audio Tool (RAT). Each user involved in a RAT session can add a ratSource node to their avatar and configure it with their RAT session identifier.

For more detailed information on how to configure Bergen see the configuration section.

Transformation

The transformation section lists nodes related to matrix transformation which is at the heart of OpenGL and other scene graph based rendering systems. Placing ygTransform nodes in a tree structure allows geometry to be translated, oriented, and scaled in logical groupings. A number of nodes derived from the ygTransform node provide functions such as pointing at objects, moving from location to location, and following paths stored in files. Several nodes in this section including the mover and pointFollower are designed to accept a set message that dictates the location of the transformation between a beginning and end location. The timer node described in the math section is useful for creating dynamic movement with these nodes.

For convenience, both the ygObject and ygTexture nodes create ygTransform nodes automatically when passed position, orientation, and size messages. In order to avoid the recreation of these nodes, be sure to change the name of the parent transform when changing the name of ygObject and ygTexture nodes in scene files.

Selection

After matrix manipulations, selection is the second most important feature of using scene graphs to organize geometry. Selectors, such as the ygSwitch node, allow sections of the scene graph to be turned off. When a ygSwitch node is turned off, all content associated with its children nodes will no longer be included in the world. The ygSelector performs a similiar function but allows the scene graph under only a single child node to be included in the world. Performer uses the scene graph structure to eliminate parts of the world from rendering that are not visible. Selection nodes can also allow you to keep frame rates high by turning off content when possible. The levelOfDetail node can select a lower resolution model when the user is farther away from an object and the visibility node can make portions of the world invisible when a user enters of leaves an area (i.e. the furniture become invisible when the user leaves the room).

Attributes

The attributes section lists nodes that can be used to change attributes of geometry such as textures and materials. Like most nodes in this section, the material node affects the material and transparency properties of all the nodes that are its children. Of note in this section are the viewTexture node which can render an alternate viewpoint onto an existing texture and the applyTexture node which can add smaller textures to an existing texture to create effects such as burn markes and transparent holes. Other attribute nodes such as clipPlane and stencilBuffer allow clipping planes and stencil masks to affect the rendering of geometry in other locations in the scene graph.

Video

The movieTexture node uses the openQuicktime library to play *.mov files on the texture of its children nodes. A separate ygSound node and appropriate sample file are required for sound playback of movies. The quicktime format used by openQuicktime is called PhotoJPEG. Premier and the Professional version of Apple Quicktime software can save mov files in the PhotoJPEG file format. Because they are loaded as textures, movies should be reformated to a power of 2 and can have their aspect ratio adjusted by transforming the size of the polygon they are played on.

Trigger

The trigger section lists nodes that trigger events based on interactions within the virtual world. The userTrigger and wandTrigger nodes detect the presence of ygHead and ygWand nodes respectively. The nodeTrigger node detects the presence of a particular type of node (i.e. detect(ygObject)). They each generate enter and exit events when the respective nodes they detect enter and exit their volume. Like all nodes derived from ygSpace, these nodes default to an infinite volume and, as a result, will detect thier respective nodes immediately upon startup if their volumes are not reduced.

The pointAtTrigger generates events when a ygWand is pointing at any of the geometry of its children nodes. For example, the following

pointAtTrigger( when(start, print(the user is pointing) ) )
    {
    ygObject( file(ground.pfb) )
    ygObject( file(tree.pfb) )
    }


will print out a message to the shell window when the user is pointing at either the ground or the tree. In addition to detecting the presence of the ygWand node, both the wandTrigger and pointAtTrigger nodes also generate any events being generated by the ygWand while they are active. For example, the

Math

The math section lists nodes that are used to make logical and arithmatic calculations. The most important nodes in this section are the value node and the timer node derived from it. The value node is a general purpose numerical array that can store any number of values and generates and event called changed when any of them change. The value node has messages for clamping the possible values in its array called maximums and minimums. And, it can also be configured to easily increment and decrement by particular deltas. The following is an example of a value node setup to hold the values (0.2 6.5 8.0):

value( values(0.2 6.5 8.0) )


The interpolate node is derived from the value node and is useful for performing simple linear calculations. The input message sets the values in the array according to the properties set by the startValues and endValues messages. An input message of 0.0 will set the values in the array to the startValues and an input message of 1.0 will set the values in the array to the endValues. In the following example

interpolate( endValues(0.2 60.0), when(changed, print(array values are: $values0 $values1), input(0.5) )


the input message will interpolate the resulting array half way between the start and end values. Having changed, the node will print out

array values are: 0.1 30.0

The timer node is an interplate node that sets the input between 0.0 and 1.0 over a duration. Sending the timer node the start message moves the input value forward from 0.0. The stop, forward, and reverse messages can be used to pause, continue, and reverse the timer respectively. Because the array can be configured to assume values between any start and end value, the timer node is ideal for manipulating values over time. In the following example

timer( duration(5), startValues(10 20), endValues(20 60),
           when( changed, myTransform.position(0 $values0 $values1) ) )

ygTransform myTransform( position(0 10 20) )


the timer will change the transform position from (0 10 20) to (0 20 60) over a period of 5 seconds. Note that the timer node constructs the position message by replacing $values0 and $values1 with the values in its array. The index values of the values variable is not related in any way to the fact that they are replacing the second and third arguments in the position message.

The boolean node is useful for keeping track of simple true/false relations and generates events to reflect changes in its state. Because it uses a very simple parsing mechanism for scene files, Ygdrasil organizes math nodes in the scene graph to form more complicated calculations and logical operations. For example, the following math nodes

and openDoor( when(changedTrue, doorTimer.start) )
    {
    greaterThan( )
        {
        value knobOrientation( )
        value( set(10.0) )
        }
    boolean doorLocked( false )
    }


will start a timer called doorTimer when both the knobOrientation is greater than 10.0 and the boolean node doorLocked has been set to true.

Examples

In addition to the example scenes for each node, there are also a number of additional example scenes located in the data/scenes directory located at /usr/local/yg.

example1.yg demonstrates using the pointAtTrigger and the timer node to create a spinning banana
example2.yg demonstrates using the wandTrigger, sound, and CAVENavigator nodes
widget_arm.yg demonstrates using the pointAtNode and the distance node to create an articulated arm for the user avatar
widget_leg.yg demonstrates using the moveToNode, the distance node, and timers to create an articulated leg for the user avatar
widget_velocity.yg demonstrates using the add, multiply, and mathematics nodes to calculate the user velocity
ygUser_articulated.yg
demonstrates using the ygScene node and mathematics to create user arms and legs
userMirror.yg demonstrates using the userPosition node and the interpolate node to create a distorted image of the the user avatar
pathSelector.yg demonstrates using the boolean, and, or, and nodeTrigger nodes to create a user puzzle
applyScale.yg demonstrates using the applyTexture node and interpolate node to map an "X" on a texture
spatialized.yg demonstrates each of the available sound nodes
stencilMirror.yg demonstrates how to use the reflection and stencilBuffer nodes to create a mirror

Configuration

Runtime options for Ygdrasil are controlled by a configuration file located in /usr/local/yg/etc called yg.config. Configuration file variables are defined as follows:

YG_PATH=models:textures:${YG_PATH}

where YG_PATH is the name of the variable being set and ${YG_PATH} can be expanded to the value of the current variable when necessary. The following configuration options can be adjusted in the yg.config file or superceded by a .ygrc file located in the user home directory or the local working directory where Ygdrasil is started:

variable default description
YG_PATH

data:${YG_DIR}/data:
${YG_DIR}/data/scenes: ${YG_DIR}/data/models:
${YG_DIR}/data/textures

location of models, textures, scene files, and sounds
(unless BERGEN_PATH has been set)
YG_DSO_PATH modules:${YG_DIR}/modules location of user and system dynamic shared objects
YG_DEBUG not defined
(i.e. "world.sendmessages net.* *.display ygSelector.select")
initialize node and system wide debug flags (also see ygWorld, ygNet, ygNetClient, ygDSOLoader)
YG_PRELOAD_CLASSES not defined
(i.e "clipPlane stencilBuffer")
some nodes will indicate in their documentation that they must be preloaded
YG_PF_DBASE_CONVERTERS not defined (i.e. "iv flt") preloading database converters can increase stability
YG_PF_FONT_DIR ${YG_DIR}/data/fonts location of fonts used by the graphical interface and text node
YG_NET_SERVER not defined set the name of the host running ygrepeater server
YG_NET_PORT 3500 set the port used to communicate with the server
YG_NET_REPEATER not defined indicate that ygrepeater should be started on the server
YG_NET_PROCESS_INTERVAL 0 (networking processed each frame) set a delay time between network processing
YG_NET_DOWNLOAD_DELAY 300 frames distributed nodes must wait before downloading
YG_NET_UPLOAD_DELAY 10 frames distributed nodes must wait before uploading
YG_NET_TRANSFORM_INTERVAL 0 (transforms updated each frame) set a delay time between transform updates
YG_NET_THREAD not defined (Linux systems thread by default) set to 0 or 1 for a separate thread for networking
YG_NET_NOTHREAD not defined set to 1 to prevent creating a networkng thread
YG_NET_DEFAULT_UNRELIABLE not defined set to 1 to make all comunications UDP
YG_NET_CACHE_SERVER not defined (defaults to YG_NET_SERVER) set the host machine for intial scene download

YG_NET_CACHE_PORT

not defined(default YG_NET_PORT+1)

set the port for initial scene download
YG_FILESERVER_HOST not defined
(usually YG_NET_SERVER)
set the host machine running the fileserver
YG_FILESERVER_PORT not defined set the port for the fileserver to use
YG_FILESERVER_DIRECTORY ./ set the location for downloads and uploads
YG_SERVER_DO_SLEEP not defined sleep to reduce spinlock when run with --noview
YG_MESSAGE_PORT 1967 port for receiving messages from other processes
YG_PF_SHOW_TEXTURES not defined display Performer textures as they are loaded
YG_PF_MULTIPROCESS not defined
(i.e. PFMP_FORK_DRAW)
setup processing for multi-precessor systems
YG_CPP "/lib/cpp -C" (SGI:"/usr/bin/cc -E") C preprocessor command line
YG_NOCPP not defined set to 1 in order to disable C preprocessing
YG_INTERFACE not defined set to 1 to start the interface when a server is defined
YG_SOUND_INTERVAL 0.1 frequency of sound node position updates
YG_LISTENER_INTERVAL 0.1 frequency of listener position updates
BERGEN_SERVER not defined set the remote host to run the snerd server
BERGEN_RAT_EXECUTABLE rat-4.2.23 name of the executable running RAT
BERGEN_RAT_INTERVAL 0.2 frequency of amplitude and pan updates
PFSHAREDSIZE 500000000 size in bytes of the Performer shared memory arena
PFVIDEORATE not defined (i.e. 60) refresh rate may need to be set for Performer
__GL_SYNC_TO_VBLANK 1 Nvidia specific setting to lock Performer frame rate
__GL_FSAA_MODE 1 Nvidia specific setting to set anitaliasing mode

Networking

Ygdrasil nodes automatically share their state when running in a networked configuration. Remote users can join an existing world by simply connecting to a server, without needing the scene file for the world. When the YG_NET_SERVER variable is defined in the Ygdrasil configuration file, Ygdrasil will start the server program, ygrepeater, on the server machine when starting the application. A public key must be generated for Secure Shell authorization in order to allow remote execution on the server machine. On a client machine, when the YG_NET_SERVER variable has been defined, simply typing

yg

at the command prompt will connect to the server and load the remote scene. The default user avatar in ygUser.yg will be used unless another copy has been placed at an earlier location in YG_PATH.

There are two main considerations when scripting scenes that will be networked between multiple users. First, care should be taken not to make user interaction relative to a specific user. Using the user variable in trigger events (i.e. when(enter.$user.teleport(0 0 0) ) ) will help to keep user interaction generalized. Second, Ygdrasil normaly does not require large bandwith to connect multiple users in complex scenes, however setting the YG_NET_TRANSFORM_INTERVAL variable to a frequency of 0.1 seconds can significantly reduce network traffic. Individual ygTransform nodes that require shorter or longer intervals can then be set individualy with the updateInterval message.

By default, Linux machines create a separate thread to handle the sending and receiving of network data. However, some Linux systems may suffer from jumpy frame rates due to this configuration. Setting either the YG_NET_THREAD or YG_NET_NOTHREAD variables in the Ygdrasil configuration file may help avoid this problem. The frame rate may also be improved by decreasing the frequency at which network messages are processed by adjusting the YG_NET_PROCESSING_INTERVAL variable.

Ygdrasil can use a combination of CAVElib distribution and networking to run distributed in a cluster environment. Once the CAVE library has been properly configured, typing

ygdistrib example1.yg

at the command prompt will start the repeater on the YG_NET_SERVER machine and start Ygdrasil on each of the machines listed in the .ygclients file. A public key must be generated for Secure Shell authorization in order to allow remote execution on each client machine.

The server program, ygrepeater, can be run on the local machine by setting YG_NET_SERVER to localhost, but generaly it is preferable to dedicate a separate machine as the networking server. A single machine can host a world without graphics by setting the server to localhost and running yg with the --noview command line option.

Bergen

The Bergen sound library can be used to send sound requests to a sound server on either the local or a remote machine. The default configuration uses a program called snerd to generate sounds on the sound server machine. Alternately, sound requests can be sent to a sound server running SuperCollider or MaxMSP using OSC protocol. Snerd uses a configuration file located at /usr/local/bergen/etc called snerd.config. This configuration file is where the number of speakers and their location is defined along with the spatialization method. The following configuration options can be placed in the snerd.config file or superceded by a .snerdrc file located in the user home directory or the local working directory where snerd is started:

variable values default description
speaker three floats none add one line for each speaker defined
spatialization none | mono | stereo | quad none configures the sound card hardware
srate integer 22050 sampling rate
udpport integer 5900 bergen to snerd communication port
listenerfilter float 0.2 linear filter constant for listenter position
soundfilter float 0.2 linear filter constant for position of each sound


the default configuration in snerd.config

speaker 5 10 -5
speaker -5 10 -5
spatialization stereo


sets up two speakers 5 feet in front of the origin, 10 feet high, and 5 feet on each side.

The default configuration runs snerd on the local machine. The sound server machine on which snerd should be started is set with the BERGEN_SERVER variable in the Ygdrasil configuration file. When the variable is set, the start script will automatically start and stop snerd on the remote machine. A public key must be generated for Secure Shell authorization in order to allow remote execution on the server machine. The path snerd uses to find sound files is determined set automatically by Ygdrasil as the YG_PATH described above. This path can be overriden by setting the variable BERGEN_PATH in the Ygdrasil configuration file when snerd is not run on the local machine.

The listenerfilter and soundfilter settings are useful for preventing abrupt changes in sound amplitude due to position changes. Ygdrasil updates the position of the listener and each sound at a default frequency of 0.1 seconds. The default value can be overriden by setting the YG_SERVER_UPDATE_INTERVAL variable in the Ygdrasil configuration file.

Robust Audio Tool

The Robust Audio Tool (RAT) is an audio conferencing tool that can be used for networked virtual reality. The SoundBlaster Live! series of sound cards have proven reliable at allowing both Bergen and RAT to use the sound card simultaniously. As will likely be most common, the RAT application should be started before the snerd sound server. Starting the applications in the opposite order will not allow RAT to initialize.

The Ygdrasil ratSource node is capable of manipulating any number of RAT source streams to spatialize and directionalize the sources by adjusting gain and pan values. Each ratSource node must be given the name of a single session ID (SSID). Each participant can add a ratSource node to their user avatar and configure the Ydrasil configuration file to pass the SSID value onto the ratSource node. The BERGEN_RAT_SOURCE variable set in the configuration file will be passed onto the scene file in the form of a macro of the same name. For example, the following lines

ratSource( volume(sphere), falloffDistance(10),
                    source(BERGEN_RAT_SOURCE) )


will create a source for the local user and replace the BERGE_RAT_SOURCE variable with the value assigned in the Ygdrasil configuration file. The default executable name for RAT is rat-4.2.23 and can be adjusted by setting the BERGEN_RAT_EXECUTABLE variable in the Ygdrasil configuration file. The default frequency at which RAT sources are updated is 0.2 seconds and can be adjusted by setting the BERGEN_RAT_UPDATE_INTERVAL variable in the Ygdrasil configuration file.

CAVELib

Ygdrasil uses the CAVElib simulation library to manage display windows and capture inputs from tracking sensors and controller devices. The software reads a configuration file called cave.config in the directory /usr/local/CAVE/etc at startup. Ygdrasil uses a customized version of the CAVElib and the installation adds the following lines from /usr/local/yg/etc/yg.cave.config to the existing cave.config file:

wallDisplay simulator -1 window 800x640+100+100
simulatorMode wand
simulatorShowWand n
simulatorShowOutline n
simulatorWandPosition 0 4.5 -2
wallEyes simulator right
interocularDistance 0.0
hideCursor y
simulatorControls F5KEY F3KEY F1KEY F2KEY F4KEY F9KEY ENDKEY HOMEKEY INSERTKEY DELKEY
PAD8 PAD2 PAD4 PAD6 PAD5 PADMINUS PADPLUSKEY PAD8 PAD4 PAD2 PAD6 PAGEUPKEY PAGE
DOWNKEY F6KEY LEFTARROWKEY RIGHTARROWKEY UPARROWKEY DOWNARROWKEY F7KEY F8KEY TAB
KEY PADENTER PADENTER

These options enlarge the simulator window from the default 512x512 up to 800x640 and hide the CAVE outline, the wand indicator, and the mouse cursor. The last option moves the keyboard controls off of the alphabetic and numeric keys in order to utilize the keyboard without interference. Systems configured for stereo viewing can be used in simulator mode by adding the following line to a local .caverc file:

simulator y

Configurations that have been setup for stereo viewing will have also have a host.config file where host is the hostname of the machine. The configuration options above set the interocular distance to 0.0 in order to eliminate distortions in the graphical interface in simulator mode. This option will likely need to be removed for immersive stereo viewing and will not affect the overall performance of the graphical interface.

The customized version of the CAVE library includes a new simulator interface that alternates between two modes of control for the mouse; one that controls the viewpoint and one that positions the wand at the same location as the mouse. The simulator mode configuration option can be set as follows:

simulatorMode classic

in order to return to the classic simulator interface.

User Nodes

The majority of the nodes available for Ygdrasil scripting are compiled as Dynamic Shared Object (DSO) files so that they can be easily added or removed from the system.  Users can customize existing nodes or add nodes with new features by following the procedure below:

If your new class is derived from another node class that is not built-in (that is, from some other DSO class), the DSO for the parent class must be loaded before the derived class's DSO. Otherwise, the derived class's DSO will have unresolved symbols, and it will fail to load. To tell Ygdrasil about other classes to load before a given class, list them in a .ygdep dependencies file. For example, if the class myUserTrigger is derived from userTrigger, then in the same directory that contains myUserTrigger.so there should be a text file named myUserTrigger.ygdep, with the following line:

userTrigger

If there is more than one dependency, list each one on a separate line. You do not need to list any dependencies of the parent class; those will be found recursively as the parent class is loaded.

Coding

Beginning with Ygdrasil version 0.4.0, message names and arguments are established by making calls to addNetMessage() in the class constructor. This allows the system to automatically parse messages and place their contents in the appropriate class variables. In addition, this system allows Ygdrasil to save the current scene graph and display message names and their arguments in the grahical interface. User nodes developed prior to 0.4.0 can be converted by following the simple procedure found here. The following describes how to establish messages and their arguments in the class constructor.

Message Registration

Ygdrasil nodes store their state in member variables that are registered with the messages that adjust them. Identify which messages are needed to define the node state and assign variables for each message argument. For example, the ygObject class creates a string variable and a boolean variable to store the state of the file message. The message name and associated variables set by the message are registered in the class constructor by making consecutive calls to the addNetMessage() function as follows:

cache_ = false;
addNetMessage( "file", &filename_, YG_NET_STRING );
addNetMessage( "file", &cache_, YG_NET_BOOL );


Initialize each variable before registering them in order to establish the default value. The following data types can be registered with Ygdrasil:

datatype token comment
ygString YG_NET_CLASS store the name of a node class
ygString YG_NET_NODE store file name of a node
ygString YG_NET_FILE store a file name on the filesystem
ygString YG_NET_STRING store a character string
ygString YG_NET_STRING_ADD store a character string (concatenate each new entry)
ygString YG_NET_STRING_DELETE store a character string (remove each new entry)
int YG_NET_INT store an integer value or a string mapped to an integer
float YG_NET_FLOAT store a single float value
bool YG_NET_BOOL store a boolean (set to default when no argument given)
bool YG_NET_BOOL_NEGATE store a boolean (set to negated default when no argument)
pfVec2 YG_NET_VEC2 store 2 floats
pfVec3 YG_NET_VEC3 store 3 floats
pfVec4 YG_NET_VEC4 store 4 floats
vector<float> YG_NET_VECTOR store a variable sized array of float values
pfMatrix YG_NET_MATRIX store a full 16 float matrix
pfCoord YG_NET_COORD store a 6 float coordinate translation and orientation
ygString YG_NET_EVENT used by the graphical interface
ygString YG_NET_WHEN used by the graphical interface

Integer values stored with YG_NET_INT can be assigned to strings by making a call to addNetMessageMap() for each integer/string pair. For example, the ygEnvironment node message fog is registered with the following function calls:

addNetMessage( "draw", &draw_, YG_NET_INT );
addNetMessageMap( "draw", "false", 0 );
addNetMessageMap( "draw", "true", 1 );
addNetMessageMap( "draw", "local", 2 );
addNetMessageMap( "draw", "remote", 3 );
addNetMessageMap( "fog", "exp2", 4 );


All messages set their associated variables to the default value when no argument is given. In the case of boolean data, the YG_NET_BOOL_NEGATE data type will set the variable to the opossite state of the default value. For example, in the ygGeometry node

floor_ = false;
wall_ = false;
addNetMessage("floor", &floor_, YG_NET_BOOL_NEGATE);
addNetMessage("wall", &wall_, YG_NET_BOOL_NEGATE);


the floor and wall messages will be set to true when received without an argument.

Some nodes have multiple messages that affect the same node state. In the case of ygSwitch, the toggle, on, and off messages all affect the same state variable. Messages represented by a single boolean are also printed out or saved as only the message name when appropriate. As a result, the off message variable, netOff_, was registered with YG_NET_BOOL_NEGATE so that the printout/save will appear as ygSwitch() when the variable is in its default state and asygSwitch(off) when the variable is negated. Furthermore, it is preferable to keep messages and their argument data agregated together when possible. For example, the ygObject node takes file(string,[cache]) but also allows a separate cache([bool]) message. The cache argument is registered with with the file message and the cache message was left to be parsed manually by message().

Message Parsing

Although registered messages are parsed automaticaly, some further processing is often necessary once a message has been received. In addition, nodes may accept unregistered messages that are parsed and handled in the message() class method. All messages are parsed into ygMessage objects, which contain the message name and a vector of the arguments, all as ygStrings. For example, the message position(1 0 2) becomes a ygMessage with the message name "position" and 3 arguments of "1", "0", and "2". The == operator is overloaded for messages to easily compare ygMessage names with ygStrings. There are also a number of convenience functions for converting message arguments into various types:
msg.stringArg(i) returns the ygString argument i
msg.intArg(i) returns argument i as an int
msg.floatArg(i) returns argument i as a float
msg.boolArg(i) returns argument i as a bool; "true", "on", or "1" are interpreted as true, everything else as false
msg.getVec2Args(v,i) fills in pfVec2 v with the values of aruments i and i+1 (i defaults to 0)
msg.getVec3Args(v,i) fills in pfVec3 v with the values of aruments i through i+2 (i defaults to 0)
msg.getVec4Args(v,i) fills in pfVec4 v with the values of aruments i through i+3 (i defaults to 0)
msg.numArgs() will return the number of arguments

In the following example from the ygObject node:

void ygObject::message(const ygMessage& msg)
    {
    //load the given filename with optional caching
    if (msg == "file")
        {
        loadFile(filename_);
        }
    //set the default object caching mode
    else if (msg == "cache")
        {
        if (msg.numArgs() > 0)
            cache_ = msg.boolArg(0);
        }


the file message requires that the appropriate file loaded into the filename_ variable be loaded. Registered messages that do not need further processing should still have an entry in message() in order to prevent generating an error message when they are not found. In the above example, the cache message sets the cache_ variable by parsing the message manually.

Network Keys

Ygdrasil creates the minimum possible subclassed node on remote clients. For example, nodes derived from ygTransform, such as the mover node, only create a ygTransform node on remote clients because they do not add any variables that need to be shared with client nodes. A node is marked as requiring creation on remote clients by setting the second argument to true in the setClassName() function.

Ydrasil creates a networking key for each registered message variable. However, in some situations it is desirable to send the data from multiple messages using a separate networking key. The ygTransform node, for example, registers a separate message called coord to share the position and orientation key data using a YG_NET_COORD datatype. The dontSendNetMessage() function indicates that the key data associated with the message should not be sent to client nodes. Alternately, the dontSaveNetMessage() function indicates that the coord message/key should not be included in the graph/save printout.

//register the position and orientation messages
addNetMessage("position",&position_,YG_NET_VEC3);
dontSendNetMessage("position");
addNetMessage("orientation",&orientation_,YG_NET_VEC3);
dontSendNetMessage("orientation");
//create a hidden message with the coordinate network key
addNetMessage("coord", &neCoord_, YG_NET_COORD);
dontSaveNetMessage("coord");
unreliableMessage("coord");


Client nodes will receive a coord message and use the aggregated data type to update their local data. Be sure to update the visible message parameters when the hidden variables are updated on the client (i.e. position and orientation should mirror COORD_NETKEY) as follows:

//set the translational component
else if (msg == "position")
    {
    dcs->setTrans(position_[0],position_[1],position_[2]);
    coord_.xyz.set(position_[0],position_[1],position_[2]);
    netMessageChanged("coord");
    }
//set the rotational component applied before translation
else if (msg == "orientation")
    {
    dcs->setRot(orientation_[2],orientation_[0],orientation_[1]);
    coord_.hpr.set(orientation_[2],orientation_[0],orientation_[1]);
    netMessageChanged("coord");
    }
else if (msg == "coord")
    {
    setPosition(coord_.xyz);
    setOrientation(coord_.hpr[1],coord_.hpr[2],coord_.hpr[0]);
    }


Note that the netMessageChanged() function is used to indicate that a variable associated with a message has changed and that remote clients should be updated.

The reset() method is provided for setting class variables back to their default value. The resetNetMessage() function is provided to reset variables associated with messages to their default value. If the value is different from the default value then client nodes will be updated automatically. Nodes that do not update values on remote clients need not use the resetNetMessage() function to initialize values.

Documentation

The documentation for each Ygdrasil node is produced automatically by a code documentation generator called cxxDocumentor. The comments and function calls within the C++ source file are used to generate the documentation for each node. User developed nodes can be added to the local documentation by following the guidlines below.

Heading

All comments before the first class method definition are assumed to be the documentation heading. These comments are placed at the top of the documentation page with the comment indicators removed. Only C++ style comments will be included in the documentation. Any HTML tags imbedded within the comments will subsequently be interpreted by the browser. As a result, the unordered list tag has been used to create a series of notes within the heading of each documented node.

Tags

The parsing system looks for a pre-defined set of tags followed by a semi-colon within the comments. These tags currently include Description, Category, Author, and Revision. The description and category tags are used to organize and briefly describe the function of each node. Documented nodes that do not include a category tag will not be included in the node index page. New categories can be added by editing the index template file, /usr/netusr/www/yg/ygIndex.templatel. The revision tag is used to generate a listing of revisions in cronological order. Revision tags must begin with a date of the form MM/DD/YY.

Comments

All intra-method comments not associated directly with a message, event, or key with be used to construct a comment tree of the code. Each if, while and for statement starts a new subheading of comments. Comments that give a general overview of how the class functions will help users to better understand the actual implementation of the node they are using. Furthermore, the comments in the reset method are used to establishing the default state of the node.Coding

The cxxDocumentor documentation system is a generalized C++ parsing engine that has been extended to generate documentation for Ygdrasil messages, events, and debug flags.

Messages

The names of node messages and their arguments are extracted from the C++ code by the parsing system. Message argument syntax is derived from either addNetMessage and related function calls or from the structure of function calls in the messages method.

Parsing the meaning of Ygdrasil messages from addNetMessage and addNetMessageMap function calls requires that code adhere to the following general guidelines:

Coding

Parsing the meaning of Ygdrasil messages from the messages method requires that code adhere to the following general guidelines:

Ambiguous dangling-else structures are of the form:

if (msg == "clip")
    if (msg.args.size() == 2)
        setClip(msg);
    else
        msg.error(name(),"(wrong number of arguments)");


The C++ parsing algorithm used by the documentation system cannot resolve these structures and they should be clarified as follows:

if (msg == "clip")
    {
    if (msg.args.size() == 2)
        setClip(msg);
    else
        msg.error(name(),"(wrong number of arguments)");
    }


The documentation system cannot extract the type of arguments being used unless they are extracted from ygMessage within the message method.  As a result, the previous example should let the system know that two float arguments are required by including the method calls to the ygMessage instead of passing the ygMessage object as follows:

if (msg == "clip")
    {
    if (msg.args.size() == 2)
        setClip(msg.floatArg(0),msg.floatArg(1));
    else
        msg.error(name(),"(wrong number of arguments)");
    }


Optional message arguments can be indicated by extracting them from the ygMessage within an if-elseif-else structure.  If the statements within the else section do not extract any arguments and they are not specifying the message error, then the other arguments will be considered as optional.  The following example extracts a single optional argument:

if (msg == "wall")
    {
    if (msg.args.size() > 0)
        setWall(msg.boolArg(0));
    else
        setWall();
    }


The following example extracts a single argument that is not optional:

if (msg == "file")
    {
    if (msg.args.size() == 1)
        loadFile(msg.args[0]);
    else
        msg.error(name(),"(wrong number of arguments)");
    }

Comments immediately above the message name filter will be used as the description for that message:

//load the given case sensitive filename
if (msg == "file")
    {
    if (msg.args.size() == 1)
        loadFile(msg.args[0]);
    else
        msg.error(name(),"(wrong number of arguments)");
    }
//set collision detection status
else if (msg == "wall")
    {
    if (msg.args.size() > 0)
        setWall(msg.boolArg(0));
    else
        setWall();
    }

Events

Event names are extracted by either finding text in the eventOccured message or by resolving the ygString argument.  The resolution of ygString involves the processing of the statements such as:

ygString myString("yg");
myString += "String";


Event arguments are resolved in a similiar manner with the portion before each equal sign being interpreted as the argument name.  The following eventOccured method call will generate two event arguments named wand and user:

ygString myArgs("wand=wand1 user=user1");
eventOccured("enter",myArgs);


Comments immediately above the eventOccured method call will be included as the event description:

//the switch has turned on
eventOccurred("SwitchOn");

Debug Flags

Debug flag names and are found by examining the debugFlag method:

debugFlag("volume",&p_->debugVolume);

Comments immediately above the debugFlag method call will be includedas the debug description:

//show a wireframe of the volume
debugFlag("volume",&p_->debugVolume);

Examples

Scripting examples are an important part of good documentation and an example scene file should be created for each new node added. The documentation for each node inlcudes a link to an example file of the same name. The name of the file is file.scene where file is the name of the new node.

Revisions

All revisions/changes should be documented in the C++ code heading in the cxx file. Move the previous revision below the revision tag so that the most recent revision is immediately following the tag:

//Author: Alex Hill
//               10/01/01
//Revision: 09/01/04 Alex Hill - changed to allow "volume" debug to be sent at any time


Your revision will be listed on the revisions page ordered by revision date.

Regenerating

User developed modules and modifications to existing nodes can be added to the existing documentation by following the commenting guidlines above:

This YGDOC script will execute the documentation system and produce all of the HTML files again. If properly commented, any new nodes should appear in one of the categories listed on the node index page.

Utilities

The /usr/local/yg/tools directory contains several useful utilities for file and geometry management. These include:

utility usage description
pfBounds infile prints out the extent of any model loaded by Performer; commonly used to determine extent and origin of a model
pfTransform [-t xtrans ytrans ztrans] [-r xrot yrot zrot]
[-s scale] [-s3 xscale yscale zscale]
infile outfile
applies a transformation to any model loaded by Performer and saves to either pfb or pfa format; commony used to reposition the center of rotation for models
pfCull [-c (off|front|back|both)] infile outfile sets the face culling state for geometry in a model; commonly used to turn off back face culling
pfTransparency [-b (on|blend|off)] infile outfile sets the transparency state of geometry in a model; commonly used to turn on transparency in models with material transparency but appear solid
pfFixSGIpfa infile outfile converts a Performer 3.X pfa file to one that can be read by Perfomer 2.4
makeNode classname [parentclass] creates skeleton *.h and *.cxx files for creating a new node DSO
updateClass infile outfile converts yg_0.1.x DSO node files to the yg 0.4.x naming convention
updateScene infile outfile converts yg_0.1.x and yg 0.4.x scene files to work with yg 0.4.2 and above
ygdrasil.xml kate syntax file copy into /opt/kde3/share/apps/katepart/syntax on SUSE systems

Rebuilding

The source files for Ygdrasil can be recompiled if necessary to change core functionality or for distribution to other platforms. The core Ygdrasil executable includes static versions of CavernSoft's QUANTA networking library, VRCO's CAVElib library, and the Bergen client library. The QUANTA library is open source and availlable in the downloads section or at www.evl.uic.edu/cavern/quanta. Researchers working in collaboration with the Electronic Visualization Laboratory can get authorization to receive a software license from VRCO by contacting Daniel Sandin. Proper interaction with the desktop interface of the Ygdrasil GUI requires a version of customized version of the CAVE libraries availlable from EVL.

The majority of Ygdrasil modules do not rely on external libraries. The movieTexture node uses the OpenQuicktime version 1.0b library to play PhotoJPEG movies. The software can be downloaded from the downloads section or at www.openquicktime.org.

To recompile Ygdrasil for IRIX systems, change the ARCHITECTURE variable in the primary Makefile to sgi. Change the YG_DIR and BERGE_DIR variables if the source code has been installed in another location.