#include "vpacsample.h" /* Demonstration Stereographics OpenGL application for the VPAC portable stereograph display hardware. The purpose of this software is to illustrate how to set up the stereographic settings for frame sequenial stereo and dual display stereo. A side purpose is to illustrate GLUT calls and handlers. */ /* See vpacsample.h */ OPTIONS options; CAMERA camera; INTERFACESTATE interfacestate; int main(int argc,char **argv) { int i; /* The default camera attributes */ CameraHome(0); camera.stereo = NOSTEREO; camera.screenwidth = 300; camera.screenheight = 300; /* Default options */ options.debug = FALSE; options.fullscreen = FALSE; options.windowdump = FALSE; options.record = FALSE; options.targetfps = 30; /* State of the input device, mouse in this case */ interfacestate.button = -1; interfacestate.shift = FALSE; interfacestate.mouseh = 0; interfacestate.mousev = 0; /* Parse the command line arguments */ for (i=1;i': /* Move backward */ case '.': TranslateCamera(0.0,0.0,-camera.focallength/50); break; case '-': /* Zoom in */ case '_': if (camera.aperture < 90) camera.aperture *= 1.05; break; case '+': /* Zoom out */ case '=': if (camera.aperture > 5) camera.aperture /= 1.05; break; } } /* Deal with special key strokes */ void HandleSpecialKeyboard(int key,int x, int y) { if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) interfacestate.shift = TRUE; else interfacestate.shift = FALSE; switch (key) { case GLUT_KEY_LEFT: if (interfacestate.shift) TranslateCamera(-1.0,0.0,0.0); else RotateCamera(-1.0,0.0,0.0); break; case GLUT_KEY_RIGHT: if (interfacestate.shift) TranslateCamera(1.0,0.0,0.0); else RotateCamera(1.0,0.0,0.0); break; case GLUT_KEY_UP: if (interfacestate.shift) TranslateCamera(0.0,1.0,0.0); else RotateCamera(0.0,1.0,0.0); break; case GLUT_KEY_DOWN: if (interfacestate.shift) TranslateCamera(0.0,-1.0,0.0); else RotateCamera(0.0,-1.0,0.0); break; case GLUT_KEY_F1: CameraHome(1); break; case GLUT_KEY_F2: CameraHome(2); break; case GLUT_KEY_F3: CameraHome(3); break; case GLUT_KEY_F4: CameraHome(4); break; case GLUT_KEY_F5: CameraHome(5); break; case GLUT_KEY_F6: CameraHome(6); break; } } /* Rotate (ix,iy) or roll (iz) the camera about the focal point */ void RotateCamera(double ix,double iy,double iz) { XYZ vp,vu,vd,right; XYZ newvp,newr; double dx,dy,dz,radius; vu = camera.vu; Normalise(&vu); vp = camera.vp; vd = camera.vd; Normalise(&vd); CROSSPROD(vd,vu,right); Normalise(&right); /* Handle the roll */ if (iz != 0) { camera.vu.x += iz * right.x; camera.vu.y += iz * right.y; camera.vu.z += iz * right.z; Normalise(&camera.vu); return; } /* Distance from the rotate point */ dx = camera.vp.x - camera.pr.x; dy = camera.vp.y - camera.pr.y; dz = camera.vp.z - camera.pr.z; radius = sqrt(dx*dx + dy*dy + dz*dz); /* Determine the new view point */ newvp.x = vp.x + ix * right.x + iy * vu.x - camera.pr.x; newvp.y = vp.y + ix * right.y + iy * vu.y - camera.pr.y; newvp.z = vp.z + ix * right.z + iy * vu.z - camera.pr.z; Normalise(&newvp); camera.vp.x = camera.pr.x + radius * newvp.x; camera.vp.y = camera.pr.y + radius * newvp.y; camera.vp.z = camera.pr.z + radius * newvp.z; /* Determine the new right vector */ newr.x = camera.vp.x + right.x - camera.pr.x; newr.y = camera.vp.y + right.y - camera.pr.y; newr.z = camera.vp.z + right.z - camera.pr.z; Normalise(&newr); newr.x = camera.pr.x + radius * newr.x - camera.vp.x; newr.y = camera.pr.y + radius * newr.y - camera.vp.y; newr.z = camera.pr.z + radius * newr.z - camera.vp.z; camera.vd.x = camera.pr.x - camera.vp.x; camera.vd.y = camera.pr.y - camera.vp.y; camera.vd.z = camera.pr.z - camera.vp.z; Normalise(&camera.vd); /* Determine the new up vector */ CROSSPROD(newr,camera.vd,camera.vu); Normalise(&camera.vu); } /* Translate (pan) the camera view point */ void TranslateCamera(double ix,double iy,double iz) { XYZ right; CROSSPROD(camera.vd,camera.vu,right); Normalise(&right); camera.vp.x += iy*camera.vu.x + ix*right.x + iz*camera.vd.x; camera.vp.y += iy*camera.vu.y + ix*right.y + iz*camera.vd.y; camera.vp.z += iy*camera.vu.z + ix*right.z + iz*camera.vd.z; camera.pr.x += iy*camera.vu.x + ix*right.x + iz*camera.vd.x; camera.pr.y += iy*camera.vu.y + ix*right.y + iz*camera.vd.y; camera.pr.z += iy*camera.vu.z + ix*right.z + iz*camera.vd.z; } /* Handle mouse events. Do nothing special, just remember the state. */ void HandleMouse(int button,int state,int x,int y) { if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) interfacestate.shift = TRUE; else interfacestate.shift = FALSE; if (state == GLUT_DOWN) { if (button == GLUT_LEFT_BUTTON) { interfacestate.button = GLUT_LEFT_BUTTON; } else if (button == GLUT_MIDDLE_BUTTON) { interfacestate.button = GLUT_MIDDLE_BUTTON; } else { /* Assigned to menus */ } } interfacestate.mouseh = x; interfacestate.mousev = y; } /* Handle mouse motion */ void HandleMouseMotion(int x,int y) { double dx,dy; dx = x - interfacestate.mouseh; dy = y - interfacestate.mousev; if (interfacestate.button == GLUT_LEFT_BUTTON) { if (interfacestate.shift) TranslateCamera(-dx/25,dy/25,0.0); else RotateCamera(-dx/25,dy/25,0.0); } else if (interfacestate.button == GLUT_MIDDLE_BUTTON) { if (interfacestate.shift) TranslateCamera(0.0,0.0,camera.focallength*dx/50); else RotateCamera(0.0,0.0,dx/50); } interfacestate.mouseh = x; interfacestate.mousev = y; } /* What to do on moviing nouse, the button isn't down. */ void HandlePassiveMotion(int x,int y) { interfacestate.mouseh = x; interfacestate.mousev = y; } /* Handle the main menu */ void HandleMainMenu(int whichone) { switch (whichone) { case 10: exit(0); break; } } /* Handle the camera menu */ void HandleCameraMenu(int whichone) { CameraHome(whichone-1); } /* How to handle visibility If the window is not visible then don't draw it, allows layered windows without a processor load blowout. */ void HandleVisibility(int visible) { if (visible == GLUT_VISIBLE) glutIdleFunc(HandleIdle); else glutIdleFunc(NULL); } /* What to do on an idle event. Maintain a constant frame rate. */ void HandleIdle(void) { static double tstart = -1; double tstop; if (tstart < 0) tstart = GetRunTime(); tstop = GetRunTime(); if (tstop - tstart > 1.0/options.targetfps) { glutPostRedisplay(); tstart = tstop; } } /* Handle a window reshape/resize */ void HandleReshape(int w,int h) { camera.screenwidth = w; camera.screenheight = h; } /* Display the program usage information */ void GiveUsage(char *cmd) { fprintf(stderr,"Usage: %s [command line options]\n",cmd); fprintf(stderr,"Command line options\n"); fprintf(stderr," -h this text\n"); fprintf(stderr," -f full screen\n"); fprintf(stderr," -s active stereo\n"); fprintf(stderr," -ss dual screen stereo\n"); fprintf(stderr,"Mouse buttons\n"); fprintf(stderr," left camera rotate\n"); fprintf(stderr," shift left camera pan\n"); fprintf(stderr," middle camera roll\n"); fprintf(stderr," shift middle camera forward, reverse\n"); fprintf(stderr,"Key Strokes\n"); fprintf(stderr," arrow keys camera rotate\n"); fprintf(stderr," <,> camera forward, reverse\n"); fprintf(stderr," +,- camera zoom in, out\n"); fprintf(stderr," [,] camera roll\n"); fprintf(stderr," r toggle window recording\n"); fprintf(stderr," w capture window\n"); fprintf(stderr," ESC,q quit\n"); exit(-1); } /* Move the camera to the home position */ void CameraHome(int position) { XYZ origin = {0,0,0}; XYZ up = {0.0,0.0,1.0},x = {1.0,0.0,0.0}; switch (position) { case 0: camera.aperture = 60; camera.focallength = 6; camera.eyesep = camera.focallength / 25; camera.near = camera.focallength / 10; camera.far = camera.focallength * 10; camera.vp.x = -camera.focallength; camera.vp.y = 0; camera.vp.z = 0; camera.vd.x = 1; camera.vd.y = 0; camera.vd.z = 0; camera.vu = up; break; case 1: /* Front */ camera.vp.x = -camera.focallength; camera.vp.y = 0; camera.vp.z = 0; camera.vd.x = 1; camera.vd.y = 0; camera.vd.z = 0; camera.vu = up; break; case 2: /* Back */ camera.vp.x = camera.focallength; camera.vp.y = 0; camera.vp.z = 0; camera.vd.x = -1; camera.vd.y = 0; camera.vd.z = 0; camera.vu = up; break; case 3: /* Left */ camera.vp.x = 0; camera.vp.y = camera.focallength; camera.vp.z = 0; camera.vd.x = 0; camera.vd.y = -1; camera.vd.z = 0; camera.vu = up; break; case 4: /* Right */ camera.vp.x = 0; camera.vp.y = -camera.focallength; camera.vp.z = 0; camera.vd.x = 0; camera.vd.y = 1; camera.vd.z = 0; camera.vu = up; break; case 5: /* Top */ camera.vp.x = 0; camera.vp.y = 0; camera.vp.z = camera.focallength; camera.vd.x = 0; camera.vd.y = 0; camera.vd.z = -1; camera.vu = x; break; case 6: /* Bottom */ camera.vp.x = 0; camera.vp.y = 0; camera.vp.z = -camera.focallength; camera.vd.x = 0; camera.vd.y = 0; camera.vd.z = 1; camera.vu = x; break; } camera.pr = origin; Normalise(&camera.vd); Normalise(&camera.vu); } /* Calculate the length of a vector */ double Modulus(XYZ p) { return(sqrt(p.x * p.x + p.y * p.y + p.z * p.z)); } /* Normalise a vector */ void Normalise(XYZ *p) { double length; length = p->x * p->x + p->y * p->y + p->z * p->z; if (length > 0) { length = sqrt(length); p->x /= length; p->y /= length; p->z /= length; } else { p->x = 0; p->y = 0; p->z = 0; } } /* Time scale at microsecond resolution but returned as seconds */ double GetRunTime(void) { double sec = 0; struct timeval tp; gettimeofday(&tp,NULL); sec = tp.tv_sec + tp.tv_usec / 1000000.0; return(sec); } /* Write the current view to an image file Do the right thing for stereo, ie: two images */ int Dump2TGA(int width,int height,int stereo) { FILE *fptr; static int counter = 0; char fname[32]; unsigned char *image; /* Allocate our buffer for the image */ if ((image = malloc(width*height*4)) == NULL) { fprintf(stderr,"Failed to allocate memory for image\n"); return(FALSE); } glFinish(); if (stereo) sprintf(fname,"L_%04d.tga",counter); else sprintf(fname,"C_%04d.tga",counter); if ((fptr = fopen(fname,"w")) == NULL) { fprintf(stderr,"WindowDump - Failed to open file for window dump\n"); return(FALSE); } /* Copy the image into our buffer */ glReadBuffer(GL_BACK_LEFT); glReadPixels(0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,image); /* Write the file */ WriteTGA(fptr,image,width,height); fclose(fptr); if (stereo) { /* Open the file */ sprintf(fname,"R_%04d.tga",counter); if ((fptr = fopen(fname,"w")) == NULL) { fprintf(stderr,"Failed to open file for window dump\n"); return(FALSE); } /* Copy the image into our buffer */ glReadBuffer(GL_BACK_RIGHT); glReadPixels(0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,image); /* Write the file */ WriteTGA(fptr,image,width,height); fclose(fptr); } free(image); counter++; return(TRUE); } void WriteTGA(FILE *fptr,unsigned char *image,int nx,int ny) { int i,j; long index=0; putc(0,fptr); /* Length of ID */ putc(0,fptr); /* No colour map */ putc(2,fptr); /* Uncompressed */ putc(0,fptr); /* Index of colour map entry */ putc(0,fptr); putc(0,fptr); /* Colour map length */ putc(0,fptr); putc(0,fptr); /* Colour map size */ putc(0,fptr); /* X origin */ putc(0,fptr); putc(0,fptr); /* Y origin */ putc(0,fptr); putc((nx & 0x00ff),fptr); /* X width */ putc((nx & 0xff00) / 256,fptr); putc((ny & 0x00ff),fptr); /* Y width */ putc((ny & 0xff00) / 256,fptr); putc(24,fptr); /* 24 bit */ putc(0x00,fptr); for (j=0;j