Back to index

radiance  4R0+20100331
rhd_ogl.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: rhd_ogl.c,v 3.30 2006/06/07 17:52:04 schorsch Exp $";
00003 #endif
00004 /*
00005  * OpenGL driver for holodeck display.
00006  * Based on GLX driver using T-mesh.
00007  *
00008  * Define symbol STEREO for stereo viewing.
00009  * Define symbol DOBJ for display object viewing.
00010  */
00011 
00012 #ifdef NOSTEREO
00013 #ifdef STEREO
00014 #undef STEREO
00015 #else
00016 #undef NOSTEREO
00017 #endif
00018 #endif
00019 
00020 
00021 #include <time.h>
00022 #include <GL/glx.h>
00023 #include <GL/glu.h>
00024 #ifdef STEREO
00025 #include <X11/extensions/SGIStereo.h>
00026 #endif
00027 
00028 #include "standard.h"
00029 #include "rhd_odraw.h"
00030 #include "rhdisp.h"
00031 #include "paths.h"
00032 #ifdef DOBJ
00033 #include "rhdobj.h"
00034 #endif
00035 
00036 #include "x11icon.h"
00037 
00038 #ifndef RAYQLEN
00039 #define RAYQLEN             0             /* max. rays to queue before flush */
00040 #endif
00041 #ifndef MINWIDTH
00042 #define MINWIDTH     480           /* minimum graphics window width */
00043 #define MINHEIGHT    400           /* minimum graphics window height */
00044 #endif
00045 #ifndef VIEWDIST
00046 #define VIEWDIST     356           /* assumed viewing distance (mm) */
00047 #endif
00048 #ifndef BORWIDTH
00049 #define BORWIDTH     5             /* border width */
00050 #endif
00051 
00052 #ifndef FEQ
00053 #define FEQ(a,b)     ((a)-(b) <= FTINY && (a)-(b) >= -FTINY)
00054 #endif
00055 
00056 #define       VWHEADLOCK    01            /* head position is locked flag */
00057 #define       VWPERSP              02            /* perspective view is set */
00058 #define       VWORTHO              04            /* orthographic view is set */
00059 #define       VWCHANGE      010           /* view has changed */
00060 #define       VWSTEADY      020           /* view is now steady */
00061 #define VWMAPPED     040           /* window is mapped */
00062 
00063 #define GAMMA        1.4           /* default gamma correction */
00064 
00065 #define FRAMESTATE(s)       (((s)&(ShiftMask|ControlMask))==(ShiftMask|ControlMask))
00066 
00067 #define MOVPCT              7             /* percent distance to move /frame */
00068 #define MOVDIR(b)    ((b)==Button1 ? 1 : (b)==Button2 ? 0 : -1)
00069 #define MOVDEG              (-5)          /* degrees to orbit CW/down /frame */
00070 #define MOVORB(s)    ((s)&ShiftMask ? 1 : (s)&ControlMask ? -1 : 0)
00071 
00072 #define setstereobuf(bid)   (glXWaitGL(), \
00073                             XSGISetStereoBuffer(ourdisplay, gwind, bid), \
00074                             glXWaitX())
00075 
00076 #define  ourscreen   DefaultScreen(ourdisplay)
00077 #define  ourroot     RootWindow(ourdisplay,ourscreen)
00078 #define  ourmask     (StructureNotifyMask|ExposureMask|KeyPressMask|\
00079                      ButtonPressMask|ButtonReleaseMask)
00080 
00081 #define  levptr(etype)      ((etype *)&currentevent)
00082 
00083 struct driver odev;                /* global device driver structure */
00084 
00085 TMstruct      *tmGlobal;           /* global tone-mapping structure */
00086 
00087 char odev_args[64];                /* command arguments */
00088 
00089 static GLfloat       *depthbuffer = NULL; /* depth buffer */
00090 
00091 #ifdef STEREO
00092 static VIEW   vwright;             /* right eye view */
00093 static GLfloat       *depthright = NULL;  /* right depth buffer */
00094 #endif
00095 
00096 static int    rayqleft = 0;        /* rays left to queue before flush */
00097 
00098 static XEvent  currentevent;              /* current event */
00099 
00100 static unsigned long  ourblack=0, ourwhite=~0;
00101 
00102 static Display  *ourdisplay = NULL;       /* our display */
00103 static XVisualInfo  *ourvinf;             /* our visual information */
00104 static Window  gwind = 0;          /* our graphics window */
00105 static GLXContext    gctx;         /* our GLX context */
00106 
00107 static double pwidth, pheight;     /* pixel dimensions (mm) */
00108 
00109 static double dev_zmin, dev_zmax;  /* fore and aft clipping plane dist. */
00110 static double dev_zrat;            /* (1. - dev_zmin/dev_zmax) */
00111 
00112 #define setzrat()    (dev_zrat = 1. - dev_zmin/dev_zmax)
00113 #define mapdepth(d)  ((d)>0.9995 ? FHUGE : dev_zmin/(1.-(d)*dev_zrat))
00114 
00115 static int    inpresflags;         /* input result flags */
00116 
00117 static int    viewflags;           /* what's happening with view */
00118 
00119 /*
00120 static int  resizewindow(), getevent(), getkey(), moveview(), wipeclean(),
00121               xferdepth(), freedepth(), setglortho(),
00122               setglpersp(), getframe(), getmove(), fixwindow(), mytmflags();
00123 
00124 static double getdistance();
00125 */
00126 static void checkglerr(char *where);
00127 static void xferdepth(void);
00128 static void freedepth(void);
00129 static double getdistance(int dx, int dy, FVECT direc);
00130 static int mytmflags(void);
00131 static void getevent(void);
00132 static void draw3dline(register FVECT wp[2]);
00133 static void draw_grids(int fore);
00134 static int moveview(int dx, int dy, int mov, int orb);
00135 static void getframe(XButtonPressedEvent *ebut);
00136 static void getmove(XButtonPressedEvent *ebut);
00137 static void getkey(register XKeyPressedEvent *ekey);
00138 static void fixwindow(register XExposeEvent *eexp);
00139 static void resizewindow(register XConfigureEvent *ersz);
00140 static void waitabit(void);
00141 static void setglpersp(void);
00142 static void setglortho(void);
00143 static void wipeclean(void);
00144 
00145 
00146 #ifdef STEREO
00147 static void pushright(void);
00148 static void popright(void);
00149 #endif
00150 
00151 extern int    gmPortals;    /* GL portal list id */
00152 
00153 extern time_t time();
00154 
00155 
00156 extern void
00157 dev_open(id)                /* initialize GLX driver */
00158 char  *id;
00159 {
00160        extern char   *getenv();
00161        static RGBPRIMS      myprims = STDPRIMS;
00162        static int    atlBest[] = {GLX_RGBA, GLX_DOUBLEBUFFER,
00163                             GLX_RED_SIZE,8, GLX_GREEN_SIZE,8,
00164                             GLX_BLUE_SIZE,8, GLX_DEPTH_SIZE,15, None};
00165        static int    atlOK[] = {GLX_RGBA, GLX_DOUBLEBUFFER,
00166                             GLX_RED_SIZE,4, GLX_GREEN_SIZE,4,
00167                             GLX_BLUE_SIZE,4, GLX_DEPTH_SIZE,15, None};
00168        char   *ev;
00169        double gamval = GAMMA;
00170        RGBPRIMP      dpri = stdprims;
00171        XSetWindowAttributes ourwinattr;
00172        XWMHints      ourxwmhints;
00173        XSizeHints    oursizhints;
00174                                    /* check for unsupported stereo */
00175 #ifdef NOSTEREO
00176        error(INTERNAL, "stereo display driver unavailable");
00177 #endif
00178                                    /* open display server */
00179        ourdisplay = XOpenDisplay(NULL);
00180        CHECK(ourdisplay==NULL, USER,
00181                      "cannot open X-windows; DISPLAY variable set?");
00182 #ifdef STEREO
00183        switch (XSGIQueryStereoMode(ourdisplay, ourroot)) {
00184        case STEREO_TOP:
00185        case STEREO_BOTTOM:
00186               break;
00187        case STEREO_OFF:
00188               error(USER,
00189               "wrong video mode: run \"/usr/gfx/setmon -n STR_TOP\" first");
00190        case X_STEREO_UNSUPPORTED:
00191               error(USER, "stereo mode not supported on this screen");
00192        default:
00193               error(INTERNAL, "unknown stereo mode");
00194        }
00195 #endif
00196                                    /* find a usable visual */
00197        ourvinf = glXChooseVisual(ourdisplay, ourscreen, atlBest);
00198        if (ourvinf == NULL)
00199               ourvinf = glXChooseVisual(ourdisplay, ourscreen, atlOK);
00200        CHECK(ourvinf==NULL, USER, "no suitable visuals available");
00201                                    /* get a context */
00202        gctx = glXCreateContext(ourdisplay, ourvinf, NULL, GL_TRUE);
00203                                    /* set gamma and tone mapping */
00204        if ((ev = XGetDefault(ourdisplay, "radiance", "gamma")) != NULL
00205                      || (ev = getenv("DISPLAY_GAMMA")) != NULL)
00206               gamval = atof(ev);
00207        if ((ev = getenv("DISPLAY_PRIMARIES")) != NULL &&
00208                      sscanf(ev, "%f %f %f %f %f %f %f %f",
00209                             &myprims[RED][CIEX],&myprims[RED][CIEY],
00210                             &myprims[GRN][CIEX],&myprims[GRN][CIEY],
00211                             &myprims[BLU][CIEX],&myprims[BLU][CIEY],
00212                             &myprims[WHT][CIEX],&myprims[WHT][CIEY]) >= 6)
00213               dpri = myprims;
00214        tmGlobal = tmInit(mytmflags(), dpri, gamval);
00215        if (tmGlobal == NULL)
00216               error(SYSTEM, "not enough memory in dev_open");
00217                                    /* open window */
00218        ourwinattr.background_pixel = ourblack;
00219        ourwinattr.border_pixel = ourblack;
00220        ourwinattr.event_mask = ourmask;
00221                                    /* this is stupid */
00222        ourwinattr.colormap = XCreateColormap(ourdisplay, ourroot,
00223                             ourvinf->visual, AllocNone);
00224        gwind = XCreateWindow(ourdisplay, ourroot, 0, 0,
00225               DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH,
00226 #ifdef STEREO
00227               (DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH)/2,
00228 #else
00229               DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH,
00230 #endif
00231               BORWIDTH, ourvinf->depth, InputOutput, ourvinf->visual,
00232               CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &ourwinattr);
00233        CHECK(gwind==0, SYSTEM, "cannot create window");
00234        XStoreName(ourdisplay, gwind, id);
00235                                    /* set window manager hints */
00236        ourxwmhints.flags = InputHint|IconPixmapHint;
00237        ourxwmhints.input = True;
00238        ourxwmhints.icon_pixmap = XCreateBitmapFromData(ourdisplay, gwind,
00239                      (char *)x11icon_bits, x11icon_width, x11icon_height);
00240        XSetWMHints(ourdisplay, gwind, &ourxwmhints);
00241        oursizhints.min_width = MINWIDTH;
00242 #ifdef STEREO
00243        oursizhints.min_height = MINHEIGHT/2;
00244        oursizhints.max_width = DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH;
00245        oursizhints.max_height = (DisplayHeight(ourdisplay,ourscreen) -
00246                                    2*BORWIDTH)/2;
00247        oursizhints.flags = PMinSize|PMaxSize;
00248 #else
00249        oursizhints.min_height = MINHEIGHT;
00250        oursizhints.flags = PMinSize;
00251 #endif
00252        XSetNormalHints(ourdisplay, gwind, &oursizhints);
00253                                    /* set GLX context */
00254        glXMakeCurrent(ourdisplay, gwind, gctx);
00255        glEnable(GL_DEPTH_TEST);
00256        glDepthFunc(GL_LEQUAL);
00257        glClearColor(0, 0, 0, 0);
00258        glFrontFace(GL_CCW);
00259        glDisable(GL_CULL_FACE);
00260        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
00261        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00262        glPixelStorei(GL_PACK_ALIGNMENT, 1);
00263        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00264                                    /* figure out sensible view */
00265        pwidth = (double)DisplayWidthMM(ourdisplay, ourscreen) /
00266                      DisplayWidth(ourdisplay, ourscreen);
00267        pheight = (double)DisplayHeightMM(ourdisplay, ourscreen) /
00268                      DisplayHeight(ourdisplay, ourscreen);
00269 #ifdef STEREO
00270        pheight *= 2.;
00271        setstereobuf(STEREO_BUFFER_LEFT);
00272 #endif
00273        checkglerr("setting rendering parameters");
00274        odev.v = stdview;
00275        odev.v.type = VT_PER;
00276        viewflags = VWSTEADY;              /* view starts static */
00277                                    /* map the window */
00278        XMapWindow(ourdisplay, gwind);
00279        dev_input();                /* sets size and view angles */
00280        if (!odInit(DisplayWidth(ourdisplay,ourscreen) *
00281                      DisplayHeight(ourdisplay,ourscreen) / 3))
00282               error(SYSTEM, "insufficient memory for value storage");
00283        odev.name = id;
00284        odev.firstuse = 1;          /* can't recycle samples */
00285        odev.ifd = ConnectionNumber(ourdisplay);
00286 }
00287 
00288 
00289 extern void
00290 dev_close(void)                    /* close our display and free resources */
00291 {
00292 #ifdef DOBJ
00293        dobj_cleanup();
00294 #endif
00295        freedepth();
00296        gmEndGeom();
00297        gmEndPortal();
00298        odDone();
00299        glXMakeCurrent(ourdisplay, None, NULL);
00300        glXDestroyContext(ourdisplay, gctx);
00301        XDestroyWindow(ourdisplay, gwind);
00302        gwind = 0;
00303        XCloseDisplay(ourdisplay);
00304        ourdisplay = NULL;
00305        tmDone(tmGlobal);
00306        odev.v.type = 0;
00307        odev.hres = odev.vres = 0;
00308        odev.ifd = -1;
00309 }
00310 
00311 
00312 extern void
00313 dev_clear(void)                    /* clear our representation */
00314 {
00315        viewflags |= VWCHANGE;             /* pretend our view has changed */
00316        wipeclean();                /* clean off display and samples */
00317        dev_flush();                /* redraw geometry & get depth */
00318        rayqleft = 0;               /* hold off update */
00319 }
00320 
00321 
00322 extern int
00323 dev_view(                   /* assign new driver view */
00324        register VIEW *nv
00325 )
00326 {
00327 #ifdef STEREO
00328        double d;
00329 #endif
00330        if (nv->type != VT_PER ||          /* check view legality */
00331                      nv->horiz > 160. || nv->vert > 160.) {
00332               error(COMMAND, "illegal view type/angle");
00333               nv->type = odev.v.type;
00334               nv->horiz = odev.v.horiz;
00335               nv->vert = odev.v.vert;
00336               return(0);
00337        }
00338        if (nv != &odev.v) {
00339                                           /* resize window? */
00340               if (!FEQ(nv->horiz,odev.v.horiz) ||
00341                             !FEQ(nv->vert,odev.v.vert)) {
00342                      int    dw = DisplayWidth(ourdisplay,ourscreen);
00343                      int    dh = DisplayHeight(ourdisplay,ourscreen);
00344 
00345                      dw -= 25;     /* for window frame */
00346                      dh -= 50;
00347 #ifdef STEREO
00348                      dh /= 2;
00349 #endif
00350                      odev.hres = 2.*VIEWDIST/pwidth *
00351                                    tan(PI/180./2.*nv->horiz);
00352                      odev.vres = 2.*VIEWDIST/pheight *
00353                                    tan(PI/180./2.*nv->vert);
00354                      if (odev.hres > dw) {
00355                             odev.vres = dw * odev.vres / odev.hres;
00356                             odev.hres = dw;
00357                      }
00358                      if (odev.vres > dh) {
00359                             odev.hres = dh * odev.hres / odev.vres;
00360                             odev.vres = dh;
00361                      }
00362                      XResizeWindow(ourdisplay, gwind, odev.hres, odev.vres);
00363                      dev_input();  /* get resize event */
00364               }
00365               odev.v = *nv; /* setview() already called */
00366               viewflags |= VWCHANGE;
00367        }
00368 #ifdef STEREO
00369        vwright = *nv;
00370        d = eyesepdist / sqrt(nv->hn2);
00371        VSUM(vwright.vp, nv->vp, nv->hvec, d);
00372        /* setview(&vwright);       -- Unnecessary */
00373 #endif
00374        wipeclean();
00375        return(1);
00376 }
00377 
00378 
00379 extern void
00380 dev_section(         /* add octree for geometry rendering */
00381        char   *gfn,
00382        char   *pfn
00383 )
00384 {
00385        if (gfn == NULL) {
00386               gmEndGeom();
00387               gmEndPortal();
00388               wipeclean();         /* new geometry, so redraw it */
00389               return;
00390        }
00391        if (access(gfn, R_OK) == 0)
00392               gmNewGeom(gfn);
00393 #ifdef DEBUG
00394        else {
00395               sprintf(errmsg, "cannot load octree \"%s\"", gfn);
00396               error(WARNING, errmsg);
00397        }
00398 #endif
00399        if (pfn != NULL)
00400               gmNewPortal(pfn);
00401 }
00402 
00403 
00404 extern void
00405 dev_auxcom(          /* process an auxiliary command */
00406        char   *cmd,
00407        char   *args
00408 )
00409 {
00410 #ifdef DOBJ
00411        int    vischange;
00412 
00413        if ((vischange = dobj_command(cmd, args)) >= 0) {
00414               if (vischange) {
00415                      imm_mode = beam_sync(1) > 0;
00416                      dev_clear();
00417               }
00418               return;
00419        }
00420 #endif
00421        sprintf(errmsg, "%s: unknown command", cmd);
00422        error(COMMAND, errmsg);
00423 }
00424 
00425 
00426 extern VIEW *
00427 dev_auxview(         /* return nth auxiliary view */
00428        int    n,
00429        int    hvres[2]
00430 )
00431 {
00432        hvres[0] = odev.hres; hvres[1] = odev.vres;
00433        if (n == 0)
00434               return(&odev.v);
00435 #ifdef STEREO
00436        if (n == 1)
00437               return(&vwright);
00438 #endif
00439        return(NULL);
00440 }
00441 
00442 
00443 extern int
00444 dev_input(void)                    /* get X11 input */
00445 {
00446        inpresflags = 0;
00447 
00448        do
00449               getevent();
00450 
00451        while (XPending(ourdisplay) > 0);
00452 
00453        odev.inpready = 0;
00454 
00455        return(inpresflags);
00456 }
00457 
00458 
00459 extern void
00460 dev_value(           /* add a pixel value to our texture */
00461        COLR   c,
00462        FVECT  d,
00463        FVECT  p
00464 )
00465 {
00466 #ifdef DOBJ
00467        if (dobj_lightsamp != NULL) {      /* in light source sampling */
00468               (*dobj_lightsamp)(c, d, p);
00469               return;
00470        }
00471 #endif
00472        odSample(c, d, p);          /* add to display representation */
00473        if (!--rayqleft)
00474               dev_flush();         /* flush output */
00475 }
00476 
00477 
00478 extern int
00479 dev_flush(void)                    /* flush output as appropriate */
00480 {
00481        int    ndrawn;
00482 
00483        if ((viewflags&(VWMAPPED|VWPERSP)) == (VWMAPPED|VWPERSP)) {
00484 #ifdef STEREO
00485               pushright();                /* draw right eye */
00486               ndrawn = gmDrawGeom();
00487 #ifdef DOBJ
00488               ndrawn += dobj_render();
00489 #endif
00490               checkglerr("rendering right eye");
00491               popright();                 /* draw left eye */
00492 #endif
00493               ndrawn = gmDrawGeom();
00494 #ifdef DOBJ
00495               ndrawn += dobj_render();
00496 #endif
00497               glXSwapBuffers(ourdisplay, gwind);
00498               checkglerr("rendering base view");
00499        }
00500        if ((viewflags&(VWMAPPED|VWSTEADY|VWPERSP|VWORTHO)) ==
00501                      (VWMAPPED|VWSTEADY|VWPERSP)) {
00502                                    /* first time after steady */
00503               if (ndrawn)
00504                      xferdepth();  /* transfer and clear depth */
00505               setglortho();        /* set orthographic view */
00506 
00507        }
00508        if ((viewflags&(VWMAPPED|VWSTEADY|VWPERSP|VWORTHO)) ==
00509                      (VWMAPPED|VWSTEADY|VWORTHO)) {
00510                                    /* else update cones */
00511 #ifdef STEREO
00512               pushright();
00513               odUpdate(1);         /* draw right eye */
00514               popright();
00515 #endif
00516               odUpdate(0);         /* draw left eye */
00517               glFlush();           /* flush OpenGL */
00518        }
00519        rayqleft = RAYQLEN;
00520                                    /* flush X11 and return # pending */
00521        return(odev.inpready = XPending(ourdisplay));
00522 }
00523 
00524 
00525 static void
00526 checkglerr(          /* check for GL or GLU error */
00527        char   *where
00528 )
00529 {
00530        register GLenum      errcode;
00531 
00532        while ((errcode = glGetError()) != GL_NO_ERROR) {
00533               sprintf(errmsg, "OpenGL error %s: %s",
00534                             where, gluErrorString(errcode));
00535               error(WARNING, errmsg);
00536        }
00537 }
00538 
00539 
00540 static void
00541 xferdepth(void)                    /* load and clear depth buffer */
00542 {
00543        register GLfloat     *dbp;
00544        register GLubyte     *pbuf;
00545 
00546        if (depthbuffer == NULL) {  /* allocate private depth buffer */
00547 #ifdef STEREO
00548               depthright = (GLfloat *)malloc(
00549                             odev.hres*odev.vres*sizeof(GLfloat));
00550 #endif
00551               depthbuffer = (GLfloat *)malloc(
00552                             odev.hres*odev.vres*sizeof(GLfloat));
00553               CHECK(depthbuffer==NULL, SYSTEM, "out of memory in xferdepth");
00554        }
00555                             /* allocate alpha buffer for portals */
00556        if (gmPortals)
00557               pbuf = (GLubyte *)malloc(odev.hres*odev.vres*sizeof(GLubyte));
00558        else
00559               pbuf = NULL;
00560 #ifdef STEREO
00561        pushright();
00562        glReadPixels(0, 0, odev.hres, odev.vres,
00563                      GL_DEPTH_COMPONENT, GL_FLOAT, depthright);
00564        if (pbuf != NULL) {
00565               glClear(GL_COLOR_BUFFER_BIT);
00566               gmDrawPortals(0xff, -1, -1, -1);
00567               glReadPixels(0, 0, odev.hres, odev.vres,
00568                             GL_RED, GL_UNSIGNED_BYTE, pbuf);
00569        }
00570        for (dbp = depthright + odev.hres*odev.vres; dbp-- > depthright; )
00571               if (pbuf != NULL && pbuf[dbp-depthright]&0x40)
00572                      *dbp = FHUGE;
00573               else
00574                      *dbp = mapdepth(*dbp);
00575        glClear(GL_DEPTH_BUFFER_BIT);
00576        odDepthMap(1, depthright);
00577        popright();
00578 #endif
00579                             /* read back depth buffer */
00580        glReadPixels(0, 0, odev.hres, odev.vres,
00581                      GL_DEPTH_COMPONENT, GL_FLOAT, depthbuffer);
00582        if (pbuf != NULL) {
00583               glClear(GL_COLOR_BUFFER_BIT);             /* find portals */
00584               gmDrawPortals(0xff, -1, -1, -1);
00585               glReadPixels(0, 0, odev.hres, odev.vres,
00586                             GL_RED, GL_UNSIGNED_BYTE, pbuf);
00587 #ifdef DEBUG
00588               glXSwapBuffers(ourdisplay, gwind);
00589 #endif
00590        }
00591        for (dbp = depthbuffer + odev.hres*odev.vres; dbp-- > depthbuffer; )
00592               if (pbuf != NULL && pbuf[dbp-depthbuffer]&0x40)
00593                      *dbp = FHUGE;
00594               else
00595                      *dbp = mapdepth(*dbp);
00596        glClear(GL_DEPTH_BUFFER_BIT);             /* clear system depth buffer */
00597        odDepthMap(0, depthbuffer);        /* transfer depth data */
00598        if (pbuf != NULL)
00599               free((void *)pbuf);         /* free our portal buffer */
00600 }
00601 
00602 
00603 static void
00604 freedepth(void)                           /* free recorded depth buffer */
00605 {
00606        if (depthbuffer == NULL)
00607               return;
00608 #ifdef STEREO
00609        odDepthMap(1, NULL);
00610        free((void *)depthright);
00611        depthright = NULL;
00612 #endif
00613        odDepthMap(0, NULL);
00614        free((void *)depthbuffer);
00615        depthbuffer = NULL;
00616 }
00617 
00618 
00619 static double
00620 getdistance(  /* distance from fore plane along view ray */
00621        int    dx,
00622        int    dy,
00623        FVECT  direc
00624 )
00625 {
00626        GLfloat       gldepth;
00627        double dist;
00628 
00629        if ((dx<0) | (dx>=odev.hres) | (dy<0) | (dy>=odev.vres))
00630               return(FHUGE);
00631        if (depthbuffer != NULL)
00632               dist = depthbuffer[dy*odev.hres + dx];
00633        else {
00634               glReadPixels(dx,dy, 1,1, GL_DEPTH_COMPONENT,
00635                             GL_FLOAT, &gldepth);
00636               if (gldepth <= FTINY)
00637                      return (FHUGE);      /* call failed */
00638               dist = mapdepth(gldepth);
00639        }
00640        if (dist >= .99*FHUGE)
00641               return(FHUGE);
00642        return((dist-odev.v.vfore)/DOT(direc,odev.v.vdir));
00643 }
00644 
00645 
00646 #ifdef STEREO
00647 static void
00648 pushright(void)                    /* push on right view & buffer */
00649 {
00650        double d;
00651 
00652        setstereobuf(STEREO_BUFFER_RIGHT);
00653        if (viewflags & VWPERSP) {
00654               glMatrixMode(GL_MODELVIEW);
00655               glPushMatrix();
00656               d = -eyesepdist / sqrt(odev.v.hn2);
00657               glTranslated(d*odev.v.hvec[0], d*odev.v.hvec[1],
00658                             d*odev.v.hvec[2]);
00659               checkglerr("setting right view");
00660        }
00661 }
00662 
00663 
00664 static void
00665 popright(void)                     /* pop off right view & buffer */
00666 {
00667        if (viewflags & VWPERSP) {
00668               glMatrixMode(GL_MODELVIEW);
00669               glPopMatrix();
00670        }
00671        setstereobuf(STEREO_BUFFER_LEFT);
00672 }
00673 #endif
00674 
00675 
00676 static int
00677 mytmflags(void)                    /* figure out tone mapping flags */
00678 {
00679        extern char   *progname;
00680        register char *cp, *tail;
00681                                    /* find basic name */
00682        for (cp = tail = progname; *cp; cp++)
00683               if (*cp == '/')
00684                      tail = cp+1;
00685        for (cp = tail; *cp && *cp != '.'; cp++)
00686               ;
00687 #ifdef DEBUG
00688        if (cp > tail && cp[-1] == 'h')
00689               return(TM_F_HUMAN);
00690        else
00691               return(TM_F_CAMERA);
00692 #else
00693        if (cp > tail && cp[-1] == 'h')
00694               return(TM_F_HUMAN|TM_F_NOSTDERR);
00695        else
00696               return(TM_F_CAMERA|TM_F_NOSTDERR);
00697 #endif
00698 }
00699 
00700 
00701 static void
00702 getevent(void)                     /* get next event */
00703 {
00704        XNextEvent(ourdisplay, levptr(XEvent));
00705        switch (levptr(XEvent)->type) {
00706        case ConfigureNotify:
00707               resizewindow(levptr(XConfigureEvent));
00708               break;
00709        case UnmapNotify:
00710               viewflags &= ~VWMAPPED;
00711               break;
00712        case MapNotify:
00713               odRemap(0);
00714               viewflags |= VWMAPPED;
00715               break;
00716        case Expose:
00717               fixwindow(levptr(XExposeEvent));
00718               break;
00719        case KeyPress:
00720               getkey(levptr(XKeyPressedEvent));
00721               break;
00722        case ButtonPress:
00723               if (FRAMESTATE(levptr(XButtonPressedEvent)->state))
00724                      getframe(levptr(XButtonPressedEvent));
00725               else
00726                      getmove(levptr(XButtonPressedEvent));
00727               break;
00728        }
00729 }
00730 
00731 
00732 static void
00733 draw3dline(                 /* draw 3d line in world coordinates */
00734        register FVECT       wp[2]
00735 )
00736 {
00737        glVertex3d(wp[0][0], wp[0][1], wp[0][2]);
00738        glVertex3d(wp[1][0], wp[1][1], wp[1][2]);
00739 }
00740 
00741 
00742 static void
00743 draw_grids(          /* draw holodeck section grids */
00744        int    fore
00745 )
00746 {
00747        glPushAttrib(GL_LIGHTING_BIT|GL_ENABLE_BIT);
00748        glDisable(GL_LIGHTING);
00749        if (fore)
00750               glColor3ub(4, 250, 250);
00751        else
00752               glColor3ub(0, 0, 0);
00753        glBegin(GL_LINES);          /* draw each grid line */
00754        gridlines(draw3dline);
00755        glEnd();
00756        checkglerr("drawing grid lines");
00757        glPopAttrib();
00758 }
00759 
00760 
00761 static int
00762 moveview(     /* move our view */
00763        int    dx,
00764        int    dy,
00765        int    mov,
00766        int    orb
00767 )
00768 {
00769        VIEW   nv;
00770        FVECT  odir, v1, wip;
00771        double d;
00772 #ifdef DOBJ
00773        double d1;
00774 #endif
00775                             /* start with old view */
00776        nv = odev.v;
00777                             /* orient our motion */
00778        if (viewray(v1, odir, &odev.v,
00779                      (dx+.5)/odev.hres, (dy+.5)/odev.vres) < -FTINY)
00780               return(0);           /* outside view */
00781        if (mov | orb) {     /* moving relative to geometry */
00782               d = getdistance(dx, dy, odir);     /* distance from front plane */
00783 #ifdef DOBJ
00784               d1 = dobj_trace(NULL, v1, odir);
00785               if (d1 < d)
00786                      d = d1;
00787 #endif
00788               if (d >= .99*FHUGE)
00789                      d = 0.5*(dev_zmax+dev_zmin);       /* just guess */
00790               VSUM(wip, v1, odir, d);
00791               VSUB(odir, wip, odev.v.vp);
00792        } else               /* panning with constant viewpoint */
00793               VCOPY(nv.vdir, odir);
00794        if (orb && mov) {           /* orbit left/right */
00795               spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov);
00796               VSUM(nv.vp, wip, odir, -1.);
00797               spinvector(nv.vdir, nv.vdir, nv.vup, d);
00798        } else if (orb) {           /* orbit up/down */
00799               fcross(v1, odir, nv.vup);
00800               if (normalize(v1) == 0.)
00801                      return(0);
00802               spinvector(odir, odir, v1, d=MOVDEG*PI/180.*orb);
00803               VSUM(nv.vp, wip, odir, -1.);
00804               spinvector(nv.vdir, nv.vdir, v1, d);
00805        } else if (mov) {           /* move forward/backward */
00806               d = MOVPCT/100. * mov;
00807               VSUM(nv.vp, nv.vp, odir, d);
00808        }
00809        if (!mov ^ !orb && viewflags&VWHEADLOCK) {       /* restore height */
00810               VSUM(v1, odev.v.vp, nv.vp, -1.);
00811               d = DOT(v1, nv.vup);
00812               VSUM(nv.vp, nv.vp, odev.v.vup, d);
00813        }
00814        if (setview(&nv) != NULL)
00815               return(0);    /* illegal view */
00816        dev_view(&nv);
00817        inpresflags |= DFL(DC_SETVIEW);
00818        return(1);
00819 }
00820 
00821 
00822 static void
00823 getframe(                          /* get focus frame */
00824        XButtonPressedEvent  *ebut
00825 )
00826 {
00827        int    startx = ebut->x, starty = ebut->y;
00828        int    endx, endy, midx, midy;
00829        FVECT  odir, v1;
00830        double d;
00831 #ifdef DOBJ
00832        double d1;
00833 #endif
00834                                           /* get mouse drag */
00835        XMaskEvent(ourdisplay, ButtonReleaseMask, levptr(XEvent));
00836        endx = levptr(XButtonReleasedEvent)->x;
00837        endy = levptr(XButtonReleasedEvent)->y;
00838        midx = (startx + endx) >> 1;
00839        midy = (starty + endy) >> 1;
00840                                           /* set focus distance */
00841        if (viewray(v1, odir, &odev.v,
00842                      (midx+.5)/odev.hres, (midy+.5)/odev.vres) < -FTINY)
00843               return;
00844        d = getdistance(midx, midy, odir); /* distance from front plane */
00845 #ifdef DOBJ
00846        d1 = dobj_trace(NULL, v1, odir);
00847        if (d1 < d)
00848               d = d1;
00849 #endif
00850        if (d < .99*FHUGE)
00851               odev.v.vdist = d + sqrt(dist2(v1, odev.v.vp));
00852                                           /* set frame for rendering */
00853        if ((endx == startx) | (endy == starty))
00854               return;
00855        if (endx < startx) {register int c = endx; endx = startx; startx = c;}
00856        if (endy < starty) {register int c = endy; endy = starty; starty = c;}
00857        sprintf(odev_args, "%.3f %.3f %.3f %.3f",
00858                      (startx+.5)/odev.hres, 1.-(endy+.5)/odev.vres,
00859                      (endx+.5)/odev.hres, 1.-(starty+.5)/odev.vres);
00860        inpresflags |= DFL(DC_FOCUS);
00861 }
00862 
00863 
00864 static void
00865 waitabit(void)                            /* pause a moment */
00866 {
00867        struct timespec      ts;
00868        ts.tv_sec = 0;
00869        ts.tv_nsec = 100000000L;
00870        nanosleep(&ts, NULL);
00871 }
00872 
00873 
00874 static void
00875 getmove(                           /* get view change */
00876        XButtonPressedEvent  *ebut
00877 )
00878 {
00879        int    movdir = MOVDIR(ebut->button);
00880        int    movorb = MOVORB(ebut->state);
00881        int    ndrawn;
00882        Window rootw, childw;
00883        int    rootx, rooty, wx, wy;
00884        unsigned int  statemask;
00885 
00886        XNoOp(ourdisplay);          /* makes sure we're not idle */
00887 
00888        viewflags &= ~VWSTEADY;            /* flag moving view */
00889        setglpersp();               /* start us off in perspective */
00890        while (!XCheckMaskEvent(ourdisplay,
00891                      ButtonReleaseMask, levptr(XEvent))) {
00892                                    /* pause so as not to move too fast */
00893               waitabit();
00894                                    /* get cursor position */
00895               if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw,
00896                             &rootx, &rooty, &wx, &wy, &statemask))
00897                      break;        /* on another screen */
00898                                    /* compute view motion */
00899               if (!moveview(wx, odev.vres-1-wy, movdir, movorb)) {
00900                      sleep(1);
00901                      continue;     /* cursor in bad place */
00902               }
00903               draw_grids(1);              /* redraw grid */
00904 #ifdef STEREO
00905               pushright();
00906               draw_grids(1);
00907               ndrawn = gmDrawGeom();
00908 #ifdef DOBJ
00909               ndrawn += dobj_render();
00910 #endif
00911               popright();
00912 #endif
00913                                    /* redraw octrees */
00914               ndrawn = gmDrawGeom();
00915 #ifdef DOBJ
00916               ndrawn += dobj_render();    /* redraw objects */
00917 #endif
00918               glXSwapBuffers(ourdisplay, gwind);
00919               if (!ndrawn)
00920                      sleep(1);     /* for reasonable interaction */
00921        }
00922        if (!(inpresflags & DFL(DC_SETVIEW))) {   /* do final motion */
00923               movdir = MOVDIR(levptr(XButtonReleasedEvent)->button);
00924               wx = levptr(XButtonReleasedEvent)->x;
00925               wy = levptr(XButtonReleasedEvent)->y;
00926               moveview(wx, odev.vres-1-wy, movdir, movorb);
00927        }
00928        viewflags |= VWSTEADY;             /* done goofing around */
00929 }
00930 
00931 
00932 static void
00933 setglpersp(void)                   /* set perspective view in GL */
00934 {
00935        double d, xmin, xmax, ymin, ymax;
00936        GLfloat       vec[4];
00937        double depthlim[2];
00938                                    /* set depth limits */
00939        gmDepthLimit(depthlim, odev.v.vp, odev.v.vdir);
00940        if (depthlim[0] >= depthlim[1]) {
00941               dev_zmin = 1.;
00942               dev_zmax = 100.;
00943        } else {
00944               dev_zmin = 0.5*depthlim[0];
00945               dev_zmax = 1.25*depthlim[1];
00946               if (dev_zmin > dev_zmax/5.)
00947                      dev_zmin = dev_zmax/5.;
00948        }
00949        if (odev.v.vfore > FTINY)
00950               dev_zmin = odev.v.vfore;
00951        if (odev.v.vaft > FTINY)
00952               dev_zmax = odev.v.vaft;
00953        if (dev_zmin*500. < dev_zmax)
00954               dev_zmin = dev_zmax/500.;
00955        setzrat();
00956        xmax = dev_zmin * tan(PI/180./2. * odev.v.horiz);
00957        xmin = -xmax;
00958        d = odev.v.hoff * (xmax - xmin);
00959        xmin += d; xmax += d;
00960        ymax = dev_zmin * tan(PI/180./2. * odev.v.vert);
00961        ymin = -ymax;
00962        d = odev.v.voff * (ymax - ymin);
00963        ymin += d; ymax += d;
00964                                    /* set view matrix */
00965        glMatrixMode(GL_PROJECTION);
00966        glLoadIdentity();
00967        glFrustum(xmin, xmax, ymin, ymax, dev_zmin, dev_zmax);
00968        gluLookAt(odev.v.vp[0], odev.v.vp[1], odev.v.vp[2],
00969               odev.v.vp[0] + odev.v.vdir[0],
00970               odev.v.vp[1] + odev.v.vdir[1],
00971               odev.v.vp[2] + odev.v.vdir[2],
00972               odev.v.vup[0], odev.v.vup[1], odev.v.vup[2]);
00973        checkglerr("setting perspective view");
00974        vec[0] = vec[1] = vec[2] = 0.; vec[3] = 1.;
00975        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, vec);
00976        vec[0] = -odev.v.vdir[0];
00977        vec[1] = -odev.v.vdir[1];
00978        vec[2] = -odev.v.vdir[2];
00979        vec[3] = 0.;
00980        glLightfv(GL_LIGHT0, GL_POSITION, vec);
00981        vec[0] = vec[1] = vec[2] = .7; vec[3] = 1.;
00982        glLightfv(GL_LIGHT0, GL_SPECULAR, vec);
00983        glLightfv(GL_LIGHT0, GL_DIFFUSE, vec);
00984        vec[0] = vec[1] = vec[2] = .3; vec[3] = 1.;
00985        glLightfv(GL_LIGHT0, GL_AMBIENT, vec);
00986        glEnable(GL_LIGHT0);
00987        glEnable(GL_LIGHTING);             /* light our GL objects */
00988        glShadeModel(GL_SMOOTH);
00989        viewflags &= ~VWORTHO;
00990        viewflags |= VWPERSP;
00991 }
00992 
00993 
00994 static void
00995 setglortho(void)                   /* set up orthographic view for cone drawing */
00996 {
00997        glDrawBuffer(GL_FRONT);            /* use single-buffer mode */
00998                                    /* set view matrix */
00999        glMatrixMode(GL_PROJECTION);
01000        glLoadIdentity();
01001        glOrtho(0., (double)odev.hres, 0., (double)odev.vres,
01002                      0.001*OMAXDEPTH, 1.001*(-OMAXDEPTH));
01003        checkglerr("setting orthographic view");
01004        glDisable(GL_LIGHTING);            /* cones are constant color */
01005        glShadeModel(GL_FLAT);
01006        viewflags &= ~VWPERSP;
01007        viewflags |= VWORTHO;
01008 }
01009 
01010 
01011 static void
01012 wipeclean(void)                    /* prepare for redraw */
01013 {
01014        glDrawBuffer(GL_BACK);             /* use double-buffer mode */
01015        glReadBuffer(GL_BACK);
01016                                    /* clear buffers */
01017 #ifdef STEREO
01018        setstereobuf(STEREO_BUFFER_RIGHT);
01019        glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
01020        setstereobuf(STEREO_BUFFER_LEFT);
01021 #endif
01022        glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
01023        freedepth();
01024        if ((viewflags&(VWCHANGE|VWSTEADY)) ==
01025                      (VWCHANGE|VWSTEADY)) {      /* clear samples if new */
01026               odClean();
01027               viewflags &= ~VWCHANGE;            /* change noted */
01028        } else if (viewflags & VWSTEADY)
01029               odRedrawAll();
01030        setglpersp();               /* reset view & clipping planes */
01031 }
01032 
01033 
01034 static void
01035 getkey(                            /* get input key */
01036        register XKeyPressedEvent  *ekey
01037 )
01038 {
01039        Window rootw, childw;
01040        int    rootx, rooty, wx, wy;
01041        unsigned int  statemask;
01042        int  n;
01043        char   buf[8];
01044 
01045        n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL);
01046        if (n != 1)
01047               return;
01048        switch (buf[0]) {
01049        case 'h':                   /* turn on height motion lock */
01050               viewflags |= VWHEADLOCK;
01051               return;
01052        case 'H':                   /* turn off height motion lock */
01053               viewflags &= ~VWHEADLOCK;
01054               return;
01055        case 'l':                   /* retrieve last view */
01056               inpresflags |= DFL(DC_LASTVIEW);
01057               return;
01058        case 'p':                   /* pause computation */
01059               inpresflags |= DFL(DC_PAUSE);
01060               return;
01061        case 'v':                   /* spit out view */
01062               inpresflags |= DFL(DC_GETVIEW);
01063               return;
01064        case 'f':                   /* frame view position */
01065               if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw,
01066                             &rootx, &rooty, &wx, &wy, &statemask))
01067                      return;              /* on another screen */
01068               sprintf(odev_args, "%.4f %.4f", (wx+.5)/odev.hres,
01069                             1.-(wy+.5)/odev.vres);
01070               inpresflags |= DFL(DC_FOCUS);
01071               return;
01072        case 'F':                   /* unfocus */
01073               odev_args[0] = '\0';
01074               inpresflags |= DFL(DC_FOCUS);
01075               return;
01076        case '\n':
01077        case '\r':                  /* resume computation */
01078               inpresflags |= DFL(DC_RESUME);
01079               return;
01080        case CTRL('R'):                    /* redraw screen */
01081               odRemap(0);                 /* new tone mapping */
01082               glClear(GL_DEPTH_BUFFER_BIT);
01083 #ifdef STEREO
01084               setstereobuf(STEREO_BUFFER_RIGHT);
01085               glClear(GL_DEPTH_BUFFER_BIT);
01086               setstereobuf(STEREO_BUFFER_LEFT);
01087 #endif
01088               return;
01089        case CTRL('L'):                    /* refresh from server */
01090               if (inpresflags & DFL(DC_REDRAW))
01091                      return;                     /* already called */
01092               XRaiseWindow(ourdisplay, gwind);
01093               XFlush(ourdisplay);         /* raise up window */
01094               sleep(1);                   /* wait for restacking */
01095               dev_clear();                /* clear buffer and samples */
01096               odRemap(1);                 /* start fresh histogram */
01097               inpresflags |= DFL(DC_REDRAW);     /* resend values from server */
01098               return;
01099        case 'K':                   /* kill rtrace process(es) */
01100               inpresflags |= DFL(DC_KILL);
01101               break;
01102        case 'R':                   /* restart rtrace */
01103               inpresflags |= DFL(DC_RESTART);
01104               break;
01105        case 'C':                   /* clobber holodeck */
01106               inpresflags |= DFL(DC_CLOBBER);
01107               break;
01108        case 'q':                   /* quit the program */
01109               inpresflags |= DFL(DC_QUIT);
01110               return;
01111        default:
01112               XBell(ourdisplay, 0);
01113               return;
01114        }
01115 }
01116 
01117 
01118 static void
01119 fixwindow(                         /* repair damage to window */
01120        register XExposeEvent  *eexp
01121 )
01122 {
01123        int    xmin, ymin, xmax, ymax;
01124 
01125        if ((odev.hres == 0) | (odev.vres == 0)) {       /* first exposure */
01126               resizewindow((XConfigureEvent *)eexp);
01127               return;
01128        }
01129        xmin = eexp->x; xmax = eexp->x + eexp->width;
01130        ymin = odev.vres - eexp->y - eexp->height; ymax = odev.vres - eexp->y;
01131 
01132        if (xmin <= 0 && xmax >= odev.hres-1 &&
01133                      ymin <= 0 && ymax >= odev.vres) {
01134               DCHECK(eexp->count, WARNING, "multiple clear in fixwindow");
01135               wipeclean();                /* make sure we're go */
01136               return;
01137        }
01138                                           /* clear portion of depth */
01139        glPushAttrib(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
01140        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
01141        glDepthFunc(GL_ALWAYS);
01142        glBegin(GL_POLYGON);
01143        glVertex3i(xmin, ymin, OMAXDEPTH);
01144        glVertex3i(xmax, ymin, OMAXDEPTH);
01145        glVertex3i(xmax, ymax, OMAXDEPTH);
01146        glVertex3i(xmin, ymax, OMAXDEPTH);
01147        glEnd();
01148 #ifdef STEREO
01149        setstereobuf(STEREO_BUFFER_RIGHT);
01150        glBegin(GL_POLYGON);
01151        glVertex3i(xmin, ymin, OMAXDEPTH);
01152        glVertex3i(xmax, ymin, OMAXDEPTH);
01153        glVertex3i(xmax, ymax, OMAXDEPTH);
01154        glVertex3i(xmin, ymax, OMAXDEPTH);
01155        glEnd();
01156        odRedraw(1, xmin, ymin, xmax, ymax);
01157        setstereobuf(STEREO_BUFFER_LEFT);
01158 #endif
01159        glPopAttrib();
01160        odRedraw(0, xmin, ymin, xmax, ymax);
01161 }
01162 
01163 
01164 static void
01165 resizewindow(               /* resize window */
01166        register XConfigureEvent  *ersz
01167 )
01168 {
01169        glViewport(0, 0, ersz->width, ersz->height);
01170 
01171        if (ersz->width == odev.hres && ersz->height == odev.vres)
01172               return;
01173 
01174        odev.hres = ersz->width;
01175        odev.vres = ersz->height;
01176 
01177        odev.v.horiz = 2.*180./PI * atan(0.5/VIEWDIST*pwidth*odev.hres);
01178        odev.v.vert = 2.*180./PI * atan(0.5/VIEWDIST*pheight*odev.vres);
01179 
01180        inpresflags |= DFL(DC_SETVIEW);
01181        viewflags |= VWCHANGE;
01182 }