Syzygy contains tools to make driving around inside virtual worlds easy to implement. In fact, if you're using one of the application frameworks (see the Programming chapter), you may only need to make two function calls.

Background

There are two levels of support for navigation. First, math/arNavigationUtilities.{h,cpp} contains tools for setting and reading a global navigation matrix and for converting points, vector offsets, and matrices between input coordinates and navigation coordinates. You can use these routines even if you're not using one of the application frameworks. Second, the application frameworks also provide a navigation interface that handles automatic event-processing for navigation. This interface is currently based on the routines in arNavigationUtilities. New in 1.3: If you use the framework navigation interface, you can also control your application's navigation from a matrix event from the input event stream, e.g. from a Python input device driver or script.

The drawback is that you can't have any transformations between the navigation matrix and each object's placement matrix (placement = position + orientation), or transformations between input and navigation coordinates will fail. In other words, you can't have a transformation tree in which an object's placement is specified relative to a parent object; instead, all object placement matrices have to be computed with respect to a global coordinate system. We plan to eventually use the Syzygy graphics database (which each framework maintains a copy of) to control a transformation tree, with tools to calculate transformations between arbitrary nodes. Until then, so sorry, no transformation hierarchies if you want to use the Syzygy navigation and interaction tools.

arNavigationUtilities

These routines manipulate a global navigation matrix. The matrix itself is hidden inside a namespace (arNavigationSpace) and the accessor routines make use of a mutex to prevent multiple simultaneous accesses. Conceptually, you can treat the navigation matrix just like an object's placement matrix; for example, to translate the viewpoint to (0,0,-5) you would set the nav. matrix to ar_translationMatrix(0,0,-5). The inverse of the navigation matrix should be loaded into the OpenGL modelview matrix stack prior to rendering (the frameworks have routines for doing this).

void ar_setNavMatrix( const arMatrix4& matrix );
    sets the navigation matrix.
arMatrix4 ar_getNavMatrix();
    returns the navigation matrix.
arMatrix4 ar_getNavInvMatrix();
    returns the inverse of the navigation matrix. This is computed once,
    then not recomputed again until the first request after one of the other
    routines changes the navigation matrix.

The following two routines modify the nav. matrix by appending a translation or a rotation.

void ar_navTranslate( const arVector3& vec );
void ar_navRotate( const arVector3& axis, float degrees );

These routines convert a placement matrix, a point, or a vector offset between input and navigation coordinates (a vector offset gets rotated but not translated).

arMatrix4 ar_matrixToNavCoords( const arMatrix4& matrix );
arVector3 ar_pointToNavCoords( const arVector3& vec );
arVector3 ar_vectorToNavCoords( const arVector3& vec );
arMatrix4 ar_matrixFromNavCoords( const arMatrix4& matrix );
arVector3 ar_pointFromNavCoords( const arVector3& vec );
arVector3 ar_vectorFromNavCoords( const arVector3& vec );

Framework-mediated Navigation

The application frameworks provide a simple interface for automatically converting input events into navigation commands. Navigation commands are handled by the arNavigationUtilities, input-event conversion is handled by the classes in the [ Interaction.html]interaction directory. New in 1.3: You can set a couple of Syzygy database parameters that will make the framework copy a specific matrix event from the input stream into the navigation matrix, allowing you to externally script your navigation.

Levels of behaviors

Three levels of behaviors are available:

  1. The frameworks have built-in default behaviors.
  2. These behaviors can be modified by setting a few parameters in the Syzygy database.
  3. The first two sets of behaviors can be overridden by calling framework methods from within an application.
  4. All of these are ignored if you set the database parameters that tell the framework to set the navigation matrix from the input event stream.

Default Behavior Modification

To determine behavior, you specify two things: the nature of the behavior and the condition required to trigger it. Two types of navigation behaviors are currently available. They are based on the current state of an input event, e.g. in the case of viewpoint translation you might hold a joystick at a fixed angle to travel in a fixed direction at a fixed speed.

Behavior Types

Triggering Conditions

A triggering condition consists of an event type, and event index, and a threshold value. For example, a condition of axis/0/0.2 would mean that the specified behavior would happen whenever the absolute value of axis event 0 exceeded 0.2. //Each condition is attached to a single behavior; attaching a new behavior to a condition removes the old one//. Currently it goes the other way as well, each behavior can only be attached to one condition at a time; that latter part will probably change in the near future. Multiple behaviors can be active simultaneously if their triggering conditions are all met.

Translation and rotation speeds are specified in feet/sec. and degrees/sec, respectively. Note that the translation and rotation behaviors use actual time measurements, so the speeds should be independent of frame rate. Note also that both speeds are scaled by the actual value of the triggering event, provided that value is between the threshold value and 1.0; in other words, if you're using a joystick that returns values that vary depending on how far you move it, the speed will vary accordingly, provided the driver scales the values to fall between 0 and 1.

Using framework-mediated navigation

To use these behaviors, you need only do the following:

  1. If you're creating an application using the Distributed Scene Graph Framework (see Programming), use the framework.getNavNodeName() method to get the name of the navigation matrix node and attach all of your object nodes to it.

  2. Call framework.navUpdate() to process input events and update the nav. matrix. In a scene graph application, call it just before (3); in an app. based on the Master/Slave Framework (see Programming), call it in the preExchange() callback.

  3. Call framework.loadNavMatrix() before rendering. In a scene graph app. call it just after (2) and before framework.setViewer() and setPlayer(); in a master/slave app call it near the beginning of the draw() callback.

For a scene graph application example, see demo/cubes; master/slave apps. that use framework-mediated navigation are demo/atlantis and demo/coaster.

Framework default behaviors

By default, the condition axis/1/0.2 triggers translation along the negative z axis (forwards) and axis/0/0.2 translates along the x axis.

Database parameters

Setting the following database parameters (on the control machine for a scene graph app., on the master machine for a master/slave app) will modify the default behaviors.

SZG_NAV/x_translation sets the trigger condition for translation in x. The first field must be either "axis" or "button", the second a positive integer or 0, and the third a positive floating-point value between 0 and 1, e.g. "axis/0/0.2". y_translation, z_translation, and y_rotation set the trigger conditions for the other behaviors analogously. You cannot initiate the world-rotation behavior from the database, that has to be activated by the application.

SZG_NAV/translation_speed and rotation_speed set the speed of the translation behavior (in feet/sec.) and the rotation behavior (in deg/sec) respectively. The translation speed is scaled by the framework's unit conversion factor (provided that was set before calling framework.init()), meaning that the translation speed in a particular application will correspond to the number of application units/second that map onto 5 feet/sec in input units (trust me, it makes sense).

SZG_NAV/effector allows you to specify the input event ranges to use for navigation. This is a 5-element '/'-delimited string of 0-or-positive integers. The first element is the matrix index of the tracking device attached to the navigation device. Elements 2 and 3 are the number of buttons and the starting button index, elements 4 and 5 are the number of axes and the starting axis index. This parameter defaults to 1/0/0/2/0, in other words, use matrix #1 (#0 is generally assumed to be attached to the head), no buttons, and axes 0 and 1 for navigation.

New in 1.3: Setting SZG_NAV/use_nav_input_matrix to "true" tells the framework to copy a matrix from the input event stream into the navigation matrix. The matrix event index to copy is specified by the value of SZG_NAV/nav_input_matrix_index, which defaults to 2.

Framework methods

Calling the following framework methods from within an application will override the two levels above.

bool setNavTransCondition( char axis, arInputEventType type,
                           unsigned int index, float threshold );

and

bool setNavRotCondition( char axis, arInputEventType type,
                         unsigned int index, float threshold );

set a translation or rotation condition, e.g.

    framework.setNavTransCondition( 'z', AR_EVENT_AXIS, 1, 0.2 );

void setNavTransSpeed( float speed );
void setNavRotSpeed( float speed );

set the translation and rotation speed in application units/sec and degrees/sec, respectively. When modified from this end, the value isn't scaled by the framework conversion factor, but of course you can always do that yourself.

void setNavEffector( const arEffector& effector );

allows you to replace the navigation effector (a representation of the input device used for navigation).

void ownNavParam( const string& paramName );

tells the framework that the specified parameter should not be reloaded from the database (clobbering the value we've just set in code) in response to a "reload" message. The name is the database name without the SZG_NAV, e.g.

    framework.ownNavParam( "translation_speed" );

Externally-scripted Navigation

New in 1.3: Setting SZG_NAV/use_nav_input_matrix to "true" tells the framework to copy a matrix from the input event stream into the navigation matrix. The matrix event index to copy is specified by the value of SZG_NAV/nav_input_matrix_index, which defaults to 2.

As an example, suppose you had several applications. For a demo of all of these applications you wanted the viewpoint to always face the origin while orbiting around it at a fixed distance. You could write a Python input driver that generated a stream of matrix events with index 2 and computed as in orbit.py.