Picture

OpenGL supports stereo via quad buffering. The programmer needs to choose a visual that supports stereo. Then they draw to the left front/back buffer (you designate left buffer using glDrawBuffer and then set the xform appropriately before drawing the left-eye view) and to the  right front/back buffer. If drawing to back buffers then glXSwapBuffers must be called after rendering.

On RealityEngine and InfiniteReality Silicon Graphics supports stereo in a window. So the programmer can simply find a visual that supports stereo and then create a window with that visual. On Impact you need to call setmon in order to put the gfx in a state where it can support stereo. (If you don't call setmon then you won't be able to find any visuals that support stereo). On EXPRESS machines (XZ, EXTREME, XL, Elan) you need to call setmon and then restart the X server.
 

Here is a sample program:
 

 /*
 ** A simple OpenGL stereo application.  Uses the GLw Motif DrawingArea widget
 ** with a popup menu.  Includes a simple API for controlling stereo.
 **
 ** cc -o glwstereo glwstereo.c -lGLw -lXm -lXt -lGL -lXext -lX11 -lm
 */

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
 #include <sys/time.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/StringDefs.h>
 #include <Xm/Xm.h>
 #include <Xm/Form.h>
 #include <Xm/RowColumn.h>
 #include <Xm/PushBG.h>
 #include <Xm/SeparatoG.h>
 #include <X11/GLw/GLwMDrawA.h>

 /************************************************************************/

 #include <X11/extensions/SGIStereo.h>
 static struct stereoStateRec {

     Bool               useSGIStereo;
     Display           *currentDisplay;
     Window         currentWindow;
     GLXContext  currentContext;
     GLenum         currentDrawBuffer;
     int                   currentStereoBuffer;
     Bool               enabled;
     char               *stereoCommand;
     char               *restoreCommand;

 } stereo;
 

 /* call instead of glDrawBuffer */
 void
 stereoDrawBuffer(GLenum mode)
 {

     if (stereo.useSGIStereo) {
     stereo.currentDrawBuffer = mode;
     switch (mode) {

       case GL_FRONT:
       case GL_BACK:
       case GL_FRONT_AND_BACK:

       /*
       ** Simultaneous drawing to both left and right buffers isn't
       ** really possible if we don't have a stereo capable visual.
       ** For now just fall through and use the left buffer.
       */

       case GL_LEFT:
       case GL_FRONT_LEFT:
       case GL_BACK_LEFT:

         stereo.currentStereoBuffer = STEREO_BUFFER_LEFT;
         break;

       case GL_RIGHT:
       case GL_FRONT_RIGHT:

         stereo.currentStereoBuffer = STEREO_BUFFER_RIGHT;
         mode = GL_FRONT;
         break;

       case GL_BACK_RIGHT:

         stereo.currentStereoBuffer = STEREO_BUFFER_RIGHT;
         mode = GL_BACK;
         break;

       default:
                 break;

     }

     if (stereo.currentDisplay  stereo.currentWindow) {
          glXWaitGL();   /* sync with GL command stream before calling X */
          XSGISetStereoBuffer(stereo.currentDisplay,
               stereo.currentWindow,
               stereo.currentStereoBuffer);
          glXWaitX();   /* sync with X command stream before calling GL */

           }
      }
 glDrawBuffer(mode);
 }

 /* call instead of glClear */
 void
 stereoClear(GLbitfield mask)
 {

     if (stereo.useSGIStereo) {
          GLenum drawBuffer = stereo.currentDrawBuffer;
          switch (drawBuffer) {

      case GL_FRONT:

        stereoDrawBuffer(GL_FRONT_RIGHT);
        glClear(mask);
        stereoDrawBuffer(drawBuffer);
        break;

       case GL_BACK:

         stereoDrawBuffer(GL_BACK_RIGHT);
         glClear(mask);
         stereoDrawBuffer(drawBuffer);
         break;

       case GL_FRONT_AND_BACK:

         stereoDrawBuffer(GL_RIGHT);
         glClear(mask);
         stereoDrawBuffer(drawBuffer);
         break;

       case GL_LEFT:
       case GL_FRONT_LEFT:
       case GL_BACK_LEFT:
       case GL_RIGHT:
       case GL_FRONT_RIGHT:
       case GL_BACK_RIGHT:
       default:
            break;

     }

      }
      glClear(mask);
 }

 /* call after glXMakeCurrent */

 void
 stereoMakeCurrent(Display *dpy, Window win, GLXContext ctx)
 {

     if (stereo.useSGIStereo) {
          if (dpy  (dpy != stereo.currentDisplay)) {
               int event, error;
               /* Make sure new Display supports SGIStereo */
               if (XSGIStereoQueryExtension(dpy, event, error) == False)

       {
            dpy = NULL;
       }

     }

     if (dpy  && win && (win != stereo.currentWindow)) {
          /* Make sure new Window supports SGIStereo */
          if (XSGIQueryStereoMode(dpy, win) == X_STEREO_UNSUPPORTED) {
               win = None;
          }
     }

     if (ctx  && (ctx != stereo.currentContext)) {
          GLint drawBuffer;
          glGetIntegerv(GL_DRAW_BUFFER, &drawBuffer);
          stereoDrawBuffer((GLenum) drawBuffer);
     }

     stereo.currentDisplay = dpy;
     stereo.currentWindow = win;
     stereo.currentContext = ctx;

      }
 }

 /* call to turn on stereo viewing */
 void
 stereoEnable(void)
 {

     if (!stereo.enabled  stereo.stereoCommand) {
          system(stereo.stereoCommand);
     }
     stereo.enabled = True;

 }

 /* call to turn off stereo viewing */
 void
 stereoDisable(void)
 {

     if (stereo.enabled && stereo.restoreCommand) {
          system(stereo.restoreCommand);
     }
     stereo.enabled = False;

 }

 /* call before using stereo */
 void
 stereoInit(GLboolean usingStereoVisual, char *stereoCmd, char *restoreCmd)
 {

     stereo.useSGIStereo = !usingStereoVisual;
     stereo.currentDisplay = NULL;
     stereo.currentWindow = None;
     stereo.currentContext = NULL;
     stereo.currentDrawBuffer = GL_NONE;
     stereo.currentStereoBuffer = STEREO_BUFFER_NONE;
     stereo.enabled = False;
     if (stereo.stereoCommand) {
          free(stereo.stereoCommand);
     }
     stereo.stereoCommand = stereoCmd ? strdup(stereoCmd) : NULL;
     if (stereo.restoreCommand) {
          free(stereo.restoreCommand);
     }
     stereo.restoreCommand = restoreCmd ? strdup(restoreCmd) : NULL;

 }

 /* call when done using stereo */
 void
 stereoDone(void)
 {

     stereoDisable();
     stereoInit(True, NULL, NULL);

 }

 /************************************************************************/

 #define APP_CLASS "GLWApp"

 String fallbackResources[] = {

     /*
     ** Widget resources
     */
     "*sgiMode: True",
     "*useSchemes: all",
     "*title: Simple OpenGL Stereo Demo",
     "*form.width: 300",
     "*form.height: 300",
     /*
     ** Non-Widget (application) resources
     */
     "*stereoCommand: /usr/gfx/setmon -n 640x512_120s",
     "*restoreCommand: /usr/gfx/setmon -n 72HZ",
     "*SGIStereoCommand: /usr/gfx/setmon -n STR_BOT",
     "*SGIRestoreCommand: /usr/gfx/setmon -n 60HZ",
     NULL,

 };

 struct appResourceRec {

     _XtString stereoCommand;
     _XtString restoreCommand;
     _XtString SGIStereoCommand;
     _XtString SGIRestoreCommand;
     

 } appResources;

 

 XtResource appResourceList[] = {

     { "stereoCommand",
     XtCString, XtRString, sizeof (_XtString),
     XtOffsetOf(struct appResourceRec, stereoCommand),
     XtRImmediate, (XtPointer) NULL },
     { "restoreCommand",
     XtCString, XtRString, sizeof (_XtString),
     XtOffsetOf(struct appResourceRec, restoreCommand),
     XtRImmediate, (XtPointer) NULL },
     { "SGIStereoCommand",
     XtCString, XtRString, sizeof (_XtString),
     XtOffsetOf(struct appResourceRec, SGIStereoCommand),
     XtRImmediate, (XtPointer) NULL },
     { "SGIRestoreCommand",
     XtCString, XtRString, sizeof (_XtString),
     XtOffsetOf(struct appResourceRec, SGIRestoreCommand),
     XtRImmediate, (XtPointer) NULL },

 };

 XtAppContext appContext;
 XtWorkProcId appWorkProcId;
 Widget topLevel, form, glw, popup;
 GLXContext ctx;
 GLboolean animationEnabled = GL_FALSE;
 GLboolean stereoEnabled = GL_FALSE;
 double lastTime = 0.0;
 double rotationRate = 360.0 / 8.0;
 double rotation = 0.0;

 static void

 drawCylinder(void)
 {

     int numSides = 20;
     double radius = 0.7;
     double stepSize = 2.0*M_PI / numSides;
     int i;

     glBegin(GL_TRIANGLE_STRIP);
     for (i=0; i =numSides; ++i) {
          double a = i * stepSize;
          GLfloat x = cos(a);
          GLfloat y = sin(a);

          glNormal3f(x, y, 0.0);
          glVertex3f(x * radius, y * radius, 0.7);
          glNormal3f(x, y, 0.0);
          glVertex3f(x * radius, y * radius, -0.7);

     }


 glEnd();
 }

 static void
 initialize(void)
 {

     GLfloat lightAmb[4] = { 0.2, 0.2, 0.2, 1.0 };
     GLfloat lightPos[4] = { 1.5, 1.5, 1.5, 1.0 };
     GLfloat matAmbDiff[4] = { 0.30, 0.40, 0.80, 1.0 };
     

     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     glTranslatef(0, 0, -2);

     glEnable(GL_DEPTH_TEST);
     glEnable(GL_LIGHTING);
     glEnable(GL_LIGHT0);
     glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);
     glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matAmbDiff);
     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

 }

 void
 stereoFrustum(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top,  GLfloat near, GLfloat
      far, GLfloat eyeDist, GLfloat eyeOffset)

 {

     GLfloat eyeShift = (eyeDist - near) * (eyeOffset / eyeDist);

     glFrustum(left+eyeShift, right+eyeShift, bottom, top, near, far);
     glTranslatef(-eyeShift, 0.0, 0.0);

 }

 static void
 redraw(void)

 {

     GLfloat eyeDist = 2.0;
     GLfloat eyeOffset = 0.05;
     

     glPushMatrix();
     glRotated(rotation, 0, 1, 0);

     if (stereoEnabled) {

       /*
       ** Draw right-eye view.
       */
       glMatrixMode(GL_PROJECTION);
       glLoadIdentity();
       stereoFrustum(-1, 1, -1, 1, 1, 3, eyeDist, eyeOffset);
       glMatrixMode(GL_MODELVIEW);

       stereoDrawBuffer(GL_BACK_RIGHT);
       stereoClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
       drawCylinder();

     } else {
          eyeOffset = 0.0;
     }

     /*
     ** Draw left-eye view.
     */
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     stereoFrustum(-1, 1, -1, 1, 1, 3, eyeDist, -eyeOffset);
     glMatrixMode(GL_MODELVIEW);

     stereoDrawBuffer(GL_BACK_LEFT);
     stereoClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     drawCylinder();

     glPopMatrix();
     GLwDrawingAreaSwapBuffers(glw);

 }

 double
 getTime(void)
 {

     struct timeval time;
     gettimeofday(&time);
     return ((double) time.tv_sec + (double) time.tv_usec * 1E-6);

 }

 static Boolean
 animateWP(XtPointer clientData)
 {

     double currentTime = getTime();
     double elapsed = currentTime - lastTime;

     lastTime = currentTime;
     rotation += rotationRate * ((elapsed = 1.0) ? elapsed : 1.0);
     if (rotation = 360) rotation -= 360;

     redraw();
     return False;

 }

 static void
 popupCB(Widget w, XtPointer clientData, XtPointer callData)
 {

     int button = (int) clientData;
     Bool needsRedraw = False;

     switch (button) {
     case 0:
          glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
          needsRedraw = True;
          break;
     case 1:
          glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
          needsRedraw = True;
          break;
     case 2:
          animationEnabled = !animationEnabled;
          if (animationEnabled) {
               lastTime = getTime();
               appWorkProcId = XtAppAddWorkProc(appContext, animateWP, NULL);
          } else {
               XtRemoveWorkProc(appWorkProcId);
          }
          break;
     case 3:
          stereoEnabled = !stereoEnabled;
          if (stereoEnabled) {
          stereoEnable();
          } else {
               stereoDisable();
          }
          needsRedraw = True;
          break;
     case 4:
          stereoDone();
          exit(EXIT_SUCCESS);
          break;
     default:
          break;
     }

     if (needsRedraw) {
          redraw();
     }

 }

 static void
 popupEH(Widget w, XtPointer clientData, XEvent *event, Boolean *cont)
 {

     Widget popup = *((Widget *) clientData);

     if ((event->type = = ButtonPress) && (event->xbutton.button = = Button3)) {
          XmMenuPosition(popup, event- xbutton);
          XtManageChild(popup);
     }

 }

 static void
 exposeCB(Widget w, XtPointer clientData, XtPointer callData)
 {

     redraw();

 }

 static void
 resizeCB(Widget w, XtPointer clientData, XtPointer callData)
 {

     GLwDrawingAreaCallbackStruct *glwcallData =
          (GLwDrawingAreaCallbackStruct *) callData;

     glViewport(0, 0, glwcallData- width, glwcallData- height);
     redraw();

 }

 int
 main(int argc, char **argv)
 {

     int stereoAttrs[] = {
          GLX_STEREO,
          GLX_RGBA,
          GLX_DOUBLEBUFFER,
          GLX_RED_SIZE, 1,
          GLX_DEPTH_SIZE, 1,
     None,
     };

     int visualAttrs[] = {
          GLX_RGBA,
          GLX_DOUBLEBUFFER,
          GLX_RED_SIZE, 1,
          GLX_DEPTH_SIZE, 1,
     None,
     };
     Display *dpy;
     int scrn;
     XVisualInfo *vis;
     Arg args[10];
     int n;

     topLevel = XtOpenApplication(&appContext, APP_CLASS, NULL, 0,
          &argc, argv, fallbackResources,
          applicationShellWidgetClass, NULL, 0);

     XtGetApplicationResources(topLevel, &appResources, appResourceList,
          XtNumber(appResourceList), NULL, 0);

     dpy = XtDisplay(topLevel);
     scrn = XScreenNumberOfScreen(XtScreen(topLevel));

     if ((vis = glXChooseVisual(dpy, scrn, stereoAttrs)) != NULL) {
          /* initialize for use with a stereo capable visual */
          stereoInit(True,
               appResources.stereoCommand,
               appResources.restoreCommand);

     } else if ((vis = glXChooseVisual(dpy, scrn, visualAttrs)) != NULL)

 {

          /* initialize for use without a stereo capable visual */
          stereoInit(False,
               appResources.SGIStereoCommand,
               appResources.SGIRestoreCommand);

          /* Force the topLevel widget to maintain a 2:1 aspect ratio */
          n = 0;
          XtSetArg(args[n], XmNminAspectX, 2); n++;
          XtSetArg(args[n], XmNminAspectY, 1); n++;
          XtSetArg(args[n], XmNmaxAspectX, 2); n++;
          XtSetArg(args[n], XmNmaxAspectY, 1); n++;
          XtSetValues(topLevel, args, n);

     } else {
          fprintf(stderr, "can't find appropriate visual\n");
          exit(EXIT_FAILURE);
     }

     if ((ctx = glXCreateContext(dpy, vis, 0, True)) == NULL) {
          fprintf(stderr, "can't create rendering context\n");
          exit(EXIT_FAILURE);
     }

     form = XtCreateManagedWidget("form", xmFormWidgetClass, topLevel,  NULL, 0);

     n = 0;
     XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
     XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
     XtSetArg(args[n], GLwNvisualInfo, vis); n++;
     glw = XtCreateManagedWidget("glw", glwMDrawingAreaWidgetClass, form, args, n);
     XtAddCallback(glw, GLwNexposeCallback, exposeCB, 0);
     XtAddCallback(glw, GLwNresizeCallback, resizeCB, 0);


     {
          XmButtonType buttonTypes[] = {
               XmPUSHBUTTON, XmPUSHBUTTON, XmSEPARATOR,
               XmPUSHBUTTON,
               XmSEPARATOR, XmPUSHBUTTON, XmSEPARATOR, XmPUSHBUTTON,
          };
          XmString buttonLabels[XtNumber(buttonTypes)];

          buttonLabels[0] = XmStringCreateLocalized("draw filled");
          buttonLabels[1] = XmStringCreateLocalized("draw lines");
          buttonLabels[2] = NULL;
          buttonLabels[3] = XmStringCreateLocalized("toggle animation");
          buttonLabels[4] = NULL;
          buttonLabels[5] = XmStringCreateLocalized("toggle stereo mode");
          buttonLabels[6] = NULL;
          buttonLabels[7] = XmStringCreateLocalized("quit");

          n = 0;
          XtSetArg(args[n], XmNbuttonCount, XtNumber(buttonTypes)); n++;
          XtSetArg(args[n], XmNbuttonType, buttonTypes); n++;
          XtSetArg(args[n], XmNbuttons, buttonLabels); n++;
          XtSetArg(args[n], XmNsimpleCallback, popupCB); n++;
          popup = XmCreateSimplePopupMenu(form, "popup", args, n);
          XtAddEventHandler(form, ButtonPressMask, False, popupEH, &popup);

          XmStringFree(buttonLabels[0]);
          XmStringFree(buttonLabels[1]);
          XmStringFree(buttonLabels[3]);
          XmStringFree(buttonLabels[5]);
          XmStringFree(buttonLabels[7]);
     }

     XtRealizeWidget(topLevel);

     GLwDrawingAreaMakeCurrent(glw, ctx);
     stereoMakeCurrent(dpy, XtWindow(glw), ctx);
     initialize();

     XtAppMainLoop(appContext);

}

All materials © Copyright 1996-97, StereoGraphics Corporation

[SGI GL] [SGI OpenGL] [How It Works]
[StereoGraphics] [Products] [How To Buy] [Software] [Developers] [News] [Stereo Cafe] [Support] [Contact Us]