| 1 | /* Copyright (c) 1998 Silicon Graphics, Inc. */ | 
| 2 |  | 
| 3 | #ifndef lint | 
| 4 | static char SCCSid[] = "$SunId$ SGI"; | 
| 5 | #endif | 
| 6 |  | 
| 7 | /* | 
| 8 | * OpenGL GLX driver for holodeck display. | 
| 9 | * Based on old GLX driver using cones. | 
| 10 | * | 
| 11 | * Define symbol STEREO for stereo viewing. | 
| 12 | * Define symbol DOBJ for display object viewing. | 
| 13 | */ | 
| 14 |  | 
| 15 | #ifdef NOSTEREO | 
| 16 | #ifdef STEREO | 
| 17 | #undef STEREO | 
| 18 | #else | 
| 19 | #undef NOSTEREO | 
| 20 | #endif | 
| 21 | #endif | 
| 22 |  | 
| 23 | #include "standard.h" | 
| 24 | #include "rhd_sample.h" | 
| 25 |  | 
| 26 | #include <sys/types.h> | 
| 27 | #include <GL/glx.h> | 
| 28 | #include <GL/glu.h> | 
| 29 | #ifdef STEREO | 
| 30 | #include <X11/extensions/SGIStereo.h> | 
| 31 | #endif | 
| 32 | #ifdef DOBJ | 
| 33 | #include "rhdobj.h" | 
| 34 | #endif | 
| 35 |  | 
| 36 | #include "x11icon.h" | 
| 37 |  | 
| 38 | #ifndef RAYQLEN | 
| 39 | #define RAYQLEN         250             /* max. rays to queue before flush */ | 
| 40 | #endif | 
| 41 |  | 
| 42 | #ifndef FEQ | 
| 43 | #define FEQ(a,b)        ((a)-(b) <= FTINY && (a)-(b) >= -FTINY) | 
| 44 | #endif | 
| 45 |  | 
| 46 | #define GAMMA           1.4             /* default gamma correction */ | 
| 47 |  | 
| 48 | #define MOVPCT          7               /* percent distance to move /frame */ | 
| 49 | #define MOVDIR(b)       ((b)==Button1 ? 1 : (b)==Button2 ? 0 : -1) | 
| 50 | #define MOVDEG          (-5)            /* degrees to orbit CW/down /frame */ | 
| 51 | #define MOVORB(s)       ((s)&ShiftMask ? 1 : (s)&ControlMask ? -1 : 0) | 
| 52 |  | 
| 53 | #ifndef TARGETFPS | 
| 54 | #define TARGETFPS       4.0             /* target frames/sec during motion */ | 
| 55 | #endif | 
| 56 |  | 
| 57 | #define MINWIDTH        480             /* minimum graphics window width */ | 
| 58 | #define MINHEIGHT       400             /* minimum graphics window height */ | 
| 59 |  | 
| 60 | #define VIEWDIST        356             /* assumed viewing distance (mm) */ | 
| 61 |  | 
| 62 | #define BORWIDTH        5               /* border width */ | 
| 63 |  | 
| 64 | #define setstereobuf(bid)       (glXWaitGL(), \ | 
| 65 | XSGISetStereoBuffer(ourdisplay, gwind, bid), \ | 
| 66 | glXWaitX()) | 
| 67 |  | 
| 68 | #define  ourscreen      DefaultScreen(ourdisplay) | 
| 69 | #define  ourroot        RootWindow(ourdisplay,ourscreen) | 
| 70 | #define  ourmask        (StructureNotifyMask|ExposureMask|KeyPressMask|\ | 
| 71 | ButtonPressMask|ButtonReleaseMask) | 
| 72 |  | 
| 73 | #define  levptr(etype)  ((etype *)¤tevent) | 
| 74 |  | 
| 75 | struct driver   odev;                   /* global device driver structure */ | 
| 76 |  | 
| 77 | static VIEW     vwright;                /* right eye view */ | 
| 78 |  | 
| 79 | static int      rayqleft = 0;           /* rays left to queue before flush */ | 
| 80 |  | 
| 81 | static XEvent  currentevent;            /* current event */ | 
| 82 |  | 
| 83 | static int  mapped = 0;                 /* window is mapped? */ | 
| 84 | static unsigned long  ourblack=0, ourwhite=~0; | 
| 85 |  | 
| 86 | static Display  *ourdisplay = NULL;     /* our display */ | 
| 87 | static XVisualInfo  *ourvinf;           /* our visual information */ | 
| 88 | static Window  gwind = 0;               /* our graphics window */ | 
| 89 | static GLXContext       gctx;           /* our GLX context */ | 
| 90 |  | 
| 91 | static double   pwidth, pheight;        /* pixel dimensions (mm) */ | 
| 92 |  | 
| 93 | static double   mindpth, maxdpth;       /* min. and max. depth */ | 
| 94 |  | 
| 95 | double  dev_zmin, dev_zmax;             /* fore and aft clipping plane dist. */ | 
| 96 |  | 
| 97 | static int      inpresflags;            /* input result flags */ | 
| 98 |  | 
| 99 | static int      headlocked = 0;         /* lock vertical motion */ | 
| 100 |  | 
| 101 | static int  resizewindow(), getevent(), getkey(), moveview(), wipeclean(), | 
| 102 | setglpersp(), getmove(), fixwindow(), mytmflags(); | 
| 103 |  | 
| 104 | #ifdef STEREO | 
| 105 | static int  pushright(), popright(); | 
| 106 | #endif | 
| 107 |  | 
| 108 | extern time_t   time(); | 
| 109 |  | 
| 110 |  | 
| 111 | dev_open(id)                    /* initialize GLX driver */ | 
| 112 | char  *id; | 
| 113 | { | 
| 114 | extern char     *getenv(); | 
| 115 | static RGBPRIMS myprims = STDPRIMS; | 
| 116 | static int      atlBest[] = {GLX_RGBA, GLX_RED_SIZE,8, | 
| 117 | GLX_GREEN_SIZE,8, GLX_BLUE_SIZE,8, | 
| 118 | GLX_DEPTH_SIZE,15, None}; | 
| 119 | char    *ev; | 
| 120 | double  gamval = GAMMA; | 
| 121 | RGBPRIMP        dpri = stdprims; | 
| 122 | XSetWindowAttributes    ourwinattr; | 
| 123 | XWMHints        ourxwmhints; | 
| 124 | XSizeHints      oursizhints; | 
| 125 | /* check for unsupported stereo */ | 
| 126 | #ifdef NOSTEREO | 
| 127 | error(USER, "stereo display driver unavailable"); | 
| 128 | #endif | 
| 129 | /* open display server */ | 
| 130 | ourdisplay = XOpenDisplay(NULL); | 
| 131 | if (ourdisplay == NULL) | 
| 132 | error(USER, "cannot open X-windows; DISPLAY variable set?\n"); | 
| 133 | #ifdef STEREO | 
| 134 | switch (XSGIQueryStereoMode(ourdisplay, ourroot)) { | 
| 135 | case STEREO_TOP: | 
| 136 | case STEREO_BOTTOM: | 
| 137 | break; | 
| 138 | case STEREO_OFF: | 
| 139 | error(USER, | 
| 140 | "wrong video mode: run \"/usr/gfx/setmon -n STR_TOP\" first"); | 
| 141 | case X_STEREO_UNSUPPORTED: | 
| 142 | error(USER, "stereo mode not supported on this screen"); | 
| 143 | default: | 
| 144 | error(INTERNAL, "unknown stereo mode"); | 
| 145 | } | 
| 146 | #endif | 
| 147 | /* find a usable visual */ | 
| 148 | ourvinf = glXChooseVisual(ourdisplay, ourscreen, atlBest); | 
| 149 | if (ourvinf == NULL) | 
| 150 | error(USER, "no suitable visuals available"); | 
| 151 | /* get a context */ | 
| 152 | gctx = glXCreateContext(ourdisplay, ourvinf, NULL, GL_TRUE); | 
| 153 | /* set gamma and tone mapping */ | 
| 154 | if ((ev = XGetDefault(ourdisplay, "radiance", "gamma")) != NULL | 
| 155 | || (ev = getenv("DISPLAY_GAMMA")) != NULL) | 
| 156 | gamval = atof(ev); | 
| 157 | if ((ev = getenv("DISPLAY_PRIMARIES")) != NULL && | 
| 158 | sscanf(ev, "%f %f %f %f %f %f %f %f", | 
| 159 | &myprims[RED][CIEX],&myprims[RED][CIEY], | 
| 160 | &myprims[GRN][CIEX],&myprims[GRN][CIEY], | 
| 161 | &myprims[BLU][CIEX],&myprims[BLU][CIEY], | 
| 162 | &myprims[WHT][CIEX],&myprims[WHT][CIEY]) >= 6) | 
| 163 | dpri = myprims; | 
| 164 | if (tmInit(mytmflags(), dpri, gamval) == NULL) | 
| 165 | error(SYSTEM, "not enough memory in dev_open"); | 
| 166 | /* open window */ | 
| 167 | ourwinattr.background_pixel = ourblack; | 
| 168 | ourwinattr.border_pixel = ourblack; | 
| 169 | ourwinattr.event_mask = ourmask; | 
| 170 | /* this is stupid */ | 
| 171 | ourwinattr.colormap = XCreateColormap(ourdisplay, ourroot, | 
| 172 | ourvinf->visual, AllocNone); | 
| 173 | gwind = XCreateWindow(ourdisplay, ourroot, 0, 0, | 
| 174 | DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH, | 
| 175 | #ifdef STEREO | 
| 176 | (DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH)/2, | 
| 177 | #else | 
| 178 | DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH, | 
| 179 | #endif | 
| 180 | BORWIDTH, ourvinf->depth, InputOutput, ourvinf->visual, | 
| 181 | CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &ourwinattr); | 
| 182 | if (gwind == 0) | 
| 183 | error(SYSTEM, "cannot create window\n"); | 
| 184 | XStoreName(ourdisplay, gwind, id); | 
| 185 | /* set window manager hints */ | 
| 186 | ourxwmhints.flags = InputHint|IconPixmapHint; | 
| 187 | ourxwmhints.input = True; | 
| 188 | ourxwmhints.icon_pixmap = XCreateBitmapFromData(ourdisplay, | 
| 189 | gwind, x11icon_bits, x11icon_width, x11icon_height); | 
| 190 | XSetWMHints(ourdisplay, gwind, &ourxwmhints); | 
| 191 | oursizhints.min_width = MINWIDTH; | 
| 192 | #ifdef STEREO | 
| 193 | oursizhints.min_height = MINHEIGHT/2; | 
| 194 | oursizhints.max_width = DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH; | 
| 195 | oursizhints.max_height = (DisplayHeight(ourdisplay,ourscreen) - | 
| 196 | 2*BORWIDTH)/2; | 
| 197 | oursizhints.flags = PMinSize|PMaxSize; | 
| 198 | #else | 
| 199 | oursizhints.min_height = MINHEIGHT; | 
| 200 | oursizhints.flags = PMinSize; | 
| 201 | #endif | 
| 202 | XSetNormalHints(ourdisplay, gwind, &oursizhints); | 
| 203 | /* set GLX context */ | 
| 204 | glXMakeCurrent(ourdisplay, gwind, gctx); | 
| 205 | glEnable(GL_DEPTH_TEST); | 
| 206 | glDepthFunc(GL_LEQUAL); | 
| 207 | glShadeModel(GL_SMOOTH); | 
| 208 | glDisable(GL_DITHER); | 
| 209 | glDisable(GL_CULL_FACE); | 
| 210 | /* figure out sensible view */ | 
| 211 | pwidth = (double)DisplayWidthMM(ourdisplay, ourscreen) / | 
| 212 | DisplayWidth(ourdisplay, ourscreen); | 
| 213 | pheight = (double)DisplayHeightMM(ourdisplay, ourscreen) / | 
| 214 | DisplayHeight(ourdisplay, ourscreen); | 
| 215 | #ifdef STEREO | 
| 216 | pheight *= 2.; | 
| 217 | setstereobuf(STEREO_BUFFER_LEFT); | 
| 218 | #endif | 
| 219 | copystruct(&odev.v, &stdview); | 
| 220 | odev.v.type = VT_PER; | 
| 221 | /* map the window */ | 
| 222 | XMapWindow(ourdisplay, gwind); | 
| 223 | dev_input();                    /* sets size and view angles */ | 
| 224 | /* allocate our samples */ | 
| 225 | if (!smInit(DisplayWidth(ourdisplay,ourscreen) * | 
| 226 | DisplayHeight(ourdisplay,ourscreen) / 10)) | 
| 227 | error(SYSTEM, "insufficient memory for value storage"); | 
| 228 | mindpth = FHUGE; maxdpth = FTINY; | 
| 229 | odev.name = id; | 
| 230 | odev.ifd = ConnectionNumber(ourdisplay); | 
| 231 | } | 
| 232 |  | 
| 233 |  | 
| 234 | dev_close()                     /* close our display and free resources */ | 
| 235 | { | 
| 236 | smInit(0); | 
| 237 | #ifdef DOBJ | 
| 238 | dobj_cleanup(); | 
| 239 | #endif | 
| 240 | glXMakeCurrent(ourdisplay, None, NULL); | 
| 241 | glXDestroyContext(ourdisplay, gctx); | 
| 242 | XDestroyWindow(ourdisplay, gwind); | 
| 243 | gwind = 0; | 
| 244 | XCloseDisplay(ourdisplay); | 
| 245 | ourdisplay = NULL; | 
| 246 | tmDone(NULL); | 
| 247 | odev.v.type = 0; | 
| 248 | odev.hres = odev.vres = 0; | 
| 249 | odev.ifd = -1; | 
| 250 | } | 
| 251 |  | 
| 252 |  | 
| 253 | dev_clear()                     /* clear our representation */ | 
| 254 | { | 
| 255 | smInit(rsL.max_samp); | 
| 256 | wipeclean(); | 
| 257 | rayqleft = 0;                   /* hold off update */ | 
| 258 | } | 
| 259 |  | 
| 260 |  | 
| 261 | int | 
| 262 | dev_view(nv)                    /* assign new driver view */ | 
| 263 | register VIEW   *nv; | 
| 264 | { | 
| 265 | double  d; | 
| 266 |  | 
| 267 | if (nv->type != VT_PER ||               /* check view legality */ | 
| 268 | nv->horiz > 160. || nv->vert > 160.) { | 
| 269 | error(COMMAND, "illegal view type/angle"); | 
| 270 | nv->type = odev.v.type; | 
| 271 | nv->horiz = odev.v.horiz; | 
| 272 | nv->vert = odev.v.vert; | 
| 273 | return(0); | 
| 274 | } | 
| 275 | if (nv != &odev.v &&                    /* resize window? */ | 
| 276 | (!FEQ(nv->horiz,odev.v.horiz) || | 
| 277 | !FEQ(nv->vert,odev.v.vert))) { | 
| 278 | int     dw = DisplayWidth(ourdisplay,ourscreen); | 
| 279 | int     dh = DisplayHeight(ourdisplay,ourscreen); | 
| 280 |  | 
| 281 | dw -= 25;       /* for window frame */ | 
| 282 | dh -= 50; | 
| 283 | #ifdef STEREO | 
| 284 | dh /= 2; | 
| 285 | #endif | 
| 286 | odev.hres = 2.*VIEWDIST/pwidth * | 
| 287 | tan(PI/180./2.*nv->horiz); | 
| 288 | odev.vres = 2.*VIEWDIST/pheight * | 
| 289 | tan(PI/180./2.*nv->vert); | 
| 290 | if (odev.hres > dw) { | 
| 291 | odev.vres = dw * odev.vres / odev.hres; | 
| 292 | odev.hres = dw; | 
| 293 | } | 
| 294 | if (odev.vres > dh) { | 
| 295 | odev.hres = dh * odev.hres / odev.vres; | 
| 296 | odev.vres = dh; | 
| 297 | } | 
| 298 | XResizeWindow(ourdisplay, gwind, odev.hres, odev.vres); | 
| 299 | dev_input();    /* get resize event */ | 
| 300 | } | 
| 301 | copystruct(&odev.v, nv);        /* setview() already called */ | 
| 302 | setglpersp(&odev.v); | 
| 303 | #ifdef STEREO | 
| 304 | copystruct(&vwright, nv); | 
| 305 | d = eyesepdist / sqrt(nv->hn2); | 
| 306 | VSUM(vwright.vp, nv->vp, nv->hvec, d); | 
| 307 | /* setview(&vwright);   -- Unnecessary */ | 
| 308 | #endif | 
| 309 | checkglerr("setting view"); | 
| 310 | wipeclean(); | 
| 311 | return(1); | 
| 312 | } | 
| 313 |  | 
| 314 |  | 
| 315 | dev_auxcom(cmd, args)           /* process an auxiliary command */ | 
| 316 | char    *cmd, *args; | 
| 317 | { | 
| 318 | #ifdef DOBJ | 
| 319 | if (dobj_command(cmd, args) >= 0) | 
| 320 | return; | 
| 321 | #endif | 
| 322 | sprintf(errmsg, "%s: unknown command", cmd); | 
| 323 | error(COMMAND, errmsg); | 
| 324 | } | 
| 325 |  | 
| 326 |  | 
| 327 | VIEW * | 
| 328 | dev_auxview(n, hvres)           /* return nth auxiliary view */ | 
| 329 | int     n; | 
| 330 | int     hvres[2]; | 
| 331 | { | 
| 332 | hvres[0] = odev.hres; hvres[1] = odev.vres; | 
| 333 | if (n == 0) | 
| 334 | return(&odev.v); | 
| 335 | #ifdef STEREO | 
| 336 | if (n == 1) | 
| 337 | return(&vwright); | 
| 338 | #endif | 
| 339 | return(NULL); | 
| 340 | } | 
| 341 |  | 
| 342 |  | 
| 343 | int | 
| 344 | dev_input()                     /* get X11 input */ | 
| 345 | { | 
| 346 | inpresflags = 0; | 
| 347 |  | 
| 348 | do | 
| 349 | getevent(); | 
| 350 |  | 
| 351 | while (XPending(ourdisplay) > 0); | 
| 352 |  | 
| 353 | odev.inpready = 0; | 
| 354 |  | 
| 355 | return(inpresflags); | 
| 356 | } | 
| 357 |  | 
| 358 |  | 
| 359 | dev_value(c, d, p)              /* add a pixel value to our mesh */ | 
| 360 | COLR    c; | 
| 361 | FVECT   d, p; | 
| 362 | { | 
| 363 | double  depth; | 
| 364 | #ifdef DOBJ | 
| 365 | if (dobj_lightsamp != NULL) {   /* in light source sampling */ | 
| 366 | (*dobj_lightsamp)(c, d, p); | 
| 367 | return; | 
| 368 | } | 
| 369 | #endif | 
| 370 | smNewSamp(c, d, p);             /* add to display representation */ | 
| 371 | if (p != NULL) { | 
| 372 | depth = (p[0] - odev.v.vp[0])*d[0] + | 
| 373 | (p[1] - odev.v.vp[1])*d[1] + | 
| 374 | (p[2] - odev.v.vp[2])*d[2]; | 
| 375 | if (depth > FTINY) { | 
| 376 | if (depth < mindpth) | 
| 377 | mindpth = depth; | 
| 378 | if (depth > maxdpth) | 
| 379 | maxdpth = depth; | 
| 380 | } | 
| 381 | } | 
| 382 | if (!--rayqleft) | 
| 383 | dev_flush();            /* flush output */ | 
| 384 | } | 
| 385 |  | 
| 386 |  | 
| 387 | int | 
| 388 | dev_flush()                     /* flush output */ | 
| 389 | { | 
| 390 | #ifdef STEREO | 
| 391 | pushright();                    /* update right eye */ | 
| 392 | glClear(GL_DEPTH_BUFFER_BIT); | 
| 393 | smUpdate(&vwright, 100); | 
| 394 | #ifdef DOBJ | 
| 395 | dobj_render();                  /* usually in foreground */ | 
| 396 | #endif | 
| 397 | popright();                     /* update left eye */ | 
| 398 | glClear(GL_DEPTH_BUFFER_BIT); | 
| 399 | #endif | 
| 400 | smUpdate(&odev.v, 100); | 
| 401 | #ifdef DOBJ | 
| 402 | dobj_render(); | 
| 403 | #endif | 
| 404 | glFlush();                      /* flush OGL */ | 
| 405 | checkglerr("flushing display"); | 
| 406 | rayqleft = RAYQLEN; | 
| 407 | /* flush X11 and return # pending */ | 
| 408 | return(odev.inpready = XPending(ourdisplay)); | 
| 409 | } | 
| 410 |  | 
| 411 |  | 
| 412 | checkglerr(where)               /* check for GL or GLU error */ | 
| 413 | char    *where; | 
| 414 | { | 
| 415 | register GLenum errcode; | 
| 416 |  | 
| 417 | while ((errcode = glGetError()) != GL_NO_ERROR) { | 
| 418 | sprintf(errmsg, "OpenGL error %s: %s", | 
| 419 | where, gluErrorString(errcode)); | 
| 420 | error(WARNING, errmsg); | 
| 421 | } | 
| 422 | } | 
| 423 |  | 
| 424 |  | 
| 425 | #ifdef STEREO | 
| 426 | static | 
| 427 | pushright()                     /* push on right view */ | 
| 428 | { | 
| 429 | double  d; | 
| 430 |  | 
| 431 | setstereobuf(STEREO_BUFFER_RIGHT); | 
| 432 | glMatrixMode(GL_MODELVIEW); | 
| 433 | glPushMatrix(); | 
| 434 | d = -eyesepdist / sqrt(odev.v.hn2); | 
| 435 | glTranslated(d*odev.v.hvec[0], d*odev.v.hvec[1], d*odev.v.hvec[2]); | 
| 436 | } | 
| 437 |  | 
| 438 |  | 
| 439 | static | 
| 440 | popright()                      /* pop off right view */ | 
| 441 | { | 
| 442 | glMatrixMode(GL_MODELVIEW); | 
| 443 | glPopMatrix(); | 
| 444 | setstereobuf(STEREO_BUFFER_LEFT); | 
| 445 | } | 
| 446 | #endif | 
| 447 |  | 
| 448 |  | 
| 449 | static int | 
| 450 | mytmflags()                     /* figure out tone mapping flags */ | 
| 451 | { | 
| 452 | extern char     *progname; | 
| 453 | register char   *cp, *tail; | 
| 454 | /* find basic name */ | 
| 455 | for (cp = tail = progname; *cp; cp++) | 
| 456 | if (*cp == '/') | 
| 457 | tail = cp+1; | 
| 458 | for (cp = tail; *cp && *cp != '.'; cp++) | 
| 459 | ; | 
| 460 | if (cp > tail && cp[-1] == 'h') | 
| 461 | return(TM_F_HUMAN|TM_F_NOSTDERR); | 
| 462 | else | 
| 463 | return(TM_F_CAMERA|TM_F_NOSTDERR); | 
| 464 | } | 
| 465 |  | 
| 466 |  | 
| 467 | static | 
| 468 | getevent()                      /* get next event */ | 
| 469 | { | 
| 470 | XNextEvent(ourdisplay, levptr(XEvent)); | 
| 471 | switch (levptr(XEvent)->type) { | 
| 472 | case ConfigureNotify: | 
| 473 | resizewindow(levptr(XConfigureEvent)); | 
| 474 | break; | 
| 475 | case UnmapNotify: | 
| 476 | mapped = 0; | 
| 477 | break; | 
| 478 | case MapNotify: | 
| 479 | mapped = 1; | 
| 480 | break; | 
| 481 | case Expose: | 
| 482 | fixwindow(levptr(XExposeEvent)); | 
| 483 | break; | 
| 484 | case KeyPress: | 
| 485 | getkey(levptr(XKeyPressedEvent)); | 
| 486 | break; | 
| 487 | case ButtonPress: | 
| 488 | getmove(levptr(XButtonPressedEvent)); | 
| 489 | break; | 
| 490 | } | 
| 491 | } | 
| 492 |  | 
| 493 |  | 
| 494 | static | 
| 495 | draw3dline(wp)                  /* draw 3d line in world coordinates */ | 
| 496 | register FVECT  wp[2]; | 
| 497 | { | 
| 498 | glVertex3d(wp[0][0], wp[0][1], wp[0][2]); | 
| 499 | glVertex3d(wp[1][0], wp[1][1], wp[1][2]); | 
| 500 | } | 
| 501 |  | 
| 502 |  | 
| 503 | static | 
| 504 | draw_grids()                    /* draw holodeck section grids */ | 
| 505 | { | 
| 506 | static BYTE     gridrgba[4] = {0x0, 0xff, 0xff, 0x00}; | 
| 507 |  | 
| 508 | if (!mapped) | 
| 509 | return; | 
| 510 | glColor4ub(gridrgba[0], gridrgba[1], gridrgba[2], gridrgba[3]); | 
| 511 | glBegin(GL_LINES);              /* draw each grid line */ | 
| 512 | gridlines(draw3dline); | 
| 513 | glEnd(); | 
| 514 | } | 
| 515 |  | 
| 516 |  | 
| 517 | static | 
| 518 | moveview(dx, dy, mov, orb)      /* move our view */ | 
| 519 | int     dx, dy, mov, orb; | 
| 520 | { | 
| 521 | VIEW    nv; | 
| 522 | FVECT   odir, v1, wip; | 
| 523 | double  d; | 
| 524 | register int    li; | 
| 525 | /* start with old view */ | 
| 526 | copystruct(&nv, &odev.v); | 
| 527 | /* change view direction */ | 
| 528 | if (viewray(v1, odir, &odev.v, | 
| 529 | (dx+.5)/odev.hres, (dy+.5)/odev.vres) < -FTINY) | 
| 530 | return(0);              /* outside view */ | 
| 531 | if (mov | orb) { | 
| 532 | #ifdef DOBJ | 
| 533 | d = dobj_trace(NULL, v1, odir); /* check objects */ | 
| 534 | /* is holodeck in front? */ | 
| 535 | if ((li = smFindSamp(v1, odir)) >= 0 && | 
| 536 | (rsL.wp[li][0] - nv.vp[0])*odir[0] + | 
| 537 | (rsL.wp[li][1] - nv.vp[1])*odir[1] + | 
| 538 | (rsL.wp[li][2] - nv.vp[2])*odir[2] < d) | 
| 539 | VCOPY(wip, rsL.wp[li]); | 
| 540 | else if (d < .99*FHUGE)         /* object is closer */ | 
| 541 | VSUM(wip, nv.vp, odir, d); | 
| 542 | else                            /* nothing visible */ | 
| 543 | return(0); | 
| 544 | #else | 
| 545 | if ((li = smFindSamp(v1, odir)) < 0) | 
| 546 | return(0);      /* not on window */ | 
| 547 | VCOPY(wip, rsL.wp[li]); | 
| 548 | #endif | 
| 549 | VSUM(odir, wip, nv.vp, -1.); | 
| 550 | } else | 
| 551 | VCOPY(nv.vdir, odir); | 
| 552 | if (orb && mov) {               /* orbit left/right */ | 
| 553 | spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov); | 
| 554 | VSUM(nv.vp, wip, odir, -1.); | 
| 555 | spinvector(nv.vdir, nv.vdir, nv.vup, d); | 
| 556 | } else if (orb) {               /* orbit up/down */ | 
| 557 | fcross(v1, odir, nv.vup); | 
| 558 | if (normalize(v1) == 0.) | 
| 559 | return(0); | 
| 560 | spinvector(odir, odir, v1, d=MOVDEG*PI/180.*orb); | 
| 561 | VSUM(nv.vp, wip, odir, -1.); | 
| 562 | spinvector(nv.vdir, nv.vdir, v1, d); | 
| 563 | } else if (mov) {               /* move forward/backward */ | 
| 564 | d = MOVPCT/100. * mov; | 
| 565 | VSUM(nv.vp, nv.vp, odir, d); | 
| 566 | } | 
| 567 | if (!mov ^ !orb && headlocked) {        /* restore head height */ | 
| 568 | VSUM(v1, odev.v.vp, nv.vp, -1.); | 
| 569 | d = DOT(v1, odev.v.vup); | 
| 570 | VSUM(nv.vp, nv.vp, odev.v.vup, d); | 
| 571 | } | 
| 572 | if (setview(&nv) != NULL) | 
| 573 | return(0);      /* illegal view */ | 
| 574 | dev_view(&nv); | 
| 575 | inpresflags |= DFL(DC_SETVIEW); | 
| 576 | return(1); | 
| 577 | } | 
| 578 |  | 
| 579 |  | 
| 580 | static | 
| 581 | getmove(ebut)                           /* get view change */ | 
| 582 | XButtonPressedEvent     *ebut; | 
| 583 | { | 
| 584 | int     movdir = MOVDIR(ebut->button); | 
| 585 | int     movorb = MOVORB(ebut->state); | 
| 586 | int     qlevel = 99; | 
| 587 | time_t  lasttime, thistime; | 
| 588 | int     nframes; | 
| 589 | Window  rootw, childw; | 
| 590 | int     rootx, rooty, wx, wy; | 
| 591 | unsigned int    statemask; | 
| 592 |  | 
| 593 | XNoOp(ourdisplay); | 
| 594 |  | 
| 595 | lasttime = time(0); nframes = 0; | 
| 596 | while (!XCheckMaskEvent(ourdisplay, | 
| 597 | ButtonReleaseMask, levptr(XEvent))) { | 
| 598 |  | 
| 599 | if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw, | 
| 600 | &rootx, &rooty, &wx, &wy, &statemask)) | 
| 601 | break;          /* on another screen */ | 
| 602 |  | 
| 603 | if (!moveview(wx, odev.vres-1-wy, movdir, movorb)) { | 
| 604 | sleep(1); | 
| 605 | lasttime++; | 
| 606 | continue; | 
| 607 | } | 
| 608 | #ifdef STEREO | 
| 609 | pushright(); | 
| 610 | glClear(GL_COLOR_BUFFER_BIT); | 
| 611 | draw_grids(); | 
| 612 | smUpdate(&vwright, qlevel); | 
| 613 | #ifdef DOBJ | 
| 614 | dobj_render(); | 
| 615 | #endif | 
| 616 | popright(); | 
| 617 | #endif | 
| 618 | glClear(GL_COLOR_BUFFER_BIT); | 
| 619 | draw_grids(); | 
| 620 | smUpdate(&odev.v, qlevel); | 
| 621 | #ifdef DOBJ | 
| 622 | dobj_render(); | 
| 623 | #endif | 
| 624 | glFlush(); | 
| 625 | checkglerr("moving view"); | 
| 626 | nframes++; | 
| 627 | thistime = time(0); | 
| 628 | if (thistime - lasttime >= 3 || | 
| 629 | nframes > (int)(3*3*TARGETFPS)) { | 
| 630 | qlevel = thistime<=lasttime ? 1000 : | 
| 631 | (int)((double)nframes/(thistime-lasttime) | 
| 632 | / TARGETFPS * qlevel + 0.5); | 
| 633 | lasttime = thistime; nframes = 0; | 
| 634 | if (qlevel > 99) { | 
| 635 | if (qlevel > 300) {     /* put on the brakes */ | 
| 636 | sleep(1); | 
| 637 | lasttime++; | 
| 638 | } | 
| 639 | qlevel = 99; | 
| 640 | } else if (qlevel < 1) | 
| 641 | qlevel = 1; | 
| 642 | } | 
| 643 | } | 
| 644 | if (!(inpresflags & DFL(DC_SETVIEW))) { /* do final motion */ | 
| 645 | movdir = MOVDIR(levptr(XButtonReleasedEvent)->button); | 
| 646 | wx = levptr(XButtonReleasedEvent)->x; | 
| 647 | wy = levptr(XButtonReleasedEvent)->y; | 
| 648 | moveview(wx, odev.vres-1-wy, movdir, movorb); | 
| 649 | } | 
| 650 | } | 
| 651 |  | 
| 652 |  | 
| 653 | static | 
| 654 | setglpersp(vp)                  /* set perspective view in GL */ | 
| 655 | register VIEW   *vp; | 
| 656 | { | 
| 657 | double  d, xmin, xmax, ymin, ymax; | 
| 658 |  | 
| 659 | if (mindpth >= maxdpth) { | 
| 660 | dev_zmin = 0.1; | 
| 661 | dev_zmax = 100.; | 
| 662 | } else { | 
| 663 | dev_zmin = 0.5*mindpth; | 
| 664 | dev_zmax = 1.5*maxdpth; | 
| 665 | if (dev_zmin > dev_zmax/100.) | 
| 666 | dev_zmin = dev_zmax/100.; | 
| 667 | } | 
| 668 | if (odev.v.vfore > FTINY) | 
| 669 | dev_zmin = odev.v.vfore; | 
| 670 | if (odev.v.vaft > FTINY) | 
| 671 | dev_zmax = odev.v.vaft; | 
| 672 | if (dev_zmin < dev_zmax/5000.) | 
| 673 | dev_zmin = dev_zmax/5000.; | 
| 674 | xmax = dev_zmin * tan(PI/180./2. * odev.v.horiz); | 
| 675 | xmin = -xmax; | 
| 676 | d = odev.v.hoff * (xmax - xmin); | 
| 677 | xmin += d; xmax += d; | 
| 678 | ymax = dev_zmin * tan(PI/180./2. * odev.v.vert); | 
| 679 | ymin = -ymax; | 
| 680 | d = odev.v.voff * (ymax - ymin); | 
| 681 | ymin += d; ymax += d; | 
| 682 | /* set view matrix */ | 
| 683 | glMatrixMode(GL_PROJECTION); | 
| 684 | glLoadIdentity(); | 
| 685 | glFrustum(xmin, xmax, ymin, ymax, dev_zmin, dev_zmax); | 
| 686 | gluLookAt(odev.v.vp[0], odev.v.vp[1], odev.v.vp[2], | 
| 687 | odev.v.vp[0] + odev.v.vdir[0], | 
| 688 | odev.v.vp[1] + odev.v.vdir[1], | 
| 689 | odev.v.vp[2] + odev.v.vdir[2], | 
| 690 | odev.v.vup[0], odev.v.vup[1], odev.v.vup[2]); | 
| 691 | } | 
| 692 |  | 
| 693 |  | 
| 694 | static | 
| 695 | wipeclean()                     /* prepare for redraw */ | 
| 696 | { | 
| 697 | #ifdef STEREO | 
| 698 | setstereobuf(STEREO_BUFFER_RIGHT); | 
| 699 | glClear(GL_DEPTH_BUFFER_BIT); | 
| 700 | setstereobuf(STEREO_BUFFER_LEFT); | 
| 701 | #endif | 
| 702 | glClear(GL_DEPTH_BUFFER_BIT); | 
| 703 | smClean(); | 
| 704 | } | 
| 705 |  | 
| 706 |  | 
| 707 | static | 
| 708 | getkey(ekey)                            /* get input key */ | 
| 709 | register XKeyPressedEvent  *ekey; | 
| 710 | { | 
| 711 | int  n; | 
| 712 | char    buf[8]; | 
| 713 |  | 
| 714 | n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL); | 
| 715 | if (n != 1) | 
| 716 | return; | 
| 717 | switch (buf[0]) { | 
| 718 | case 'h':                       /* turn on height motion lock */ | 
| 719 | headlocked = 1; | 
| 720 | return; | 
| 721 | case 'H':                       /* turn off height motion lock */ | 
| 722 | headlocked = 0; | 
| 723 | return; | 
| 724 | case 'l':                       /* retrieve last view */ | 
| 725 | inpresflags |= DFL(DC_LASTVIEW); | 
| 726 | return; | 
| 727 | case 'p':                       /* pause computation */ | 
| 728 | inpresflags |= DFL(DC_PAUSE); | 
| 729 | return; | 
| 730 | case 'v':                       /* spit out view */ | 
| 731 | inpresflags |= DFL(DC_GETVIEW); | 
| 732 | return; | 
| 733 | case '\n': | 
| 734 | case '\r':                      /* resume computation */ | 
| 735 | inpresflags |= DFL(DC_RESUME); | 
| 736 | return; | 
| 737 | case CTRL('R'):                 /* redraw screen */ | 
| 738 | wipeclean(); | 
| 739 | return; | 
| 740 | case CTRL('L'):                 /* refresh from server */ | 
| 741 | if (inpresflags & DFL(DC_REDRAW)) | 
| 742 | return; | 
| 743 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
| 744 | glDisable(GL_DEPTH_TEST); | 
| 745 | draw_grids(); | 
| 746 | #ifdef STEREO | 
| 747 | pushright(); | 
| 748 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | 
| 749 | draw_grids(); | 
| 750 | popright(); | 
| 751 | #endif | 
| 752 | glEnable(GL_DEPTH_TEST); | 
| 753 | glFlush(); | 
| 754 | smInit(rsL.max_samp);           /* get rid of old values */ | 
| 755 | inpresflags |= DFL(DC_REDRAW);  /* resend values from server */ | 
| 756 | rayqleft = 0;                   /* hold off update */ | 
| 757 | return; | 
| 758 | case 'K':                       /* kill rtrace process(es) */ | 
| 759 | inpresflags |= DFL(DC_KILL); | 
| 760 | break; | 
| 761 | case 'R':                       /* restart rtrace */ | 
| 762 | inpresflags |= DFL(DC_RESTART); | 
| 763 | break; | 
| 764 | case 'C':                       /* clobber holodeck */ | 
| 765 | inpresflags |= DFL(DC_CLOBBER); | 
| 766 | break; | 
| 767 | case 'q':                       /* quit the program */ | 
| 768 | inpresflags |= DFL(DC_QUIT); | 
| 769 | return; | 
| 770 | default: | 
| 771 | XBell(ourdisplay, 0); | 
| 772 | return; | 
| 773 | } | 
| 774 | } | 
| 775 |  | 
| 776 |  | 
| 777 | static | 
| 778 | fixwindow(eexp)                         /* repair damage to window */ | 
| 779 | register XExposeEvent  *eexp; | 
| 780 | { | 
| 781 | if (odev.hres == 0 | odev.vres == 0) {  /* first exposure */ | 
| 782 | resizewindow((XConfigureEvent *)eexp); | 
| 783 | return; | 
| 784 | } | 
| 785 | if (eexp->count)                /* wait for final exposure */ | 
| 786 | return; | 
| 787 | wipeclean();                    /* clear depth */ | 
| 788 | } | 
| 789 |  | 
| 790 |  | 
| 791 | static | 
| 792 | resizewindow(ersz)                      /* resize window */ | 
| 793 | register XConfigureEvent  *ersz; | 
| 794 | { | 
| 795 | glViewport(0, 0, ersz->width, ersz->height); | 
| 796 |  | 
| 797 | if (ersz->width == odev.hres && ersz->height == odev.vres) | 
| 798 | return; | 
| 799 |  | 
| 800 | odev.hres = ersz->width; | 
| 801 | odev.vres = ersz->height; | 
| 802 |  | 
| 803 | odev.v.horiz = 2.*180./PI * atan(0.5/VIEWDIST*pwidth*odev.hres); | 
| 804 | odev.v.vert = 2.*180./PI * atan(0.5/VIEWDIST*pheight*odev.vres); | 
| 805 |  | 
| 806 | inpresflags |= DFL(DC_SETVIEW); | 
| 807 | } |