Back to index

radiance  4R0+20100331
glrad.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: glrad.c,v 3.20 2005/07/24 19:53:08 greg Exp $";
00003 #endif
00004 /*
00005  * Program to display Radiance scene using OpenGL.
00006  */
00007 
00008 #include <sys/types.h>
00009 #include <GL/glx.h>
00010 #ifndef NOSTEREO
00011 #include <X11/extensions/SGIStereo.h>
00012 #endif
00013 #include <ctype.h>
00014 #include <string.h>
00015 #include <time.h>
00016 
00017 #include "radogl.h"
00018 #include "view.h"
00019 #include "paths.h"
00020 #include "glradicon.h"
00021 #include "rtprocess.h"
00022 
00023 #ifndef MAXVIEW
00024 #define MAXVIEW             63            /* maximum number of standard views */
00025 #endif
00026 #ifndef MAXSCENE
00027 #define MAXSCENE     127           /* maximum number of scene files */
00028 #endif
00029 
00030 #define ZOOMPCT             9             /* percent to zoom at a time */
00031 
00032 #define MOVPCT              7             /* percent distance to move /frame */
00033 #define MOVDIR(b)    ((b)==Button1 ? 1 : (b)==Button2 ? 0 : -1)
00034 #define MOVDEG              (-5)          /* degrees to orbit CW/down /frame */
00035 #define MOVORB(s)    ((s)&ShiftMask ? 1 : (s)&ControlMask ? -1 : 0)
00036 
00037 #define BORWIDTH     5             /* border width */
00038 
00039 #define  ourscreen   DefaultScreen(ourdisplay)
00040 #define  ourroot     RootWindow(ourdisplay,ourscreen)
00041 #define  ourmask     (StructureNotifyMask|ExposureMask|KeyPressMask|\
00042                      ButtonPressMask|ButtonReleaseMask)
00043 
00044 #define  levptr(etype)      ((etype *)&currentevent)
00045 
00046 XEvent  currentevent;                     /* current event */
00047 
00048 int  mapped = 0;                   /* window is mapped? */
00049 unsigned long  ourblack=0, ourwhite=~0;
00050 
00051 Display  *ourdisplay = NULL;              /* our display */
00052 XVisualInfo  *ourvinf;                    /* our visual information */
00053 Window  gwind = 0;                 /* our graphics window */
00054 int    hres, vres;                 /* rendering window dimensions */
00055 int    maxhres, maxvres;           /* maximum given dimensions */
00056 GLXContext    gctx;                /* our GLX context */
00057 
00058 double pwidth, pheight;            /* pixel dimensions (mm) */
00059 
00060 int    headlocked = 0;                    /* lock vertical motion */
00061 
00062 struct {
00063        char   *nam;                       /* view name (NULL if none) */
00064        VIEW   *v;                         /* parameters (NULL term.) */
00065 } vwl[MAXVIEW+1];                  /* our list of views */
00066 
00067 int    currentview = 0;            /* current view number */
00068 VIEW   thisview = STDVIEW;         /* displayed view */
00069 double eyedist = 1;                /* interocular distance */
00070 VIEW   lastview;                   /* last recorded view */
00071 
00072 char   *progname;                  /* global argv[0] */
00073 char   *radfile;                   /* rad input file */
00074 char   *scene[MAXSCENE+1];         /* material and scene file list */
00075 int    nscenef = 0;                /* number of scene files */
00076 char   *octree;                    /* octree name (NULL if unnec.) */
00077 
00078 SUBPROC       rtpd;                /* rtrace process descriptors */
00079 
00080 int    silent = 0;                 /* run rad silently? */
00081 int    backvis = 1;                /* back faces visible? */
00082 int    stereo = 0;                 /* do stereo? */
00083 
00084 #ifdef NOSTEREO
00085 #define setstereobuf(bid)   
00086 #else
00087 #define setstereobuf(bid)   (glXWaitGL(), \
00088                             XSGISetStereoBuffer(ourdisplay, gwind, bid), \
00089                             glXWaitX())
00090 #endif
00091 
00092 int    displist;                   /* our scene display list */
00093 
00094 int    no_render = 0;                     /* don't rerender */
00095 
00096 extern int    nowarn;                     /* turn warnings off? */
00097 
00098 static void startrtrace(char       *octname);
00099 static void runrad(int      ac, char      **av);
00100 static int findvw(register char    *nm);
00101 static int varmatch(register char  *s, register char    *vn);
00102 static char * scan4var(char *buf, int     buflen, char  *vname, FILE  *fp);
00103 static void dev_open(char  *id);
00104 static void dev_close(void);
00105 static int dev_view(register VIEW  *nv);
00106 static int dev_input(int    nsecs);
00107 static void render(void);
00108 static int moveview(int     dx, int       dy, int       mov, int      orb);
00109 static void waitabit(void);
00110 static void getmove(XButtonPressedEvent   *ebut);
00111 static int getintersect(FVECT      wp, FVECT     org, FVECT    dir, double   md);
00112 static void setglpersp(register VIEW      *vp);
00113 static int getkey(register XKeyPressedEvent  *ekey);
00114 static void zoomview(int    pct, int      dx, int       dy);
00115 static void gotoview(int    vwnum);
00116 static void appendview(char *nm, VIEW     *vp);
00117 static void copylastv(char  *cause);
00118 static void fixwindow(register XExposeEvent  *eexp);
00119 static void resizewindow(register XConfigureEvent  *ersz);
00120 
00121 
00122 int
00123 main(
00124        int    argc,
00125        char   *argv[]
00126 )
00127 {
00128        char   *viewsel = NULL;
00129        long   vwintvl = 0;
00130        int    i;
00131 
00132        progname = argv[0];
00133        for (i = 1; i < argc && argv[i][0] == '-'; i++)
00134               switch (argv[i][1]) {
00135               case 'v':
00136                      viewsel = argv[++i];
00137                      break;
00138               case 'w':
00139                      nowarn = !nowarn;
00140                      break;
00141               case 's':
00142                      silent = !silent;
00143                      break;
00144               case 'S':
00145                      stereo = !stereo;
00146                      break;
00147               case 'c':
00148                      vwintvl = atoi(argv[++i]);
00149                      break;
00150               case 'b':
00151                      backvis = !backvis;
00152                      break;
00153               default:
00154                      goto userr;
00155               }
00156        if (i >= argc)
00157               goto userr;
00158 #ifdef NOSTEREO
00159        if (stereo)
00160               error(INTERNAL, "stereo not supported in this version");
00161 #endif
00162                                    /* run rad and get views */
00163        runrad(argc-i, argv+i);
00164                                    /* check view */
00165        if (viewsel != NULL) {
00166               if (viewsel[0] == '-') {
00167                      char   *ve = viewsel;
00168                      if (!sscanview(&thisview, viewsel) ||
00169                                    (ve = setview(&thisview)) != NULL) {
00170                             fprintf(stderr, "%s: bad view: %s\n",
00171                                           progname, ve);
00172                             quit(1);
00173                      }
00174                      currentview = -1;
00175               } else if ((currentview = findvw(viewsel)) < 0) {
00176                      fprintf(stderr, "%s: no such view: %s\n",
00177                                           progname, viewsel);
00178                      quit(1);
00179               }
00180        }
00181                                    /* open GL */
00182        dev_open(radfile = argv[i]);
00183                                    /* load octree or scene files */
00184        if (octree != NULL) {
00185               displist = rgl_octlist(octree, NULL, NULL, NULL);
00186               startrtrace(octree);
00187        } else
00188               displist = rgl_filelist(nscenef, scene, NULL);
00189                                    /* set initial view */
00190        dev_view(currentview < 0 ? &thisview : vwl[currentview].v);
00191                                    /* input/render loop */
00192        while (dev_input(vwintvl))
00193               ;
00194                                    /* all done */
00195        quit(0);
00196 userr:
00197        fprintf(stderr,
00198               "Usage: %s [-w][-s][-b][-S][-v view] rfile [VAR=value]..\n",
00199                      argv[0]);
00200        quit(1);
00201        return 1; /* pro forma return */
00202 }
00203 
00204 
00205 void
00206 quit(                       /* exit gracefully */
00207        int    code
00208 )
00209 {
00210        if (ourdisplay != NULL)
00211               dev_close();
00212        /* if (rtpd.pid > 0) { */
00213        if (rtpd.running) {
00214               if (close_process(&rtpd) > 0)
00215                      wputs("bad exit status from rtrace\n");
00216               /* rtpd.pid = 0; */
00217        }
00218        exit(code);
00219 }
00220 
00221 
00222 static void
00223 startrtrace(                /* start rtrace on octname */
00224        char   *octname
00225 )
00226 {
00227        static char   *av[12] = {"rtrace", "-h", "-fff", "-ld+",
00228                                    "-opL", "-x", "1"};
00229        int    ac = 7;
00230 
00231        if (nowarn) av[ac++] = "-w-";
00232        av[ac++] = octname;
00233        av[ac] = NULL;
00234        if (open_process(&rtpd, av) <= 0)
00235               error(SYSTEM, "cannot start rtrace process");
00236 }
00237 
00238 
00239 static void
00240 runrad(                            /* run rad and load variables */
00241        int    ac,
00242        char   **av
00243 )
00244 {
00245        static char   optfile[] = TEMPLATE;
00246        int    nvn = 0, nvv = 0;
00247        FILE   *fp;
00248        register char *cp;
00249        char   radcomm[256], buf[128], nam[32];
00250                                    /* set rad commmand */
00251        strcpy(radcomm, "rad -w -v 0        ");   /* look out below! */
00252        cp = radcomm + 19;
00253        if (silent) {
00254               strcpy(cp, "-s ");
00255               cp += 3;
00256        }
00257        while (ac--) {
00258               strcpy(cp, *av++);
00259               while (*cp) cp++;
00260               *cp++ = ' ';
00261        }
00262        strcpy(cp, "OPTFILE=");            /* create temporary options file */
00263        strcpy(cp+8, mktemp(optfile));
00264        if (system(radcomm))        /* update octree */
00265               error(USER, "error executing rad command");
00266                                    /* replace "-v 0" with "-n -e -s -V" */
00267        strcpy(radcomm+7, "-n -e -s -V");
00268        radcomm[18] = ' ';
00269        if ((fp = popen(radcomm, "r")) == NULL)
00270               error(SYSTEM, "cannot start rad command");
00271        buf[0] = '\0';                     /* read variables alphabetically */
00272                                           /* get exposure */
00273        if ((cp = scan4var(buf, sizeof(buf), "EXPOSURE", fp)) != NULL) {
00274               expval = atof(cp);
00275               if ((*cp == '-') | (*cp == '+'))
00276                      expval = pow(2., expval);
00277               expval *= 0.5;              /* compensate for local shading */
00278        }
00279                                           /* look for eye separation */
00280        if ((cp = scan4var(buf, sizeof(buf), "EYESEP", fp)) != NULL)
00281               eyedist = atof(cp);
00282                                           /* look for materials */
00283        while ((cp = scan4var(buf, sizeof(buf), "materials", fp)) != NULL) {
00284               nscenef += wordstring(scene+nscenef, cp);
00285               buf[0] = '\0';
00286        }
00287                                           /* look for octree */
00288        if ((cp = scan4var(buf, sizeof(buf), "OCTREE", fp)) != NULL)
00289               octree = savqstr(cp);
00290                                           /* look for scene files */
00291        while ((cp = scan4var(buf, sizeof(buf), "scene", fp)) != NULL) {
00292               nscenef += wordstring(scene+nscenef, cp);
00293               buf[0] = '\0';
00294        }
00295                                           /* load view names */
00296        while ((cp = scan4var(buf, sizeof(buf), "view", fp)) != NULL) {
00297               if (nvn >= MAXVIEW)
00298                      error(INTERNAL, "too many views in rad file");
00299               vwl[nvn++].nam = *cp == '-' ? (char *)NULL :
00300                             savqstr(atos(nam, sizeof(nam), cp));
00301               buf[0] = '\0';
00302        }
00303                                           /* load actual views */
00304        do
00305               if (isview(buf)) {
00306                      vwl[nvv].v = (VIEW *)bmalloc(sizeof(VIEW));
00307                      *(vwl[nvv].v) = stdview;
00308                      sscanview(vwl[nvv].v, buf);
00309                      if ((cp = setview(vwl[nvv++].v)) != NULL) {
00310                             fprintf(stderr, "%s: bad view %d - %s\n",
00311                                           progname, nvv, cp);
00312                             quit(1);
00313                      }
00314               }
00315        while (fgets(buf, sizeof(buf), fp) != NULL);
00316        if (nvv != nvn)
00317               error(INTERNAL, "view miscount in runrad");
00318        pclose(fp);
00319                                           /* open options file */
00320        if ((fp = fopen(optfile, "r")) == NULL)
00321               error(SYSTEM, "cannot open options file");
00322                                           /* get relevant options */
00323        while (fgets(buf, sizeof(buf), fp) != NULL)
00324               if (!strncmp(buf, "-av ", 4))
00325                      setcolor(ambval, atof(buf+4),
00326                                    atof(sskip2(buf+4,1)),
00327                                    atof(sskip2(buf+4,2)));
00328               else if (backvis && !strncmp(buf, "-bv", 3) &&
00329                             (!buf[3] || strchr("0-FfNn \n",buf[3])!=NULL))
00330                      backvis = 0;
00331        fclose(fp);
00332        unlink(optfile);                   /* delete options file */
00333 }
00334 
00335 
00336 static int
00337 findvw(                     /* find named view */
00338        register char *nm
00339 )
00340 {
00341        register int  n;
00342 
00343        if ((*nm >= '1') & (*nm <= '9') &&
00344                      (n = atoi(nm)-1) <= MAXVIEW && vwl[n].v != NULL)
00345               return(n);
00346        for (n = 0; vwl[n].v != NULL; n++)
00347               if (vwl[n].nam != NULL && !strcmp(nm, vwl[n].nam))
00348                      return(n);
00349        return(-1);
00350 }
00351 
00352 
00353 static int
00354 varmatch(                          /* match line to variable */
00355        register char *s,
00356        register char *vn
00357 )
00358 {
00359        register int  c;
00360 
00361        for ( ; *vn && *s == *vn; s++, vn++)
00362               ;
00363        while (isspace(*s))
00364               s++;
00365        if (*s == '=')
00366               return(*vn);
00367        while (!(c = toupper(*s++) - toupper(*vn)) && *vn++)
00368               ;
00369        return(c);
00370 }
00371 
00372 
00373 static char *
00374 scan4var(     /* scan for variable from fp */
00375        char   *buf,
00376        int    buflen,
00377        char   *vname,
00378        FILE   *fp
00379 )
00380 {
00381        int    cval;
00382        register char *cp;
00383                                    /* search out matching line */
00384        while ((cval = varmatch(buf, vname))) {
00385               if (cval > 0)               /* gone too far? */
00386                      return(NULL);
00387               buf[0] = '\0';                     /* else get next line */
00388               if (fgetline(buf, buflen, fp) == NULL)
00389                      return(NULL);
00390        }
00391                                    /* skip variable name and '=' */
00392        for (cp = buf; *cp++ != '='; )
00393               ;
00394        while (isspace(*cp)) cp++;
00395        return(cp);
00396 }
00397 
00398 
00399 static void
00400 dev_open(                   /* initialize GLX driver */
00401        char  *id
00402 )
00403 {
00404        static int    atlBest[] = {GLX_RGBA, GLX_RED_SIZE,4,
00405                             GLX_GREEN_SIZE,4, GLX_BLUE_SIZE,4,
00406                             GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE,15, None};
00407        XSetWindowAttributes ourwinattr;
00408        XWMHints      ourxwmhints;
00409                                    /* open display server */
00410        ourdisplay = XOpenDisplay(NULL);
00411        if (ourdisplay == NULL)
00412               error(USER, "cannot open X-windows; DISPLAY variable set?\n");
00413                                    /* find a usable visual */
00414        ourvinf = glXChooseVisual(ourdisplay, ourscreen, atlBest);
00415        if (ourvinf == NULL)
00416               error(USER, "no suitable visuals available");
00417                                    /* get a context */
00418        gctx = glXCreateContext(ourdisplay, ourvinf, NULL, GL_TRUE);
00419                                    /* open window */
00420        ourwinattr.background_pixel = ourblack;
00421        ourwinattr.border_pixel = ourblack;
00422        ourwinattr.event_mask = ourmask;
00423                                    /* this is stupid */
00424        ourwinattr.colormap = XCreateColormap(ourdisplay, ourroot,
00425                             ourvinf->visual, AllocNone);
00426        gwind = XCreateWindow(ourdisplay, ourroot, 0, 0,
00427               DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH,
00428               DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH,
00429               BORWIDTH, ourvinf->depth, InputOutput, ourvinf->visual,
00430               CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &ourwinattr);
00431        if (gwind == 0)
00432               error(SYSTEM, "cannot create window\n");
00433        XStoreName(ourdisplay, gwind, id);
00434 #ifndef NOSTEREO
00435        if (stereo)                 /* check if stereo working */
00436               switch (XSGIQueryStereoMode(ourdisplay, gwind)) {
00437               case STEREO_TOP:
00438               case STEREO_BOTTOM:
00439                      break;
00440               case STEREO_OFF:
00441                      error(USER,
00442               "wrong video mode: run \"/usr/gfx/setmon -n STR_TOP\" first");
00443               case X_STEREO_UNSUPPORTED:
00444                      error(USER, "stereo not supported on this screen");
00445               default:
00446                      error(INTERNAL, "unknown stereo mode");
00447               }
00448 #endif
00449                                    /* set window manager hints */
00450        ourxwmhints.flags = InputHint|IconPixmapHint;
00451        ourxwmhints.input = True;
00452        ourxwmhints.icon_pixmap = XCreateBitmapFromData(ourdisplay, gwind,
00453               (char *)glradicon_bits, glradicon_width, glradicon_height);
00454        XSetWMHints(ourdisplay, gwind, &ourxwmhints);
00455                                    /* set GLX context */
00456        glXMakeCurrent(ourdisplay, gwind, gctx);
00457        glEnable(GL_DEPTH_TEST);
00458        glDepthFunc(GL_LESS);
00459        glShadeModel(GL_SMOOTH);
00460        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00461        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
00462        glEnable(GL_LIGHTING);
00463        glFrontFace(GL_CCW);
00464        glCullFace(GL_BACK);
00465        if (backvis)
00466               glDisable(GL_CULL_FACE);
00467        else
00468               glEnable(GL_CULL_FACE);
00469        glDrawBuffer(GL_BACK);
00470                                    /* figure out sensible view */
00471        pwidth = (double)DisplayWidthMM(ourdisplay, ourscreen) /
00472                      DisplayWidth(ourdisplay, ourscreen);
00473        pheight = (double)DisplayHeightMM(ourdisplay, ourscreen) /
00474                      DisplayHeight(ourdisplay, ourscreen);
00475        if (stereo) {               /* set stereo mode */
00476               setstereobuf(STEREO_BUFFER_LEFT);
00477               pheight *= 2.;
00478        }
00479                                    /* map the window */
00480        XMapWindow(ourdisplay, gwind);
00481        no_render++;
00482        do
00483               dev_input(0);        /* get resize event */
00484        while ((hres == 0) & (vres == 0));
00485        no_render--;
00486        rgl_checkerr("initializing GLX");
00487 }
00488 
00489 
00490 static void
00491 dev_close(void)                    /* close our display and free resources */
00492 {
00493        glXMakeCurrent(ourdisplay, None, NULL);
00494        glXDestroyContext(ourdisplay, gctx);
00495        XDestroyWindow(ourdisplay, gwind);
00496        gwind = 0;
00497        XCloseDisplay(ourdisplay);
00498        ourdisplay = NULL;
00499 }
00500 
00501 
00502 static int
00503 dev_view(                   /* assign new driver view */
00504        register VIEW *nv
00505 )
00506 {
00507        int    newhres = hres, newvres = vres;
00508        double wa, va;
00509                                    /* check view legality */
00510        if (nv->type != VT_PER) {
00511               error(COMMAND, "illegal view type");
00512               nv->type = VT_PER;
00513        }
00514        if ((nv->horiz > 160.) | (nv->vert > 160.)) {
00515               error(COMMAND, "illegal view angle");
00516               if (nv->horiz > 160.)
00517                      nv->horiz = 160.;
00518               if (nv->vert > 160.)
00519                      nv->vert = 160.;
00520        }
00521        if ((hres != 0) & (vres != 0)) {
00522               wa = (vres*pheight)/(hres*pwidth);
00523               va = viewaspect(nv);
00524               if (va > wa+.05) {
00525                      newvres = (pwidth/pheight)*va*newhres + .5;
00526                      if (newvres > maxvres) {
00527                             newvres = maxvres;
00528                             newhres = (pheight/pwidth)/va*newvres + .5;
00529                      }
00530               } else if (va < wa-.05) {
00531                      newhres = (pheight/pwidth)/va*newvres + .5;
00532                      if (newhres > maxhres) {
00533                             newhres = maxhres;
00534                             newvres = (pwidth/pheight)*va*newhres + .5;
00535                      }
00536               }
00537               if ((newhres != hres) | (newvres != vres)) {
00538                      no_render++;
00539                      XResizeWindow(ourdisplay, gwind, newhres, newvres);
00540                      do
00541                             dev_input(0);        /* get resize event */
00542                      while ((newhres != hres) | (newvres != vres));
00543                      no_render--;
00544               }
00545        }
00546        thisview = *nv;
00547        setglpersp(&thisview);
00548        render();
00549        return(1);
00550 }
00551 
00552 
00553 static int
00554 dev_input(           /* get next input event */
00555        int    nsecs
00556 )
00557 {
00558 #if 0
00559        static time_t lasttime = 0;
00560        time_t thistime;
00561 
00562        if (nsecs > 0) {
00563               thistime = time(0);
00564               nsecs -= (long)(thistime - lasttime);
00565               lasttime = thistime;
00566        }
00567        if (nsecs > 0)
00568               alarm(nsecs);
00569 #endif
00570        XNextEvent(ourdisplay, levptr(XEvent));
00571        switch (levptr(XEvent)->type) {
00572        case ConfigureNotify:
00573               resizewindow(levptr(XConfigureEvent));
00574               break;
00575        case UnmapNotify:
00576               mapped = 0;
00577               break;
00578        case MapNotify:
00579               mapped = 1;
00580               break;
00581        case Expose:
00582               fixwindow(levptr(XExposeEvent));
00583               break;
00584        case KeyPress:
00585               return(getkey(levptr(XKeyPressedEvent)));
00586        case ButtonPress:
00587               getmove(levptr(XButtonPressedEvent));
00588               break;
00589        }
00590        return(1);
00591 }
00592 
00593 
00594 static void
00595 render(void)                /* render our display list and swap buffers */
00596 {
00597        double d;
00598 
00599        if (!mapped | no_render)
00600               return;
00601        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
00602        glCallList(displist);
00603        if (stereo) {                      /* do right eye for stereo */
00604               setstereobuf(STEREO_BUFFER_RIGHT);
00605               glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
00606               glMatrixMode(GL_MODELVIEW);
00607               glPushMatrix();
00608               d = -eyedist / sqrt(thisview.hn2);
00609               glTranslated(d*thisview.hvec[0], d*thisview.hvec[1],
00610                             d*thisview.hvec[2]);
00611               glCallList(displist);
00612               glMatrixMode(GL_MODELVIEW);
00613               glPopMatrix();
00614               setstereobuf(STEREO_BUFFER_LEFT);
00615        }
00616        glXSwapBuffers(ourdisplay, gwind); /* calls glFlush() */
00617        rgl_checkerr("rendering display list");
00618 }
00619 
00620 
00621 static int
00622 moveview(     /* move our view */
00623        int    dx,
00624        int    dy,
00625        int    mov,
00626        int    orb
00627 )
00628 {
00629        VIEW   nv;
00630        FVECT  odir, v1, wp;
00631        double d;
00632                             /* start with old view */
00633        nv = thisview;
00634                             /* change view direction */
00635        if ((d = viewray(v1, odir, &thisview,
00636                      (dx+.5)/hres, (dy+.5)/vres)) < -FTINY)
00637               return(0);           /* outside view */
00638        if (mov | orb) {
00639               if (!getintersect(wp, v1, odir, d))
00640                      return(0);
00641               VSUM(odir, wp, nv.vp, -1.);
00642        } else
00643               VCOPY(nv.vdir, odir);
00644        if (orb && mov) {           /* orbit left/right */
00645               spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov);
00646               VSUM(nv.vp, wp, odir, -1.);
00647               spinvector(nv.vdir, nv.vdir, nv.vup, d);
00648        } else if (orb) {           /* orbit up/down */
00649               fcross(v1, odir, nv.vup);
00650               if (normalize(v1) == 0.)
00651                      return(0);
00652               spinvector(odir, odir, v1, d=MOVDEG*PI/180.*orb);
00653               VSUM(nv.vp, wp, odir, -1.);
00654               spinvector(nv.vdir, nv.vdir, v1, d);
00655        } else if (mov) {           /* move forward/backward */
00656               d = MOVPCT/100. * mov;
00657               VSUM(nv.vp, nv.vp, odir, d);
00658        }
00659        if (!mov ^ !orb && headlocked) {   /* restore head height */
00660               VSUM(v1, thisview.vp, nv.vp, -1.);
00661               d = DOT(v1, thisview.vup);
00662               VSUM(nv.vp, nv.vp, thisview.vup, d);
00663        }
00664        if (setview(&nv) != NULL)
00665               return(0);    /* illegal view */
00666        dev_view(&nv);
00667        return(1);
00668 }
00669 
00670 
00671 static void
00672 waitabit(void)                            /* pause a moment */
00673 {
00674        struct timespec      ts;
00675        ts.tv_sec = 0;
00676        ts.tv_nsec = 5000000;
00677        nanosleep(&ts, NULL);
00678 }
00679 
00680 
00681 static void
00682 getmove(                           /* get view change */
00683        XButtonPressedEvent  *ebut
00684 )
00685 {
00686        int    movdir = MOVDIR(ebut->button);
00687        int    movorb = MOVORB(ebut->state);
00688        int    moved = 0;
00689        Window rootw, childw;
00690        int    rootx, rooty, wx, wy;
00691        unsigned int  statemask;
00692 
00693        copylastv( movorb ? (movdir ? "left/right" : "up/down") :
00694                      (movdir ? "fore/back" : "rotate") );
00695        XNoOp(ourdisplay);
00696 
00697        while (!XCheckMaskEvent(ourdisplay,
00698                      ButtonReleaseMask, levptr(XEvent))) {
00699                                    /* pause so as not to move too fast */
00700               waitabit();
00701 
00702               if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw,
00703                             &rootx, &rooty, &wx, &wy, &statemask))
00704                      break;        /* on another screen */
00705 
00706               if (!moveview(wx, vres-1-wy, movdir, movorb)) {
00707                      sleep(1);
00708                      continue;
00709               } else
00710                      moved++;
00711        }
00712        if (!moved) {                             /* do final motion */
00713               movdir = MOVDIR(levptr(XButtonReleasedEvent)->button);
00714               wx = levptr(XButtonReleasedEvent)->x;
00715               wy = levptr(XButtonReleasedEvent)->y;
00716               moveview(wx, vres-1-wy, movdir, movorb);
00717        }
00718 }
00719 
00720 
00721 static int
00722 getintersect(        /* intersect ray with scene geometry */
00723        FVECT  wp,           /* returned world intersection point */
00724        FVECT  org,
00725        FVECT  dir,
00726        double md
00727 )
00728 {
00729        float  fbuf[6];
00730                             /* check to see if rtrace is running */
00731        /* if (rtpd.pid <= 0) */
00732        if (!rtpd.running)
00733               return(0);
00734                             /* assign origin */
00735        fbuf[0] = org[0]; fbuf[1] = org[1]; fbuf[2] = org[2];
00736                             /* compute clipping distance */
00737        if (md <= FTINY) md = FHUGE;
00738        fbuf[3] = dir[0]*md; fbuf[4] = dir[1]*md; fbuf[5] = dir[2]*md;
00739                             /* trace that ray */
00740        if (process(&rtpd, (char *)fbuf, (char *)fbuf,
00741                      4*sizeof(float), 6*sizeof(float)) != 4*sizeof(float))
00742               error(INTERNAL, "error getting data back from rtrace process");
00743        if (fbuf[3] >= .99*FHUGE)
00744               return(0);    /* missed local objects */
00745        wp[0] = fbuf[0]; wp[1] = fbuf[1]; wp[2] = fbuf[2];
00746        return(1);           /* else return world intersection */
00747 }
00748 
00749 
00750 static void
00751 setglpersp(                 /* set perspective view in GL */
00752        register VIEW *vp
00753 )
00754 {
00755        double d, xmin, xmax, ymin, ymax, zmin, zmax;
00756 
00757        zmin = 0.1;
00758        zmax = 1000.;
00759        if (thisview.vfore > FTINY)
00760               zmin = thisview.vfore;
00761        if (thisview.vaft > FTINY)
00762               zmax = thisview.vaft;
00763        xmax = zmin * tan(PI/180./2. * thisview.horiz);
00764        xmin = -xmax;
00765        d = thisview.hoff * (xmax - xmin);
00766        xmin += d; xmax += d;
00767        ymax = zmin * tan(PI/180./2. * thisview.vert);
00768        ymin = -ymax;
00769        d = thisview.voff * (ymax - ymin);
00770        ymin += d; ymax += d;
00771                                    /* set view matrix */
00772        glMatrixMode(GL_PROJECTION);
00773        glLoadIdentity();
00774        glFrustum(xmin, xmax, ymin, ymax, zmin, zmax);
00775        gluLookAt(thisview.vp[0], thisview.vp[1], thisview.vp[2],
00776               thisview.vp[0] + thisview.vdir[0],
00777               thisview.vp[1] + thisview.vdir[1],
00778               thisview.vp[2] + thisview.vdir[2],
00779               thisview.vup[0], thisview.vup[1], thisview.vup[2]);
00780        rgl_checkerr("setting perspective view");
00781 }
00782 
00783 
00784 static int
00785 getkey(                            /* get input key */
00786        register XKeyPressedEvent  *ekey
00787 )
00788 {
00789        int  n;
00790        char   buf[8];
00791 
00792        n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL);
00793        if (n != 1)
00794               return(1);
00795        switch (buf[0]) {
00796        case 'h':                   /* turn on height motion lock */
00797               headlocked = 1;
00798               break;
00799        case 'H':                   /* turn off height motion lock */
00800               headlocked = 0;
00801               break;
00802        case 'l':                   /* retrieve last (premouse) view */
00803               if (lastview.type) {
00804                      VIEW   vtmp;
00805                      vtmp = thisview;
00806                      dev_view(&lastview);
00807                      lastview = vtmp;
00808               } else
00809                      XBell(ourdisplay, 0);
00810               break;
00811        case 'n':                   /* move to next standard view */
00812               gotoview(currentview+1);
00813               break;
00814        case 'p':                   /* move to last standard view */
00815               gotoview(currentview-1);
00816               break;
00817        case '+':                   /* zoom in */
00818               zoomview(100+ZOOMPCT, ekey->x, vres-1-ekey->y);
00819               break;
00820        case '-':                   /* zoom out */
00821               zoomview(100-ZOOMPCT, ekey->x, vres-1-ekey->y);
00822               break;
00823        case 'v':                   /* spit current view to stdout */
00824               fputs(VIEWSTR, stdout);
00825               fprintview(&thisview, stdout);
00826               fputc('\n', stdout);
00827               break;
00828        case 'V':                   /* append view to rad file */
00829               appendview(NULL, &thisview);
00830               break;
00831        case 'q':                   /* quit the program */
00832               return(0);
00833        default:
00834               XBell(ourdisplay, 0);
00835               break;
00836        }
00837        return(1);
00838 }
00839 
00840 
00841 static void
00842 zoomview(                   /* zoom in or out around (dx,dy) */
00843        int    pct,
00844        int    dx,
00845        int    dy
00846 )
00847 {
00848        double h, v;
00849 
00850        if ((pct == 100) | (pct <= 0))
00851               return;
00852        copylastv("zooming");
00853        h = (dx+.5)/hres - 0.5;
00854        v = (dy+.5)/vres - 0.5;
00855        h *= (1. - 100./pct);
00856        v *= (1. - 100./pct);
00857        thisview.vdir[0] += h*thisview.hvec[0] + v*thisview.vvec[0];
00858        thisview.vdir[1] += h*thisview.hvec[1] + v*thisview.vvec[1];
00859        thisview.vdir[2] += h*thisview.hvec[2] + v*thisview.vvec[2];
00860        thisview.horiz = 2.*180./PI * atan( 100./pct *
00861                                    tan(PI/180./2.*thisview.horiz) );
00862        thisview.vert = 2.*180./PI * atan( 100./pct *
00863                                    tan(PI/180./2.*thisview.vert) );
00864        setview(&thisview);
00865        dev_view(&thisview);
00866 }
00867 
00868 
00869 static void
00870 gotoview(                          /* go to specified view number */
00871        int    vwnum
00872 )
00873 {
00874        if (vwnum < 0)
00875               for (vwnum = currentview; vwl[vwnum+1].v != NULL; vwnum++)
00876                      ;
00877        else if (vwnum >= MAXVIEW || vwl[vwnum].v == NULL)
00878               vwnum = 0;
00879        copylastv("standard view");
00880        dev_view(vwl[currentview=vwnum].v);
00881 }
00882 
00883 
00884 static void
00885 appendview(                 /* append standard view */
00886        char   *nm,
00887        VIEW   *vp
00888 )
00889 {
00890        FILE   *fp;
00891                                    /* check if already in there */
00892        if (!memcmp(&thisview, vwl[currentview].v, sizeof(VIEW))) {
00893               error(COMMAND, "view already in standard list");
00894               return;
00895        }
00896                                    /* append to file */
00897        if ((fp = fopen(radfile, "a")) == NULL) {
00898               error(COMMAND, "cannot append rad input file");
00899               return;
00900        }
00901        fputs("view=", fp);
00902        if (nm != NULL) {
00903               fputc(' ', fp); fputs(nm, fp);
00904        }
00905        fprintview(vp, fp); fputc('\n', fp);
00906        fclose(fp);
00907                                    /* append to our list */
00908        while (vwl[currentview].v != NULL)
00909               currentview++;
00910        if (currentview >= MAXVIEW)
00911               error(INTERNAL, "too many views in appendview");
00912        vwl[currentview].v = (VIEW *)bmalloc(sizeof(VIEW));
00913        *(vwl[currentview].v) = thisview;
00914        if (nm != NULL)
00915               vwl[currentview].nam = savqstr(nm);
00916 }
00917 
00918 
00919 static void
00920 copylastv(                  /* copy last view position */
00921        char   *cause
00922 )
00923 {
00924        static char   *lastvc;
00925 
00926        if (cause == lastvc)
00927               return;                     /* only record one view per cause */
00928        lastvc = cause;
00929        lastview = thisview;
00930 }
00931 
00932 
00933 static void
00934 fixwindow(                         /* repair damage to window */
00935        register XExposeEvent  *eexp
00936 )
00937 {
00938        if ((hres == 0) | (vres == 0)) {   /* first exposure */
00939               resizewindow((XConfigureEvent *)eexp);
00940               return;
00941        }
00942        if (eexp->count)            /* wait for final exposure */
00943               return;
00944                                    /* rerender everything */
00945        render();
00946 }
00947 
00948 
00949 static void
00950 resizewindow(               /* resize window */
00951        register XConfigureEvent  *ersz
00952 )
00953 {
00954        static char   resizing[] = "resizing window";
00955        double wa, va;
00956 
00957        glViewport(0, 0, hres=ersz->width, vres=ersz->height);
00958        if (hres > maxhres) maxhres = hres;
00959        if (vres > maxvres) maxvres = vres;
00960        if (no_render)
00961               return;
00962        wa = (vres*pheight)/(hres*pwidth);
00963        va = viewaspect(&thisview);
00964        if (va > wa+.05) {
00965               copylastv(resizing);
00966               thisview.vert = 2.*180./PI *
00967                             atan( tan(PI/180./2. * thisview.horiz) * wa );
00968        } else if (va < wa-.05) {
00969               copylastv(resizing);
00970               thisview.horiz = 2.*180./PI *
00971                             atan( tan(PI/180./2. * thisview.vert) / wa );
00972        } else
00973               return;
00974        setview(&thisview);
00975        dev_view(&thisview);
00976 }