Ronan's OpenMASK Tutorial

This document is a brief hands-on tutorial on installing OpenMASK and programming simple synthetic characters as OpenMASK simulation objects.


Installing OpenMASK

Prerequisites

Although it is possible to install and use OpenMASK with gcc-2.95, we will here assume that you have gcc-3.2 installed. We will also assume that your machine runs a Debian GNU/Linux (more precisely, I run a testing Debian).

Performer
Download the OpenGL Shader and OpenGL Performer 3.0 (Demo) files from http://www.sgi.com/products/evaluation/Linux_opengl_shader_3.0/ and http://www.sgi.com/products/evaluation/Linux_performer_3.0/.

You should fetch the following files and save them in a temporary directory:

        SGIshader_eoe-3.0.i386.rpm
        SGIshader_dev-3.0.i386.rpm
        performer_eoe-3.0.0_gcc3-0.tgz
        performer_dev-3.0.0_gcc3-0.tgz
        performer_demos-3.0.0_gcc3-0.tgz
        performer_demo_license-3.0.0_gcc3-0.tgz
        performer_docs-3.0.0_gcc3-0.tgz
        performer_docs-print-3.0.0_gcc3-0.tgz
        performer2.4_compat-3.0.0_gcc3-0.tgz
        performer2.5_compat-3.0.0_gcc3-0.tgz

Change to root, and convert these files to .deb using alien:

        alien SGIshader_eoe-3.0.i386.rpm
        alien SGIshader_dev-3.0.i386.rpm
        alien performer_eoe-3.0.0_gcc3-0.tgz
        alien performer_dev-3.0.0_gcc3-0.tgz
        alien performer_demos-3.0.0_gcc3-0.tgz
        alien performer_demo_license-3.0.0_gcc3-0.tgz
        alien performer_docs-3.0.0_gcc3-0.tgz
        alien performer_docs-print-3.0.0_gcc3-0.tgz
        alien performer2.4_compat-3.0.0_gcc3-0.tgz
        alien performer2.5_compat-3.0.0_gcc3-0.tgz

You can then install them with dpkg:

        dpkg -i *performer*.deb *shader*.deb

Open Inventor
To avoid problems with Open Inventor, it can be necessary to compile it yourself:
        apt-get source inventor --compile
        dpkg -i *inventor*.deb

You can make sure that everything went well for Performer and Open Inventor by running perfly on an .iv file:

        perfly /usr/share/Performer/data/tux.iv

Other Necessary Software
Install PVM, PCCTS and Doxygen:
        apt-get install pvm pccts doxygen

Optionally,if you plan on compiling the QT contributions and examples, install qt-dev:

        apt-get install qt-dev

Compiling OpenMASK

Downloading It
Change back to an ordinary user id, and get the OpenMASK 3.1 for gcc-3.2 release tarball from http://www.openmask.org.

Save it in a directory you have write access to, say /scratch. Uncompress it:

        cd /scratch
        tar xvzf OpenMASK3.1-linux-gcc3.2-performer2.5.2.tar.gz

Environment Variables
Before compiling, set the following environment variables (assuming tcsh):
        # the directory to which OpenMASK got uncompressed
        setenv OpenMASKDIR /scratch/linux-gcc3.2
        setenv PCCTSDIR /usr/include/pccts
        setenv PCCTSBINDIR /usr/bin
        setenv COMPILER gcc-3.2

And only if you plan on compiling the QT contributions and examples:

        setenv QTDIR /usr/share/qt

Launching Compilation
Just change to the directory where OpenMASK uncompressed, and launch the compile script:
        cd /scratch/linux-gcc3.2
        ./compile

Possible Problems
You might encounter problems compiling the QT contributions. If they refuse to compile, make sure that in their directory there exists directories named .moc and .ui. If they don't exist, create them and run make.

Also, the extractDoc script in /scratch/linux-gcc3.2 (or whatever is the OpenMASK location) might contain a hardcoded path. Correct that path (possibly using the $OpenMASKDIR environment variable), and run ./extractDoc to create the Doxygen documentation.


Programming a Character

A Static Character

We try now to have a 3D character show up in an OpenMASK window. For this we need three basic things: a main function which will initialise OpenMASK, read the simulation tree and start the simulation, a file describing the said simulation tree, and a class for our character.

main()
We will write our main function in a file of its own: main.cxx. Its basic structure will be the following:
    // the simulation tree loader
    #include <SimpleOpenMASK3Loader.h>
    #include <PsController.h>
    #include <PsKernelObjectAbstractFactory.h>
    #include <PsvInteractive3DVis.h>
    #include <PsCameraman.h>
    #include <PsKeyboardNavigator.h>
    #include <PsException.h>
    #include "Camille.h"
    int main( int argc, char* argv[] ) 
    {
        // construction of a simulation tree for root
        PsObjectDescriptor* simTree;
        if (argc >= 2) 
        {
                // the first argument to camille is the simulation tree file
                simTree = (new SimpleOpenMASK3Loader(argv[1]))->getRootObjectDescriptor() ;
        }
        else
        {
                cerr << "usage: camille <simtree>" << endl;
                exit(1);
        }
        // we create the simulation controller
        PsController* controller = new PsController ( *simTree, 0 ) ;

        // next come the managers for objects in the simulation tree
        // visualisation
        controller->addInstanceCreator( "PsvInteractive3DVis", 
                                   new PsSimpleSimulatedObjectCreator<PsvInteractive3DVis> ()) ;
        // camera
        controller->addInstanceCreator ("PsCameraman", 
                                   new PsSimpleSimulatedObjectCreator<PsCameraman> ()) ;
        // keyboard navigation
        controller->addInstanceCreator ("PsKeyboardNavigator", 
                                   new PsSimpleSimulatedObjectCreator<PsKeyboardNavigator> ()) ;
        // and our own object: Camille
        controller->addInstanceCreator ("Camille",
                                   new PsSimpleSimulatedObjectCreator<Camille> () );
        // finally we initialise and run the simulation
        try
        {
                controller->init();
                controller->run();
        }
        catch (PsException& e)
        {
                cerr << "Uncaught exception: " << e << endl;
        }
        // and we exit (possibly using Performer's exit function, which frees the graphical resources)
        if (Psv3DVis::callPfExit)
                pfExit();
    }

Character
Let's call our character Camille, and give her a class of her own. She has to inherit two classes: PsSimulatedObject, which is the base for any simulated object of OpenMASK, and PsvMechanismPartner, which allows her to be displayed. In Camille.h, we write:
        #include <PsSimulatedObject.h>
        #include <PsvMechanismPartner.h>
        #include <PsOutput.h>
        #include <PsTranslationYRotation.h>
        class Camille : 
          public PsSimulatedObject, 
          public PsvMechanismPartner
        {
          public:
             Camille(PsController& ctrl, const PsObjectDescriptor& objectDescriptor);
             virtual ~Camille(void);
        };

In Camille.cxx, our constructor just calls PsSimulatedObject and PsvMechanismPartner's constructors:

        #include <PsSimulatedObject.h>
        #include <PsvMechanismPartner.h>
        #include <PsController.h>
        #include <PsObjectDescriptor.h>
        #include "Camille.h"
        Camille::Camille(PsController& ctrl, const PsObjectDescriptor& objectDescriptor) :
            PsSimulatedObject(ctrl, objectDescriptor),
            PsvMechanismPartner()
        {
        }
        Camille::~Camille(void)
        {
        }

Simulation Tree
The simulation tree describes -- in a format special to OpenMASK -- all the objects you want to use in the simulation. This includes your own objects (eg, our character), a visualisation object that will allow to display them, and objects to manage interaction with the user (for instance, to allow navigating in the 3D world with the keyboard).

Makefile
There are two concerns when compiling our program. The first is to specify the right path for all the OpenMASK include files and library. The second is to link with the right libraries.

The OpenMASK include files are in the following locations:

        $(OpenMASKDIR)/kernel/inc 
        $(OpenMASKDIR)/contrib/3DVis/inc 
        $(OpenMASKDIR)/contrib/interactive3DVis/inc 
        $(OpenMASKDIR)/contrib/interactive3DVisUtilities/PsCameraman/inc 
        $(OpenMASKDIR)/contrib/3DVisPartners/inc 
        $(OpenMASKDIR)/contrib/userTypes/math/inc 
        $(OpenMASKDIR)/contrib/userTypes/events/inc 
        $(OpenMASKDIR)/contrib/3DVisInputHandlers/math/inc 
        $(OpenMASKDIR)/contrib/loaders/simpleOpenMASK3Loader/inc 
        $(OpenMASKDIR)/contrib/interactive3DVisUtilities/PsKeyboardNavigator/inc

The necessary libraries are in

        $(OpenMASKDIR)/lib

and possibly:


        /usr/X11R6/lib

The complete library list you need to link with is:

        3DVisPartners 
        3DVisInputHandlers 
        3DVis 
        Interactive3DVis 
        Interactive3DVisUtilities 
        UserTypes 
        SimpleOpenMASK3Loader 
        OpenMASK 
        pf 
        pfdu 
        pfutil 
        pfui 
        Xmu 
        X11 
        GLU 
        GL 
        Xext

Making It Move

To allow Camille to move, we have to do three things:

Wrapping it up, Camille.h now looks like:

        #include <PsSimulatedObject.h>
        #include <PsvMechanismPartner.h>
        #include <PsTranslationYRotation.h>
        #include <PsOutput.h>
        class Camille : 
              public PsSimulatedObject,
              public PsvMechanismPartner
        {
          public:
                Camille(PsController& ctrl, const PsObjectDescriptor& objectDescriptor);
                virtual ~Camille();
                virtual void init();
                virtual void compute();
          private:
                double theta;
                PsOutput<PsTranslationYRotation>& positionOutput;
        };

We just added the methods init() and compute(), and the private member positionOutput, which is a reference to an output of type PsTranslationYRotation. We also added a member named theta, which we will use to move Camille around.

Camille.cxx becomes:

        #include <PsSimulatedObject.h>
        #include <PsvMechanismPartner.h>
        #include <PsController.h>
        #include <PsObjectDescriptor.h>
        #include <PsvTranslationYRotationInputHandler.h>
        #include <cmath>
        using namespace std;
        #include "Camille.h"
        Camille::Camille(PsController& ctrl, const PsObjectDescriptor& objectDescriptor) :
            PsSimulatedObject(ctrl, objectDescriptor),
            PsvMechanismPartner(),
            theta(0.),
            positionOutput(addOutput<PsTranslationYRotation>("position"))
        {
            // initialise the position output
            positionOutput.set(PsTranslationYRotation(0.,0.,0.,0.));

            // and make it visualised
            visualiseOutput<PsvTranslationYRotationInputHandler>(positionOutput, "DCS_position");
        }
        Camille::~Camille(void)
        {
        }
        void Camille::init()
        {
        }
        void Camille::compute()
        {
            // increment the angle
            theta += 0.5;

            // angle in radians
            double theta_rad = theta * M_PI / 180.;

            // update the position
            positionOutput.set
                (PsTranslationYRotation
                 (4. * cos(theta_rad),
                  0.,
                  4. * sin(theta_rad),
                  -theta - 90));
        }

Notice here how we initialised the output. We used the function addOutput(), which creates an output of type PsTranslationYRotation, that OpenMASK will know under the name ``position''. Inputs and outputs all have a name, which can be used to connect them later (we won't use that name in this tutorial).

Then, we set a first value for the output, and call visualiseOutput() on it. The argument ``DCS_position'' is the name of a transform node in the geometry used to display Camille.

We kept our init() method empty, as we do not have any more initialisation to do (like connecting to other objects). We probably should have put the call to visualiseOutput() in the init() method, but as of OpenMASK 3.1, it makes the application crash, so we put it in the constructor.

Our compute() method updates the position of Camille, so as her to travel around a circle. For this, we use the method set() on our position output. The four arguments to the constructor PsTranslationYRotation() represent the X,Y,Z coordinates and the rotation around the Y axis.

Working With OpenGL Performer

In OpenMASK 3.1, the visualisation is built on top of OpenGL Performer. This means that OpenGL Performer is the one loading your geometry files, and that it keeps a scene graph used to display your objects. It can be useful to access Performer functions that work efficiently on this scene graph.

For instance, we could be willing to use Performer's facilities to calculate intersections of segments with objects in the scene, for the sake of implementing virtual proximeters.

The OpenGL Performer Scene Graph can be accessed in three different ways:


Resources

Official OpenMASK website: http://www.openmask.org.

Official OpenGL Performer home: http://www.sgi.com/software/performer/.

OpenGL Performer manuals: http://www.sgi.com/software/performer/manuals.html.

Open Inventor home: http://www.sgi.com/software/inventor/.

Open Inventor manuals: http://www.sgi.com/software/inventor/manuals.html.

PVM home: http://www.csm.ornl.gov/pvm/pvm_home.html.

PCCTS home: http://www.polhode.com/pccts.html.

Doxygen home: http://www.doxygen.org.


History

24/02/2003 - Minor corrections.

27/03/2003 - A few simplifications in include dirs and libraries. Last paragraph rewritten by Michaël Rouillé. Incorporated a few remarks by David Raulo too.


Author

Ronan Le Hy (lehy at imag dot fr), INRIA Rhône-Alpes, Cybermove/Laplace project.

This tutorial resides at http://www.inrialpes.fr/sharp/people/lehy/.

Feel free to send comments and suggestions about this tutorial; however, if you wish to suggest changes, please do so referring to the document in POD format (from which I generate HTML, PDF...). POD is Perl's Plain Old Documentation (see man perlpod).


Copyright

This document is copyright(c) INRIA, 2003. All rights reserved.

OpenGL Performer, Open Inventor and SGI are trademarks or registered trademarks of Silicon Graphics, Inc.