VR-Lab PS-ERT Primer - Overview

written by pilardi 11/01
last updated by pilardi 12/01

Each function in PS-ERT is documented in $BDI_PEOPLESHOP/doc/psert or here. Unfortunately, the official documentation is vague and incomplete. This primer will hopefully fill in the gaps and describe in more detail how to use PS-ERT in the VR-Lab lab.  A complete example of a Performer program using PS-ERT is pplhi, which, at the time of writing this, is located in the repository under vrlab/src/mainprogs/examples/pplhi.

What is PS-ERT?

PS-ERT is an acronym for PeopleShop Embedded Run Time.  It is a run-time library originally designed for playing back PeopleShop scenarios.  It now allows you to take control of the scenario in real-time.

PS-ERT is capable of running on top of DirectX, OpenGL, or Performer.  This primer will only cover PS-ERT running on top of Performer.

PS-ERT consists of several C++ classes:

Contents of Primer

The VR-Lab PS-ERT Primer consists of the following parts.

Compiling with PS-ERT

For PS-ERT to be able to find its libraries and data files, the BDI_PEOPLESHOP variable must be set to /usr/local/peopleshop (assuming it hasn't been moved).

Makefile.ppl contains all the libraries and includes needed to compile PS-ERT into a Performer program. It is based on the makefiles in $BDI_PEOPLESHOP/psert_examples/pf_examples.

There are two important variables defined in Makefile.ppl:


The PS-ERT library is written in C++, but it seems to need to link against the C version of the Performer library. Compile using -DPF_CPLUSPLUS_API=0. This tells Performer to use the C version of the library even when compiling in C++.

Header files

note: these are all included in psert_util.h

#include<Performer/pf.h>          //Main Performer header file
#include<peopleshop_ert_pf.h>     //PS-ERT classes and PS-ERT/Performer connection
#include<peopleshop_ert_exface.h> //Required for expressive faces
#include<peopleshop_ert_perl.h>   //Only required if using Perl scripts
#include<libbdilog.h>             //Required for debugging output

Initializing PS-ERT

The following functions must be called in this order to initialize PS-ERT:
note: these are all called by the PSERT construtor in psert_util
  1. Before any PS-ERT functions are called, the Performer scene and channel(s) need to be set up.
  2. peopleshop_set_channel_pf(perf_chan);

  3. This takes perf_chan which is the initialized Performer channel (pfChannel*). If you are using multiple channels, any one of them will suffice.
  4. peopleshop_initialize_pf(NULL);

  5. Initializes PS-ERT's interface to Performer.
  6. peopleshop_initialize_exface(NULL);

  7. This is only need if expressive faces are being used. See expressive faces in the character part of the primer for more detail.
  8. peopleshop_initialize_perl_interpreter();

  9. This seems to be required only when scenarios call Perl scripts, or when Perl is executed in-line. The Perl interpreter is only used in complex scenario files, which will probably not be very useful in the lab.
  10. psScenario*psscene=peopleshop_create_scenario();

  11. This returns PeopleShop scenario pointer, which will be used throughout the rest of the program. See the scenario part of the primer for more detail on how to use this pointer.
  12. psscene->set_graphics_attach_ptr(perf_scene);

  13. This takes perf_scene which is the initialized Performer scene (pfScene*).

Code example

#include <Performer/pf.h>
  #include <Performer/pfdu.h>
  #include <3dsound.h>
  
  #include "psert_util.h"
  
  main(){
      pfScene*scene;
      pfChannel*channel;
  
      /* set up performer scene and channel 
        ...
      */
  
      /*****************/
      /** setup psert **/
      /*****************/
      //initialize PSERT with performer channel and scene
      PSERT psert(channel,scene);
      //load first .pss file
      psert.sp()->load("bob.pss");
      //merge other .pss files
      psert.sp()->merge("alan.pss");
      //get handles to character by character name in scenario file(s)
      CharWrapper*bob=psert.get_char("bob");
      CharWrapper*alan=psert.get_char("alan");
      //set position, heading, and sound_objnum (see 3dsound.h)
      bob.setup(1,1,0,90,1);   // setup(x,y,z,heading,sound_objnum)
      alan.setup(4,1,0,90,2);
  
      /***********************************/
      /** setup 3dsound (see 3dsound.h) **/
      /***********************************/
      // s3d_init(nose_axis,top_axis,3dsound_server);
      s3d_init(PF_Y,PF_Z,"hoover");
      //associate sounds with sound objects
      // s3d_obj_init(objnum,sndnum,TRIGGERED/LOOP,remote_filename)
      s3d_obj_init(1,0,TRIGGERED,"sound1.wav");  //bob's sound 0
      s3d_obj_init(1,1,TRIGGERED,"sound2.wav");  //bob's sound 1
      s3d_obj_init(1,2,LOOP,     "sound3.wav");  //bob's sound 2
      s3d_obj_init(2,0,TRIGGERED,"sound4.wav");  //alan's sound 0
      s3d_obj_init(2,1,LOOP,     "sound5.wav");  //alan's sound 1
  
      for(long frame=0;;frame++){
          
          /************/
          /** timing **/
          /************/
          static float lasttime=0;
          float t  = pfGetTime();  //current time (according to performer)
          float dt = t-lasttime;   //frametime
          lasttime = t;            
  
          /*************************/
          /** character behaviors **/
          /*************************/
          //CharWrapper can use both CharWrapper methods and
          //  psScenarioCharacter methods
          if(frame==100) bob->set_desired_action_by_name("walk");
          if(frame==200) alan->set_emotion("Smile",1.0);
          if(frame==300) { 
              alan->set_emotion("all",0.0);
              alan->say("greetings.phn",1);
          }
          psert.update(t);  //updates characters' 3dsound position
                            // and graphical positions 
  
          /*********************************************************/
          /** update 3dsound positions and commit (see 3dsound.h) **/
          /*********************************************************/
          pfCoord viewer_head_position;
          /* get viewer_head_position;
             ...
          */
          //tell 3dsound utility position of listener's head
          s3d_head(viewer_head_position,dt);
          //commit 3dsound changes
          s3d_commit();
        /*******************************************************/
          /* performer draw commands
             ...
          */
      }
  }


TODO