[BACK]Return to dmvtO2.c CVS log [TXT][DIR] Up to [Development] / performer / src / pguide / libpf / C

File: [Development] / performer / src / pguide / libpf / C / dmvtO2.c (download)

Revision 1.1, Tue Nov 21 21:39:36 2000 UTC (16 years, 11 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * Copyright 1997 Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
 *
 * file: dmvtO2.c
 * ----------------
 * 
 *  Video Texturing using the fast path (dmbuffers) on O2
 *
 *  On O2 systems this path for defining textures can be done at
 *  hardware rendering or pixel transfer DMA rates, when the following
 *  conditions are met:
 *  - The DMbuffer, pbuffer and texture object all match in terms of
 *    width, height and depth of RGBA components.
 *  - The texture image is 64x64 or larger.
 *  - The glCopyTexSubImageEXT command is used to copy the entire
 *    image from dmpbuffer to texture.
 *
 *  This example takes video input from the O2 camera and applies it s a texture
 *  in a Performer scene
 *
 *  cc -n32 dmvtO2.c -o dmvtO2 -lpf_ogl -lGL -lX11 -lvl -ldmedia -lfpe
 *
 * Runtime Controls:
 * -----------------
 *	s - toggle spinning of geometry on/off
 *	ESC - exit
 *
 */

#include <X11/keysym.h>
#include <dmedia/vl.h>
#include <dmedia/dm_buffer.h>
#include <dmedia/dm_image.h>
#include <Performer/pf.h>

/* Global Variables */
GLXContext 	dmpc;
GLXPbufferSGIX 	dmPbuf;
DMparams 	*videoFormat;
DMbufferpool    pool;
Display 	*dpy;
Window 		xwin;
GLfloat 	spin = 0;
GLint 		spinning = 1;
int 		texWidth, texHeight;
int 		fieldWidth, fieldHeight;
int 		frameWidth, frameHeight;

pfMatrix idmat[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
};

pfVec2          texcoords[]={   {0.0f, 0.0f},
                                {1.0f, 0.0f},
                                {1.0f, 1.0f},
                                {0.0f, 1.0f} };

pfVec3          coords[] ={     {-1.0f,  -1.0f,  0.0f },
                                { 1.0f,  -1.0f,  0.0f },
                                { 1.0f,  1.0f,   0.0f },
                                {-1.0f,  1.0f,   0.0f } };

pfVec4		colors[] ={ {1.0f, 1.0f, 1.0f, 1.0f} };

static pfWindow	*Win=NULL;
pfTexture	*Tex=NULL;
pfDispList 	*SceneDL=NULL;
VLServer	svr;
VLPath		path;
VLNode		src, drn;

static void       DrawScene(void);
static pfDispList *MakeScene(pfGeoSet *geom, void *arena);

void ExitVtO2(void)
{
    glXWaitGL();
    vlEndTransfer(svr, path); /* End the data transfer */
    vlDestroyPath(svr, path); /* Destroy the path, free the memory it used */
    vlCloseVideo(svr); 	      /* Disconnect from the daemon */
    XCloseDisplay(dpy);
    pfExit();
    exit(EXIT_SUCCESS);
}

void CreateWindow(void *arena)
{
    pfFrustum 	*frust;

    GLint attribRGB[] = {PFFB_DOUBLEBUFFER, PFFB_RGBA, None};

    /* Initialize window and viewing frustum */
    Win = pfNewWin(arena);
    pfWinOriginSize(Win, 0, 0, frameWidth, frameHeight);
    pfWinName(Win, "dmvtO2");
    pfWinType(Win, PFWIN_TYPE_X);
    pfOpenWin(Win);
    dpy = pfGetCurWSConnection();
    if (!dpy) {
	pfNotify(PFNFY_FATAL,PFNFY_PRINT, "Can't connect to display\n");
    }
    xwin = pfGetWinWSWindow(Win);
    XSelectInput(dpy, xwin,  KeyPressMask);
    XMapWindow(dpy, xwin);
    XSync(dpy,FALSE);

    frust = pfNewFrust(NULL);
    pfFrustNearFar(frust, frameHeight, 3.0*frameHeight);    
    pfMakePerspFrust(frust, -0.25*frameWidth, 0.25*frameWidth, 
                            -0.25*frameHeight, 0.25*frameHeight);
    pfApplyFrust(frust);
    pfDelete(frust);
    pfLoadMatrix(idmat);
    pfTranslate (0.0f, 0.0f, -2.0*frameHeight);
    pfChooseWinFBConfig(Win, attribRGB);
}

void CreatePbuffer(void)
{
    int i, match, items, a[5];
    GLXFBConfigSGIX *fbConfig;
    int pbAttribs[] = {GLX_DIGITAL_MEDIA_PBUFFER_SGIX, GL_TRUE, None};
    int configAttribs[] = {
        GLX_RENDER_TYPE_SGIX, GLX_RGBA_BIT_SGIX,
	GLX_DRAWABLE_TYPE_SGIX, GLX_PBUFFER_BIT_SGIX,
	None 
    };
	    
    if(!(fbConfig = glXChooseFBConfigSGIX(dpy, DefaultScreen(dpy), configAttribs, &items))){
	pfNotify(PFNFY_FATAL,PFNFY_PRINT, "can't find suitable FBConfig\n");
    }
    
    /* search the list of FBconfigs and find one with RGBA8 and no DEPTH
     * to try and minimise the resources allocated for the pbuffer  
     */
    for(i = 0; i < items; i++){
	glXGetFBConfigAttribSGIX(dpy, fbConfig[i], GLX_RED_SIZE,   &a[0]);
	glXGetFBConfigAttribSGIX(dpy, fbConfig[i], GLX_GREEN_SIZE, &a[1]);
	glXGetFBConfigAttribSGIX(dpy, fbConfig[i], GLX_BLUE_SIZE,  &a[2]);
	glXGetFBConfigAttribSGIX(dpy, fbConfig[i], GLX_ALPHA_SIZE, &a[3]);
	glXGetFBConfigAttribSGIX(dpy, fbConfig[i], GLX_DEPTH_SIZE, &a[4]);
	if(a[0] == 8 && a[1] == 8 && a[2] == 8 && a[3] == 8 && a[4] == 0){ 
            match = i; break;
        }
     }
    
    if(!(dmpc = glXCreateContextWithConfigSGIX(dpy,
	    fbConfig[match], GLX_RGBA_TYPE_SGIX, NULL, GL_TRUE))){
	pfNotify(PFNFY_FATAL,PFNFY_PRINT, "can't create context\n");
    }
    
    if(!(dmPbuf = glXCreateGLXPbufferSGIX(dpy, fbConfig[match], 
		    fieldWidth, fieldHeight, pbAttribs))){
        pfNotify(PFNFY_FATAL,PFNFY_PRINT, "can't create GLXPbuffer\n");
    }
}


void InitTexture(void *arena)
{
    pfEnable(PFEN_TEXTURE);
    pfApplyTEnv(pfNewTEnv(arena));

    Tex = pfNewTex(arena);
    pfTexName(Tex, "Tex");
    pfTexImage(Tex, NULL, 4, texWidth, texHeight, 0);

    pfTexLoadSize(Tex, fieldWidth, fieldHeight);
    pfTexFilter(Tex, PFTEX_MINFILTER, PFTEX_LINEAR);
    pfTexFilter(Tex, PFTEX_MAGFILTER, PFTEX_LINEAR);
    pfTexFormat(Tex, PFTEX_INTERNAL_FORMAT, PFTEX_RGBA_8);
    pfTexLoadMode(Tex, PFTEX_LOAD_SOURCE, PFTEX_SOURCE_FRAMEBUFFER);
    pfTexLoadMode(Tex, PFTEX_LOAD_BASE, PFTEX_BASE_AUTO_SUBLOAD); 
    pfTexRepeat(Tex, PFTEX_WRAP_S, PFTEX_CLAMP);
    pfTexRepeat(Tex, PFTEX_WRAP_T, PFTEX_CLAMP);

    pfApplyTex(Tex);
}

int PowerOf2(int value)
{
    int x = 1;
    while(x <= value) x <<= 1;
    return(x);
}

void SetVLPathControls(void)
{
    VLControlValue val;
    
    val.intVal = VL_PACKING_ABGR_8;
    vlSetControl(svr, path, drn, VL_PACKING, &val);
    
    /* Set control for field rendering.  This conserves on the size of the
     * defined texture and improves performance.
     */
    val.intVal = VL_CAPTURE_NONINTERLEAVED;
    vlSetControl(svr, path, drn, VL_CAP_TYPE, &val);
    
    /* Query memory node for field size*/
    vlGetControl(svr, path, drn, VL_SIZE, &val);
    fieldWidth = val.xyVal.x; fieldHeight = val.xyVal.y;
    
    /* Query video node for frame size*/
    vlGetControl(svr, path, src, VL_SIZE, &val);
    frameWidth = val.xyVal.x; frameHeight = val.xyVal.y;
    texWidth = PowerOf2(fieldWidth); texHeight = PowerOf2(fieldWidth);
    
    val.intVal = VL_FORMAT_RGB;
    vlSetControl(svr, path, drn, VL_FORMAT, &val);
    val.intVal = VL_LAYOUT_GRAPHICS;
    vlSetControl(svr, path, drn, VL_LAYOUT, &val);
}

static void DrawScene(void) 
{
    static pfVec4 clr = {0.1f, 0.0f, 0.4f, 1.0f};
    pfClear(PFCL_COLOR | PFCL_DEPTH, clr);
  
    pfPushMatrix(); 
    pfRotate(PF_X, -spin);
    pfDrawDList(SceneDL);
    pfPopMatrix();
    pfSwapWinBuffers(Win);  

    if (spinning) spin += 0.5;
    if (spin > 360.0) spin -= 360.0;
}

static pfDispList *MakeScene(pfGeoSet *geom, void *arena)
{
    pfDispList *dl;
    pfGeoState *gstate;
    float maxS = fieldWidth / (float) texWidth;
    float maxT = fieldHeight / (float) texHeight;
  
    dl = pfNewDList(PFDL_FLAT, 0, arena);
    
    coords[0][0] = -frameWidth / 2.0;
    coords[0][1] = -frameHeight / 2.0;
    coords[1][0] = frameWidth / 2.0;
    coords[1][1] = -frameHeight / 2.0;
    coords[2][0] = frameWidth / 2.0; 
    coords[2][1] = frameHeight / 2.0;
    coords[3][0] = -frameWidth / 2.0;
    coords[3][1] = frameHeight / 2.0;

    texcoords[1][0] = maxS;
    texcoords[2][0] = maxS;
    texcoords[2][1] = maxT;
    texcoords[3][1] = maxT;

    pfGSetAttr(geom, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(geom, PFGS_TEXCOORD2, PFGS_PER_VERTEX, texcoords, NULL);
    pfGSetAttr(geom, PFGS_COLOR4, PFGS_OVERALL, colors, NULL);
    pfGSetPrimType(geom, PFGS_QUADS);
    pfGSetNumPrims(geom, 1);
    
    gstate = pfNewGState (arena);
    pfGSetGState (geom, gstate);
    
    pfGStateMode (gstate, PFSTATE_CULLFACE, PFCF_OFF);
    pfGStateAttr (gstate, PFSTATE_TEXENV, pfNewTEnv (arena));
    pfGStateAttr (gstate, PFSTATE_TEXTURE, Tex);
    
    pfOpenDList(dl);
    pfDrawGSet(geom);
    pfCloseDList();
    
    return dl;
}

void processVideoEvents(VLEvent vle)
{
    switch(vle.reason) {
      case VLTransferError:
	pfNotify(PFNFY_WARN,PFNFY_PRINT,"VLTransferError....\n");
	break;
      case VLSequenceLost:
	pfNotify(PFNFY_WARN,PFNFY_PRINT,"VLSequenceLost....\n");
	break;
      case VLTransferComplete:
	break;
      case VLTransferFailed:
	pfNotify(PFNFY_WARN,PFNFY_PRINT,"VLTransfer FAILED ....\n");
	break;
      case VLStreamPreempted:
	pfNotify(PFNFY_WARN,PFNFY_PRINT,"VLStreamPreempted ....\n");
	ExitVtO2();
	exit(EXIT_SUCCESS);
	/*NOTREACHED*/
      default:
	pfNotify(PFNFY_WARN,PFNFY_PRINT,"didn't handle that event\n");
      break;
    }
}

void redraw(void) 
{
    DMbuffer field;
    VLEvent  vle;

    /* get the next VLEvent */
    if(vlEventRecv(svr, path, &vle) < 0){
	sginap(1);
	pfNotify(PFNFY_DEBUG,PFNFY_PRINT,"no vl events\n");
	return;
    }

    processVideoEvents(vle); 
    vlEventToDMBuffer(&vle, &field);
    
    if(!(glXAssociateDMPbufferSGIX(dpy, dmPbuf, videoFormat, field))){
	pfNotify(PFNFY_FATAL,PFNFY_PRINT, "can't associate DMPbuffer\n");
    }

    /*
     *  Make the dmpbuffer current as a readable, Performer will
     *  copy the texture from it to the current pfTexture because of:
     *  pfTexLoadMode(Tex, PFTEX_LOAD_BASE, PFTEX_BASE_AUTO_SUBLOAD); 
     *  When the size and format of the pbuffer and the texture
     *  are compatible, the O2 implementation allows this copy
     *  to be done by reference.
     */
    if(!(glXMakeCurrentReadSGI(dpy, pfGetWinWSDrawable(Win), dmPbuf, pfGetWinGLCxt(Win)))){
	pfNotify(PFNFY_FATAL,PFNFY_PRINT, "can't make current read\n");
    }

    DrawScene();

    /* Tell VL that we are done using this dmbuffer so it can use it for
     * future frames.  This is not a release of memory it is a release of
     * a lock.
     */
    dmBufferFree(field);
}

void setDMparams(void)
{
    DMparams *poolParams;
    int glBufCt, videoBufCt;

    /* Create image params to correspond to video size*/
    dmParamsCreate(&videoFormat);
    dmSetImageDefaults(videoFormat, fieldWidth, fieldHeight, DM_IMAGE_PACKING_RGBA);
    dmParamsSetEnum(videoFormat, DM_IMAGE_LAYOUT, DM_IMAGE_LAYOUT_GRAPHICS);
    
    /* Create and initialize a dmbuffer pool for use by VL and GL.
     * This is the logic that makes this code example fast path video texturing
     * for O2 ! 
     */
    dmParamsCreate(&poolParams);
    dmBufferSetPoolDefaults(poolParams, 0, 0, DM_FALSE, DM_FALSE);
    
    /* VL and GL are clients of the dmbuffer.  A call to vlDMPoolGetParams
     * or dmBufferGetGLPoolParams *sets* the pools buffer count to
     *	    MAX(previous_ct, required_buffers)
     *
     * where previous_ct is the buffer count of the pool prior to calling
     * the function and required_buffers is the number of buffers required
     * by the client; VL in the vlDMPoolGetParams case and GL in the 
     * dmBufferGetGLPoolParams case.
     *
     * When multiple clients access the pool the buffer count needs to
     * be the sum of the required buffers of all the clients.
     */
    
    /* Get VL pool constraints */
    vlDMPoolGetParams(svr, path, drn, poolParams);
    videoBufCt = dmParamsGetInt(poolParams, DM_BUFFER_COUNT);
    
    /* Get GL pool constraints */
    dmParamsSetInt(poolParams, DM_BUFFER_COUNT, 0);
    dmBufferGetGLPoolParams(videoFormat, poolParams);
    glBufCt = dmParamsGetInt(poolParams, DM_BUFFER_COUNT);
    
    /* Add the buffer counts of GL and VL and set the pool accordingly.
     * Add one buffer for our application (yet another client of the dmbuffer).
     */
    dmParamsSetInt(poolParams, DM_BUFFER_COUNT, videoBufCt + glBufCt + 1); 
    
    dmBufferCreatePool(poolParams, &pool);
    dmParamsDestroy(poolParams);
}

void main(void)
{
    DMbuffer dmbuf;
    void *arena=NULL;
    pfGeoSet *screenGeom=NULL;
    

    /* Setup video path */
    svr = vlOpenVideo(""); 
    src = vlGetNode(svr, VL_SRC, VL_VIDEO, VL_ANY); 
    drn = vlGetNode(svr, VL_DRN, VL_MEM, VL_ANY); 
    path = vlCreatePath(svr, VL_ANY, src, drn); 
    
    /* Set up the hardware for and define the usage of the path */
    vlSetupPaths(svr, (VLPathList)&(path), 1, VL_SHARE, VL_SHARE);
    SetVLPathControls();

    /* Initialize Performer */    
    pfInit();
    
    arena = pfGetSharedArena();
    pfMultiprocess(PFMP_DEFAULT);
    pfConfig();	
    
    CreateWindow(arena);
    CreatePbuffer();
    
    setDMparams(); 

    /* Allocate a dmbuffer and associate with the pbuffer in order to initialize
     * the OpenGL state of the pbuffer
     */
    dmBufferAllocate(pool, &dmbuf);

    glXAssociateDMPbufferSGIX(dpy, dmPbuf, videoFormat, dmbuf);

    /* Initialize DMpbuffer */
    glXMakeCurrent(dpy, dmPbuf, dmpc);
    glViewport(0, 0, frameWidth, frameHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, frameWidth, 0, frameHeight, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glClear(GL_COLOR_BUFFER_BIT);
    glRasterPos2i(0, 0);
    dmBufferFree(dmbuf);
    glXMakeCurrent(dpy,  pfGetWinWSDrawable(Win), pfGetWinGLCxt(Win));
    
    /* Generate a texture, bind and initialize */
    InitTexture(arena);

    screenGeom = pfNewGSet(arena);
    /* create scene */
    SceneDL = MakeScene(screenGeom, arena);

    vlDMPoolRegister(svr, path, drn, pool); 	/* Register dmbuffer with VL*/
    vlBeginTransfer(svr, path, 0, NULL);	/*Begin the data transfer */ 
    
    while(1){
        if (XPending(dpy)) {
            XEvent event;
            KeySym ks;

            XNextEvent(dpy, &event);
            switch(event.type) {
              case KeyPress:
                XLookupString(&event.xkey, NULL, 0, &ks, NULL);
		switch(ks) {
                  case XK_Escape :
                    ExitVtO2();
		  case XK_s:
		    spinning = !spinning;
		    break;
	         }
	         break;
             default:
                 break;
            }
        }
	redraw();
    }
}