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:
case GL_RIGHT: case GL_FRONT_RIGHT:
case GL_BACK_RIGHT:
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:
case GL_BACK:
case GL_FRONT_AND_BACK:
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)
}
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) {
}
/* 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) {
}
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);
} |
|