Re: Playing Video within Performer

New Message Reply Date view Thread view Subject view Author view

Angus Dorbie (dorbie++at++bitch.reading.sgi.com)
Mon, 12 Feb 1996 13:57:53 +0100


Here's an example I may have posted before, I hacked this into complex.c
in Performer 1.2 from some Sirius video demo code, it applies & loads the
Sirius video texture in a draw callback.

Even if youre not using a Sirius video the key is in using the
subtexload call in the draw process.

Rgds,
Angus.

On Feb 12, 11:26am, Jason Buksh wrote:
> Subject: Playing Video within Performer
> Simple Question
> =============
>
> I'm using Performer 1.2 and need a method
> for playing video footage (Any format). How
> do I go about this ??
>
> Jason Buksh
> VR Solutions
> j.buksh++at++vrsolns.co.uk
>
>-- End of excerpt from Jason Buksh

/*
 * complex.c
 *
 * IRIS Performer example using cull and draw process callbacks.
 * Mouse and keyboard go through GL which is simpler than mixed
 * model (GLX), but does incur some overhead in the draw process.
 *
 * $Revision: 1.44 $
 * $Date: 1994/03/16 01:54:30 $
 *
 * Run-time controls:
 * ESC-key: exits
 * F1-key: profile
 * Left-mouse: advance
 * Middle-mouse: stop
 * Right-mouse: retreat
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <getopt.h> /* for cmdline handler */

#include <gl/device.h>
#include <vl/vl.h>
#include <vl/dev_sirius.h>

#include <Performer/pf.h>
#include "pfutil.h"
#include "pfflt.h"
#include "pfsgi.h"

static void CullChannel(pfChannel *chan, void *data);
static void DrawChannel(pfChannel *chan, void *data);
static void OpenPipeline(pfPipe *p);
static void UpdateView(void);
static void GetGLInput(void);
static void Usage(void);

static long Pre_Vid(pfTraverser *, void *);
static long Post_Vid(pfTraverser *, void *);

/* performer texture for sirius video */
static pfTexture *PVidTex;
/* GL handle on performer texture */
static long VideoTexture;

/*
 * VL defines
 */
static VLControlValue size, timing, tex;
static VLControlValue format;
static VLControlValue cap_type;
static VLServer svr;
static VLPath path;
static VLNode src;
static VLNode drn;
static int DoFields = 0;
static float s_scale, t_scale, s_fraction, t_fraction;

static float texture_matrix[4][4] = {
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, 0.0, 1.0
};
static float ident_matrix[4][4] = {
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, 0.0, 1.0
};

#define Button1Mask 0x01
#define Button2Mask 0x02
#define Button3Mask 0x04
#define HISPEED 0.1f
#define LOSPEED 0.001f

/*
 * structure that resides in shared memory so that the
 * application, cull, and draw processes can access it.
 */
typedef struct
{
    long exitFlag;
    long inWindow;
    float mouseX;
    float mouseY;
    long mouseButtons;
    long winOriginX;
    long winOriginY;
    long winSizeX;
    long winSizeY;
    pfCoord view;
    float sceneSize;
    int drawStats;
} SharedData;

static SharedData *Shared;

/* light source created and updated in draw-process */

static pfLight *Sun;

/* for configuring multi-process */
static long ProcSplit = PFMP_APPCULLDRAW;
/* write out scene upon read-in - uses pfDebugPrint */
static long WriteScene = 0;
char ProgName[PF_MAXSTRING];

/*
 * Usage() -- print usage advice and exit. This procedure
 * is executed in the application process.
 */

static void
Usage (void)
{
    fprintf(stderr, "Usage: %s [-p procSplit] [file.ext ...]\n", ProgName);
    exit(1);
}

/*
* docmdline() -- use getopt to get command-line arguments,
* executed at the start of the application process.
*/

static long
docmdline(int argc, char *argv[])
{
    long opt;

    strcpy(ProgName, argv[0]);
     
    /* process command-line arguments */
    while ((opt = getopt(argc, argv, "wp:?")) != -1)
    {
        switch (opt)
        {
        case 'w':
            WriteScene = 1;
            break;
        case 'p':
            ProcSplit = atoi(optarg);
            break;
        case '?':
            Usage();
        }
    }
    return optind;
}

/*
 * main() -- program entry point. this procedure
 * is executed in the application process.
 */

int
main (int argc, char *argv[])
{
    int arg;
    int found;
    pfNode *root;
    pfChannel *chan;
    pfScene *scene;
    pfPipe *p;
    pfEarthSky *eSky;
    pfBox bbox;
    float far = 10000.0f;
    float sceneSize;
     
    if (argc < 2)
        Usage();
     
    arg = docmdline(argc, argv);
     
    pfInit();

    /* configure multi-process selection */
    pfMultiprocess(ProcSplit);
     
    /* allocate shared before fork()'ing parallel processes */
    Shared = (SharedData*)pfMalloc(sizeof(SharedData), pfGetSharedArena());
    Shared->inWindow = 0;
    Shared->exitFlag = 0;
    Shared->drawStats = 1;
     
    /* initiate multi-processing mode set in pfMultiprocess call */
    pfConfig();
     
    scene = pfNewScene();
     
    /* specify directories where geometry and textures exist */
    if (!(getenv("PFPATH")))
        pfFilePath(
                   "."
                   ":./data"
                   ":../data"
                   ":../../data"
                   ":/usr/src/Performer/data"
                   );
    fprintf(stderr,"FilePath: %s\n", pfGetFilePath());
     
    p = pfGetPipe(0);
    pfPhase(PFPHASE_FLOAT);
     
    /* Open and configure full screen GL window. */
    pfInitPipe(p, OpenPipeline);
     
    pfFrameRate(20.0f);
     
    chan = pfNewChan(p);
    pfChanCullFunc(chan, CullChannel);
    pfChanDrawFunc(chan, DrawChannel);
    pfChanScene(chan, scene);
    pfChanNearFar(chan, 0.1f, far);
     
    /* Create an earth/sky model that draws sky/ground/horizon */
    eSky = pfNewESky();
    pfESkyMode(eSky, PFES_BUFFER_CLEAR, PFES_SKY_GRND);
    pfESkyAttr(eSky, PFES_GRND_HT, -10.0f);
    pfChanESky(chan, eSky);
     
    /* vertical FOV is matched to window aspect ratio */
    pfChanFOV(chan, 45.0f, -1.0f);
     
    /* Set initial view to be "in front" of scene */
         
    /* view point at center of bbox */
    pfAddVec3(Shared->view.xyz, bbox.min, bbox.max);
    pfScaleVec3(Shared->view.xyz, 0.5f, Shared->view.xyz);
         
    pfSetVec3(Shared->view.hpr, 0.0f, 0.0f, 0.0f);
    pfChanView(chan, Shared->view.xyz, Shared->view.hpr);

    /* load files named by command line arguments */
    for (found = 0; arg < argc; arg++)
    {
        if ((root = LoadFile(argv[arg], NULL)) != NULL)
        {
           pfAddChild(scene, root);
           if(!found)
             pfNodeTravFuncs( root, PFTRAV_DRAW, Pre_Vid, Post_Vid);
/*
           pfPrint(root, PFPRINT_VB_ON, PFTRAV_SELF | PFTRAV_DESCEND, stdout);
*/
           found++;
        }
    }

    /* if no files successfully loaded, terminate program */
    if (!found)
        Usage();

    /* determine extent of scene's geometry */
    pfuTravCalcBBox(scene, &bbox);

    /* find max dimension */
    sceneSize = bbox.max[PF_X] - bbox.min[PF_X];
    sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Y] - bbox.min[PF_Y]);
    sceneSize = PF_MAX2(sceneSize, bbox.max[PF_Z] - bbox.min[PF_Z]);
    sceneSize = PF_MIN2(sceneSize, 0.5f * far);
    Shared->sceneSize = sceneSize;
        
    /* offset so all is visible */
    Shared->view.xyz[PF_Y] -= sceneSize;
    Shared->view.xyz[PF_Z] += 0.25f*sceneSize;
     
    /* main simulation loop */
    while (!Shared->exitFlag)
    {
        /* wait until next frame boundary */
        pfSync();
         
        /* Set view parameters. */
        UpdateView();
        pfChanView(chan, Shared->view.xyz, Shared->view.hpr);
         
        /* initiate traversal using current state */
        pfFrame();
    }
     
    /* terminate cull and draw processes (if they exist) */
    pfExit();
     
    /* exit to operating system */
    exit(0);
}

/*
 * UpdateView() updates the eyepoint based on the information
 * placed in shared memory by GetGLInput().
 */
static void
UpdateView(void)
{
    static float speed = 0.0f;
    static float speedLimit = 4.0f;
    pfCoord *view = &Shared->view;
    float cp;

    if (!Shared->inWindow)
    {
        speed = 0;
        return;
    }
    switch (Shared->mouseButtons)
    {
    case Button1Mask: /* LEFTMOUSE: faster forward or slower backward*/
        if (speed > 0.0f)
            speed *= 1.2f;
        else
            speed /= 1.2f;
         
        if (PF_ABSLT(speed, LOSPEED * Shared->sceneSize))
            speed = LOSPEED * Shared->sceneSize;
        else if (speed >= HISPEED * Shared->sceneSize)
            speed = HISPEED * Shared->sceneSize;
        break;
    case Button2Mask: /* MIDDLEMOUSE: stop moving and pick */
        speed = 0.0f;
        break;
    case Button3Mask: /* RIGHTMOUSE: faster backward or slower foreward*/
        if (speed < 0.0f)
            speed *= 1.2f;
        else
            speed /= 1.2f;
         
        if (PF_ABSLT(speed, LOSPEED * Shared->sceneSize))
            speed = -LOSPEED * Shared->sceneSize;
        else if (speed <= -HISPEED * Shared->sceneSize)
            speed = -HISPEED * Shared->sceneSize;
        break;
    }

    /* update view direction */
    view->hpr[PF_H] -= Shared->mouseX * PF_ABS(Shared->mouseX);
    view->hpr[PF_P] = Shared->mouseY * PF_ABS(Shared->mouseY) * 90.0f;
    view->hpr[PF_R] = 0.0f;
     
    /* update view position */
    cp = cosf(PF_DEG2RAD(view->hpr[PF_P]));
    view->xyz[PF_X] += speed*sinf(-PF_DEG2RAD(view->hpr[PF_H])*cp);
    view->xyz[PF_Y] += speed*cosf(-PF_DEG2RAD(view->hpr[PF_H])*cp);
    view->xyz[PF_Z] += speed*sinf( PF_DEG2RAD(view->hpr[PF_P]));
}

/*
 * OpenPipeline() -- create a pipeline: setup the window system,
 * the IRIS GL, and IRIS Performer. this procedure is executed in
 * the draw process (when there is a separate draw process).
 */

static void
OpenPipeline(pfPipe *p)
{
    short MAT_mode;
    float xSize = 800;
    float ySize = 500;
    int t_width, t_height;

    /* negotiate with window-manager */
    foreground();
    winopen("IRIS Performer");
    winconstraints();
    /* register events of note with event-queue manager */
    qdevice(ESCKEY);
    qdevice(F1KEY);

    /* open the server */
    if (!(svr = vlOpenVideo(""))) {
        printf("couldn't open video\n");
        exit(1);
    }

    /* Get the Video source */
    src = vlGetNode(svr, VL_SRC, VL_VIDEO, VL_ANY);
    /* Get the Texture drain */
    drn = vlGetNode(svr, VL_DRN, VL_TEXTURE, 0);

    /* Create path */
    path = vlCreatePath(svr, VL_ANY, src, drn);
    if (path < 0) {
        vlPerror("vlCreatePath");
        exit(1);
    }

    /* setup path */
    if (vlSetupPaths(svr, (VLPathList)&path, 1, VL_SHARE, VL_SHARE) < 0) {
        vlPerror("vlSetupPaths");
        exit(1);
    }
        /* select the appropriate events */
    if (vlSelectEvents(svr, path, VLStreamPreemptedMask |
                            VLControlChangedMask ) < 0) {
            vlPerror("Select Events");
            exit(1);
    }
    if(DoFields)
        cap_type.intVal = VL_CAPTURE_NONINTERLEAVED;
    else
        cap_type.intVal = VL_CAPTURE_INTERLEAVED;

    if (vlSetControl(svr, path, drn, VL_CAP_TYPE, &cap_type) <0) {
        vlPerror("VlSetControl");
        exit(1);
    }

    /* Update the video Timing Format (need to do this if a change is made) */
    /* Get the timing from input source */
    if (vlGetControl(svr, path, src, VL_TIMING, &timing) < 0) {
        vlPerror("vlGetControl");
        exit(1);
    }

    /* Set texture drain's timing to input source */
    if (vlSetControl(svr, path, drn, VL_TIMING, &timing) < 0) {
        vlPerror("vlSetControl");
        exit(1);
    }

    if (vlGetControl(svr, path, src, VL_SIZE, &size) < 0) {
        vlPerror("vlSetupPaths");
        exit(1);
    }

    /* negotiate with GL */
    pfInitGfx(p);

    /* define a performer texture for use with sirius video */
    PVidTex = pfNewTex(pfGetSharedArena());
    pfTexRepeat(PVidTex, PFTEX_WRAP, PFTEX_CLAMP);
    pfTexFilter(PVidTex, PFTEX_MINFILTER, PFTEX_BILINEAR);
    pfTexFilter(PVidTex, PFTEX_MAGFILTER, PFTEX_BILINEAR);
    pfTexFormat(PVidTex, PFTEX_FAST_DEFINE, PF_ON);
    pfTexFormat(PVidTex, PFTEX_INTERNAL_FORMAT, PFTEX_RGB_5);

    tex.intVal = SIR_TEX_PACK_RGB_5;

    /* 625 textures are bigger than 525 textures */
    if ((timing.intVal == VL_TIMING_525_SQ_PIX)
|| (timing.intVal == VL_TIMING_525_CCIR601)) {
        t_width = 1024;
        t_height = 512;
    } else {
        t_width = 1024;
        t_height = 1024;
    }

    s_scale = (size.xyVal.x-1) / (float)t_width;
    t_scale = size.xyVal.y / (float)t_height;

    /* Sirius always transfers 768 pixels */
    s_fraction = 768. / (float)t_width; /* for all */
    t_fraction = size.xyVal.y / (float)t_height;

    if (DoFields) t_height=t_height>>1;

    /* frames, in fields is different */
    pfTexImage(PVidTex, NULL, SIR_VEN_16BIT_TEXEL, t_width, t_height , 0);
    VideoTexture = pfGetGLHandle((pfObject *)PVidTex);

    texture_matrix[0][0] = s_scale;
    texture_matrix[1][1] = -t_scale;
    texture_matrix[3][1] = t_scale;

    MAT_mode = getmmode();
    mmode(MTEXTURE);
    loadmatrix(texture_matrix);
    /* Set the Texture packing mode */
    if (vlSetControl(svr, path, drn, VL_PACKING, &tex) <0) {
        vlPerror("VlSetControl");
        exit(1);
    }
    loadmatrix(ident_matrix);
    mmode(MAT_mode);

    vlBeginTransfer(svr, path, 0, NULL);
     
    /* create a light source in the "south-west" (QIII) */
    Sun = pfNewLight(NULL);
    pfLightPos(Sun, -0.3f, -0.3f, 1.0f, 0.0f);
     
    /* create a default texture environment */
    pfApplyTEnv(pfNewTEnv(NULL));
     
    /* create a default lighting model */
    pfApplyLModel(pfNewLModel(NULL));
     
    pfApplyMtl(pfNewMtl(NULL));
     
    /* enable culling of back-facing polygons */
    pfCullFace(PFCF_BACK);
     
    /*
     * These enables should be set to reflect the majority of the
     * database. If most geometry is not textured, then texture
     * should be disabled. However, you then need to change the
     * FLIGHT-format file reader. (pfflt.c)
     */
    pfEnable(PFEN_TEXTURE);
    pfEnable(PFEN_LIGHTING);
    pfEnable(PFEN_FOG);

}

/*
 * CullChannel() -- traverse the scene graph and generate a
 * display list for the draw process. This procedure is
 * executed in the cull process.
 */

static void
CullChannel(pfChannel *channel, void *data)
{
    /*
     * pfDrawGeoSet or other display listable Performer routines
     * could be invoked before or after pfCull()
     */
     
    pfCull();
}

/*
 * DrawChannel() -- draw a channel and read input queue. this
 * procedure is executed in the draw process (when there is a
 * separate draw process).
 */
static void
DrawChannel (pfChannel *channel, void *data)
{
     
    /* rebind light so it stays fixed in position */
    pfLightOn(Sun);
     
    /* erase framebuffer and draw Earth-Sky model */
    pfClearChan(channel);
     
     

    /* invoke Performer draw-processing for this frame */
    pfDraw();
     
    /* draw Performer throughput statistics */
    if (Shared->drawStats)
        pfDrawChanStats(channel);
     
    /* read window origin and size (it may have changed) */
    pfGetPipeSize(pfGetChanPipe(channel),
                    &Shared->winSizeX, &Shared->winSizeY);
    pfGetPipeOrigin(pfGetChanPipe(channel),
                      &Shared->winOriginX, &Shared->winOriginY);
    GetGLInput();
}

static void
GetGLInput(void)
{
    long x, y;
     
    while (qtest())
    {
        short value;
        long device = qread(&value);
         
        /* only act on key-down transitions */
        if (value)
        {
            switch (device)
            {
                /* ESC-key signals end of simulation */
            case ESCKEY:
                Shared->exitFlag = 1;
                break;
                 
                /* F1-key toggles channel-stats display */
            case F1KEY:
                Shared->drawStats = !Shared->drawStats;
                break;
            }
        }
    }
    /* read cursor position (may be outside our window) */
    x = getvaluator(MOUSEX);
    y = getvaluator(MOUSEY);
     
    Shared->inWindow = 0;
    /* update cursor virtual position when cursor inside window */
    if (x >= Shared->winOriginX &&
        x < (Shared->winOriginX + Shared->winSizeX) &&
        y >= Shared->winOriginY &&
        y < (Shared->winOriginY + Shared->winSizeY))
    {
        Shared->inWindow = 1;
         
        Shared->mouseX = 2.0f * ((x - Shared->winOriginX) /
                                 (float)Shared->winSizeX) - 1.0f;
        Shared->mouseY = 2.0f * ((y - Shared->winOriginY) /
                                 (float)Shared->winSizeY) - 1.0f;

        Shared->mouseButtons = ((getbutton(LEFTMOUSE) ? Button1Mask : 0) |
                                (getbutton(MIDDLEMOUSE) ? Button2Mask : 0) |
                                (getbutton(RIGHTMOUSE) ? Button3Mask : 0));
    }
}

static long Pre_Vid(pfTraverser *trav, void *data)
{
  short MAT_mode;
  MAT_mode = getmmode();
  mmode(MTEXTURE);
  loadmatrix(texture_matrix);
  mmode(MAT_mode);

  pfApplyTex(PVidTex);
  /* Load the texture from Sirius to Texture memory */
  subtexload(TX_TEXTURE_0, VideoTexture, 0.0, s_fraction, 0.0, t_fraction,
            0, (unsigned long *)0, SIR_VEN_16BIT_TEXEL);
  pfOverride(PFSTATE_TEXTURE, PF_ON);
  return PFTRAV_CONT;
}

static long Post_Vid(pfTraverser *trav, void *data)
{
  short MAT_mode;
  MAT_mode = getmmode();
  mmode(MTEXTURE);
  loadmatrix(ident_matrix);
  mmode(MAT_mode);

  pfOverride(PFSTATE_TEXTURE, PF_OFF);
  return PFTRAV_CONT;
}


New Message Reply Date view Thread view Subject view Author view

This archive was generated by hypermail 2.0b2 on Mon Aug 10 1998 - 17:52:23 PDT

This message has been cleansed for anti-spam protection. Replace '++at++' in any mail addresses with the '@' symbol.