ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/hd/rhd_glx.c
Revision: 3.2
Committed: Wed Dec 24 14:06:40 1997 UTC (26 years, 3 months ago) by gregl
Content type: text/plain
Branch: MAIN
Changes since 3.1: +227 -98 lines
Log Message:
first fully working version

File Contents

# Content
1 /* Copyright (c) 1997 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 x11 driver.
10 */
11
12 #include "standard.h"
13 #include "tonemap.h"
14 #include "rhdriver.h"
15
16 #include <GL/glx.h>
17
18 #include "x11icon.h"
19
20 #ifndef FEQ
21 #define FEQ(a,b) ((a)-(b) <= FTINY && (a)-(b) >= -FTINY)
22 #endif
23
24 #ifndef int4
25 #define int4 int
26 #endif
27
28 #ifndef FREEPCT
29 #define FREEPCT 10 /* percentage of values to free */
30 #endif
31
32 #ifndef NCONEV
33 #define NCONEV 7 /* number of cone base vertices */
34 #endif
35 #ifndef CONEH
36 #define CONEH 5. /* cone height (fraction of depth) */
37 #endif
38 #ifndef CONEW
39 #define CONEW 0.07 /* cone width (fraction of screen) */
40 #endif
41 #ifndef DIRPEN
42 #define DIRPEN 0.001 /* direction penalty factor */
43 #endif
44
45 #define GAMMA 1.4 /* default gamma correction */
46
47 #define MOVPCT 7 /* percent distance to move /frame */
48 #define MOVDIR(b) ((b)==Button1 ? 1 : (b)==Button2 ? 0 : -1)
49 #define MOVDEG (-5) /* degrees to orbit CW/down /frame */
50 #define MOVORB(s) ((s)&ShiftMask ? 1 : (s)&ControlMask ? -1 : 0)
51
52 #define MINWIDTH 480 /* minimum graphics window width */
53 #define MINHEIGHT 400 /* minimum graphics window height */
54
55 #define VIEWDIST 356 /* assumed viewing distance (mm) */
56
57 #define BORWIDTH 5 /* border width */
58
59 #define ourscreen DefaultScreen(ourdisplay)
60 #define ourroot RootWindow(ourdisplay,ourscreen)
61 #define ourmask (StructureNotifyMask|ExposureMask|KeyPressMask|\
62 ButtonPressMask|ButtonReleaseMask)
63
64 #define levptr(etype) ((etype *)&currentevent)
65
66 struct driver odev; /* global device driver structure */
67
68 static XEvent currentevent; /* current event */
69
70 static int mapped = 0; /* window is mapped? */
71 static unsigned long ourblack=0, ourwhite=~0;
72
73 static Display *ourdisplay = NULL; /* our display */
74 static XVisualInfo *ourvinf; /* our visual information */
75 static Window gwind = 0; /* our graphics window */
76 static GLXContext gctx; /* our GLX context */
77
78 static double mindepth = FHUGE; /* minimum depth value so far */
79 static double maxdepth = 0.; /* maximum depth value so far */
80
81 static double pwidth, pheight; /* pixel dimensions (mm) */
82
83 static FVECT conev[NCONEV]; /* drawing cone */
84 static double coneh; /* cone height */
85
86 static int inpresflags; /* input result flags */
87
88 static int headlocked = 0; /* lock vertical motion */
89
90 static struct {
91 float (*wp)[3]; /* world intersection point array */
92 int4 *wd; /* world direction array */
93 TMbright *brt; /* encoded brightness array */
94 BYTE (*chr)[3]; /* encoded chrominance array */
95 BYTE (*rgb)[3]; /* tone-mapped color array */
96 BYTE *alpha; /* alpha values */
97 int nl; /* count of values */
98 int bl, tl; /* bottom and top (next) value index */
99 int tml; /* next value needing tone-mapping */
100 int drl; /* next value in need of drawing */
101 char *base; /* base of allocated memory */
102 } rV; /* our collection of values */
103
104 static int *valmap = NULL; /* sorted map of screen values */
105 static int vmaplen = 0; /* value map length */
106
107 #define redraw() (rV.drl = rV.bl)
108
109 static int resizewindow(), getevent(), getkey(), moveview(),
110 setGLview(), getmove(), fixwindow(), mytmflags(),
111 drawvalue(), valcmp(), clralphas(), setalphas(), mergalphas(),
112 IndexValue(), Compost(), FindValue(), TMapValues(),
113 AllocValues(), FreeValues();
114
115 extern int4 encodedir();
116 extern double fdir2diff(), dir2diff();
117
118
119 dev_open(id) /* initialize X11 driver */
120 char *id;
121 {
122 extern char *getenv();
123 static int atlBest[] = {GLX_RGBA, GLX_RED_SIZE,8,
124 GLX_GREEN_SIZE,8, GLX_BLUE_SIZE,8,
125 GLX_ALPHA_SIZE,8, GLX_DEPTH_SIZE,15,
126 None};
127 char *gv;
128 double gamval = GAMMA;
129 XSetWindowAttributes ourwinattr;
130 XWMHints ourxwmhints;
131 XSizeHints oursizhints;
132 /* open display server */
133 ourdisplay = XOpenDisplay(NULL);
134 if (ourdisplay == NULL)
135 error(USER, "cannot open X-windows; DISPLAY variable set?\n");
136 /* find a usable visual */
137 ourvinf = glXChooseVisual(ourdisplay, ourscreen, atlBest);
138 if (ourvinf == NULL)
139 error(USER, "no suitable visuals available");
140 /* get a context */
141 gctx = glXCreateContext(ourdisplay, ourvinf, NULL, GL_TRUE);
142 /* set gamma and tone mapping */
143 if ((gv = XGetDefault(ourdisplay, "radiance", "gamma")) != NULL
144 || (gv = getenv("DISPLAY_GAMMA")) != NULL)
145 gamval = atof(gv);
146 if (tmInit(mytmflags(), stdprims, gamval) == NULL)
147 error(SYSTEM, "not enough memory in dev_open");
148 /* open window */
149 ourwinattr.background_pixel = ourblack;
150 ourwinattr.border_pixel = ourblack;
151 ourwinattr.event_mask = ourmask;
152 /* this is stupid */
153 ourwinattr.colormap = XCreateColormap(ourdisplay, ourroot,
154 ourvinf->visual, AllocNone);
155 gwind = XCreateWindow(ourdisplay, ourroot, 0, 0,
156 DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH,
157 DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH,
158 BORWIDTH, ourvinf->depth, InputOutput, ourvinf->visual,
159 CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &ourwinattr);
160 if (gwind == 0)
161 error(SYSTEM, "cannot create window\n");
162 XStoreName(ourdisplay, gwind, id);
163 /* set window manager hints */
164 ourxwmhints.flags = InputHint|IconPixmapHint;
165 ourxwmhints.input = True;
166 ourxwmhints.icon_pixmap = XCreateBitmapFromData(ourdisplay,
167 gwind, x11icon_bits, x11icon_width, x11icon_height);
168 XSetWMHints(ourdisplay, gwind, &ourxwmhints);
169 oursizhints.min_width = MINWIDTH;
170 oursizhints.min_height = MINHEIGHT;
171 oursizhints.flags = PMinSize;
172 XSetNormalHints(ourdisplay, gwind, &oursizhints);
173 /* set GLX context */
174 glXMakeCurrent(ourdisplay, gwind, gctx);
175 glEnable(GL_DEPTH_TEST);
176 glDepthFunc(GL_LEQUAL);
177 glShadeModel(GL_FLAT);
178 glDisable(GL_DITHER);
179 /* figure out sensible view */
180 pwidth = (double)DisplayWidthMM(ourdisplay, ourscreen) /
181 DisplayWidth(ourdisplay, ourscreen);
182 pheight = (double)DisplayHeightMM(ourdisplay, ourscreen) /
183 DisplayHeight(ourdisplay, ourscreen);
184 copystruct(&odev.v, &stdview);
185 odev.v.type = VT_PER;
186 /* map the window */
187 XMapWindow(ourdisplay, gwind);
188 dev_input(); /* sets size and view angles */
189 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
190 /* allocate our value list */
191 if (!AllocValues(DisplayWidth(ourdisplay,ourscreen) *
192 DisplayHeight(ourdisplay,ourscreen) / 4))
193 error(SYSTEM, "insufficient memory for value storage");
194 odev.name = id;
195 odev.ifd = ConnectionNumber(ourdisplay);
196 }
197
198
199 dev_close() /* close our display and free resources */
200 {
201 glXMakeCurrent(ourdisplay, None, NULL);
202 glXDestroyContext(ourdisplay, gctx);
203 XDestroyWindow(ourdisplay, gwind);
204 gwind = 0;
205 XCloseDisplay(ourdisplay);
206 ourdisplay = NULL;
207 tmDone(NULL);
208 FreeValues();
209 odev.v.type = 0;
210 odev.hres = odev.vres = 0;
211 odev.ifd = -1;
212 }
213
214
215 int
216 dev_view(nv) /* assign new driver view */
217 register VIEW *nv;
218 {
219 if (nv->type != VT_PER || /* check view legality */
220 nv->horiz > 120. || nv->vert > 120.) {
221 error(COMMAND, "illegal view type/angle");
222 nv->type = VT_PER;
223 nv->horiz = odev.v.horiz;
224 nv->vert = odev.v.vert;
225 return(0);
226 }
227 if (nv != &odev.v) {
228 if (!FEQ(nv->horiz,odev.v.horiz) || /* resize window? */
229 !FEQ(nv->vert,odev.v.vert)) {
230 int dw = DisplayWidth(ourdisplay,ourscreen);
231 int dh = DisplayHeight(ourdisplay,ourscreen);
232
233 dw -= 25; /* for window frame */
234 dh -= 50;
235 odev.hres = 2.*VIEWDIST/pwidth *
236 tan(PI/180./2.*nv->horiz);
237 odev.vres = 2.*VIEWDIST/pheight *
238 tan(PI/180./2.*nv->vert);
239 if (odev.hres > dw) {
240 odev.vres = dw * odev.vres / odev.hres;
241 odev.hres = dw;
242 }
243 if (odev.vres > dh) {
244 odev.hres = dh * odev.hres / odev.vres;
245 odev.vres = dh;
246 }
247 XResizeWindow(ourdisplay, gwind, odev.hres, odev.vres);
248 dev_input(); /* get resize event */
249 }
250 copystruct(&odev.v, nv);
251 setGLview();
252 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
253 redraw();
254 }
255 return(1);
256 }
257
258
259 int
260 dev_input() /* get X11 input */
261 {
262 inpresflags = 0;
263
264 do
265 getevent();
266
267 while (XQLength(ourdisplay) > 0);
268
269 return(inpresflags);
270 }
271
272
273 dev_value(c, p, v) /* add a pixel value to our list */
274 COLR c;
275 FVECT p, v;
276 {
277 register int li;
278
279 li = rV.tl++;
280 if (rV.tl >= rV.nl) /* get next leaf in ring */
281 rV.tl = 0;
282 if (rV.tl == rV.bl) /* need to shake some free */
283 Compost(FREEPCT);
284 VCOPY(rV.wp[li], p);
285 rV.wd[li] = encodedir(v);
286 tmCvColrs(&rV.brt[li], rV.chr[li], c, 1);
287 }
288
289
290 int
291 dev_flush() /* flush output */
292 {
293 if (mapped) {
294 TMapValues(0);
295 while (rV.drl != rV.tl) {
296 drawvalue(rV.drl);
297 if (++rV.drl >= rV.nl)
298 rV.drl = 0;
299 }
300 glFlush();
301 }
302 return(XPending(ourdisplay));
303 }
304
305
306 static int
307 mytmflags() /* figure out tone mapping flags */
308 {
309 extern char *progname;
310 register char *cp, *tail;
311 /* find basic name */
312 for (cp = tail = progname; *cp; cp++)
313 if (*cp == '/')
314 tail = cp+1;
315 for (cp = tail; *cp && *cp != '.'; cp++)
316 ;
317 if (cp-tail == 3 && !strncmp(tail, "glx", 3))
318 return(TM_F_CAMERA);
319 if (cp-tail == 4 && !strncmp(tail, "glxh", 4))
320 return(TM_F_HUMAN);
321 error(USER, "illegal driver name");
322 }
323
324
325 static
326 setGLview() /* set our GL view */
327 {
328 double xmin, xmax, ymin, ymax, zmin, zmax;
329 double d, cx, sx, crad;
330 FVECT vx, vy;
331 register int i, j;
332 /* compute view frustum */
333 if (normalize(odev.v.vdir) == 0.0)
334 return;
335 if (mindepth < maxdepth) {
336 zmin = 0.25*mindepth;
337 zmax = 4.0*(1.+CONEH)*maxdepth;
338 } else {
339 zmin = 0.01;
340 zmax = 1000.;
341 }
342 if (odev.v.vfore > FTINY)
343 zmin = odev.v.vfore;
344 if (odev.v.vaft > FTINY)
345 zmax = odev.v.vaft;
346 xmax = zmin * tan(PI/180./2. * odev.v.horiz);
347 xmin = -xmax;
348 d = odev.v.hoff * (xmax - xmin);
349 xmin += d; xmax += d;
350 ymax = zmin * tan(PI/180./2. * odev.v.vert);
351 ymin = -ymax;
352 d = odev.v.voff * (ymax - ymin);
353 ymin += d; ymax += d;
354 /* set view matrix */
355 glMatrixMode(GL_PROJECTION);
356 glLoadIdentity();
357 glFrustum(xmin, xmax, ymin, ymax, zmin, zmax);
358 gluLookAt(odev.v.vp[0], odev.v.vp[1], odev.v.vp[2],
359 odev.v.vp[0] + odev.v.vdir[0],
360 odev.v.vp[1] + odev.v.vdir[1],
361 odev.v.vp[2] + odev.v.vdir[2],
362 odev.v.vup[0], odev.v.vup[1], odev.v.vup[2]);
363 /* set viewport */
364 glViewport(0, 0, odev.hres, odev.vres);
365 /* initialize cone for Vornoi polys */
366 coneh = CONEH*(zmax - zmin);
367 crad = 0.5 * CONEW * 0.5*(xmax-xmin + ymax-ymin) * (zmin+coneh)/zmin;
368 vy[0] = vy[1] = vy[2] = 0.;
369 for (i = 0; i < 3; i++)
370 if (odev.v.vdir[i] < 0.6 && odev.v.vdir[i] > -0.6)
371 break;
372 vy[i] = 1.;
373 fcross(vx, vy, odev.v.vdir);
374 normalize(vx);
375 fcross(vy, odev.v.vdir, vx);
376 for (j = 0, d = 0.; j < NCONEV; j++, d += 2.*PI/NCONEV) {
377 cx = crad*cos(d); sx = crad*sin(d);
378 for (i = 0; i < 3; i++)
379 conev[j][i] = coneh*odev.v.vdir[i] +
380 cx*vx[i] + sx*vy[i];
381 }
382 }
383
384
385 static
386 drawvalue(li) /* draw a pixel value as a cone */
387 register int li;
388 {
389 static FVECT disp;
390 FVECT apex;
391 double d, dorg, dnew, h, v;
392 register int i;
393 /* compute cone coordinates */
394 disp[0] = rV.wp[li][0] - odev.v.vp[0];
395 disp[1] = rV.wp[li][1] - odev.v.vp[1];
396 disp[2] = rV.wp[li][2] - odev.v.vp[2];
397 dorg = DOT(disp,odev.v.vdir);
398 if (dorg <= odev.v.vfore)
399 return; /* clipped too near */
400 if (odev.v.vaft > FTINY && dorg > odev.v.vaft)
401 return; /* clipped too far */
402 if (dorg > 1e5) { /* background pixel */
403 dnew = maxdepth;
404 d = dnew/dorg;
405 } else { /* foreground pixel, compute penalty */
406 normalize(disp);
407 d = dnew = dorg + coneh*fdir2diff(rV.wd[li],disp)*DIRPEN;
408 }
409 /* compute adjusted apex position */
410 disp[0] *= d; disp[1] *= d; disp[2] *= d;
411 apex[0] = odev.v.vp[0] + disp[0];
412 apex[1] = odev.v.vp[1] + disp[1];
413 apex[2] = odev.v.vp[2] + disp[2];
414 /* compute view position and base offset */
415 h = DOT(disp,odev.v.hvec)/(dnew*odev.v.hn2);
416 v = DOT(disp,odev.v.vvec)/(dnew*odev.v.vn2);
417 if (fabs(h - odev.v.hoff) > 0.5 || fabs(v - odev.v.voff) > 0.5)
418 return; /* clipped off screen */
419 if (dorg < mindepth)
420 mindepth = dorg;
421 if (dorg > maxdepth)
422 maxdepth = dorg;
423 for (i = 0; i < 3; i++)
424 disp[i] = apex[i] + coneh*(h*odev.v.hvec[i] + v*odev.v.vvec[i]);
425 /* draw cone (pyramid approx.) */
426 glColor4ub(rV.rgb[li][0], rV.rgb[li][1], rV.rgb[li][2], rV.alpha[li]);
427 glBegin(GL_TRIANGLE_FAN);
428 glVertex3f(apex[0], apex[1], apex[2]);
429 for (i = 0; i < NCONEV; i++)
430 glVertex3d(conev[i][0] + disp[0], conev[i][1] + disp[1],
431 conev[i][2] + disp[2]);
432 /* connect last face to first */
433 glVertex3d(conev[0][0] + disp[0], conev[0][1] + disp[1],
434 conev[0][2] + disp[2]);
435 glEnd(); /* done */
436 }
437
438
439 #define LEAFSIZ (3*sizeof(float)+sizeof(int4)+\
440 sizeof(TMbright)+7*sizeof(BYTE))
441
442 static
443 AllocValues(n) /* allocate space for n values */
444 register int n;
445 {
446 unsigned nbytes;
447 register unsigned i;
448
449 if (n <= 0)
450 return(0);
451 if (rV.nl >= n)
452 return(rV.nl);
453 else if (rV.nl > 0)
454 free(rV.base);
455 /* round space up to nearest power of 2 */
456 nbytes = n*LEAFSIZ + 8;
457 for (i = 1024; nbytes > i; i <<= 1)
458 ;
459 n = (i - 8) / LEAFSIZ; /* should we make sure n is even? */
460 rV.base = (char *)malloc(n*LEAFSIZ);
461 if (rV.base == NULL)
462 return(0);
463 /* assign larger alignment types earlier */
464 rV.wp = (float (*)[3])rV.base;
465 rV.wd = (int4 *)(rV.wp + n);
466 rV.brt = (TMbright *)(rV.wd + n);
467 rV.chr = (BYTE (*)[3])(rV.brt + n);
468 rV.rgb = (BYTE (*)[3])(rV.chr + n);
469 rV.alpha = (BYTE *)(rV.rgb + n);
470 rV.nl = n;
471 rV.drl = rV.tml = rV.bl = rV.tl = 0;
472 return(n);
473 }
474
475 #undef LEAFSIZ
476
477
478 static
479 FreeValues() /* free our allocated values */
480 {
481 if (rV.nl <= 0)
482 return;
483 free(rV.base);
484 rV.base = NULL;
485 rV.nl = 0;
486 }
487
488
489 static
490 clralphas() /* prepare for new alpha values */
491 {
492 if (!vmaplen)
493 return;
494 free((char *)valmap);
495 valmap = NULL;
496 vmaplen = 0;
497 }
498
499
500 static int
501 valcmp(v1p, v2p) /* compare two pixel values */
502 int *v1p, *v2p;
503 {
504 register int v1 = *v1p, v2 = *v2p;
505 register int c;
506
507 if ((c = rV.rgb[v1][0] - rV.rgb[v2][0])) return(c);
508 if ((c = rV.rgb[v1][1] - rV.rgb[v2][1])) return(c);
509 if ((c = rV.rgb[v1][2] - rV.rgb[v2][2])) return(c);
510 return(rV.alpha[v1] - rV.alpha[v2]);
511 }
512
513
514 static
515 mergalphas(adest, al1, n1, al2, n2) /* merge two sorted alpha lists */
516 register int *adest, *al1, *al2;
517 int n1, n2;
518 {
519 register int cmp;
520
521 while (n1 | n2) {
522 if (!n1) cmp = 1;
523 else if (!n2) cmp = -1;
524 else cmp = valcmp(al1, al2);
525 if (cmp > 0) {
526 *adest++ = *al2++;
527 n2--;
528 } else {
529 *adest++ = *al1++;
530 n1--;
531 }
532 }
533 }
534
535
536 static
537 setalphas(vbeg, nvals) /* add values to our map and set alphas */
538 int vbeg, nvals;
539 {
540 register int *newmap;
541 short ccmp[3], lastalpha;
542 int newmaplen;
543
544 if (nvals <= 0)
545 return;
546 newmaplen = vmaplen + nvals; /* allocate new map */
547 newmap = (int *)malloc(newmaplen*sizeof(int));
548 if (newmap == NULL)
549 error(SYSTEM, "out of memory in setalphas");
550 while (nvals--) { /* add new values to end */
551 rV.alpha[vbeg] = 255;
552 newmap[vmaplen+nvals] = vbeg++;
553 }
554 if (nvals >= 3*vmaplen) { /* resort the combined array */
555 while (vmaplen--)
556 newmap[vmaplen] = valmap[vmaplen];
557 qsort((char *)newmap, newmaplen, sizeof(int), valcmp);
558 } else { /* perform merge sort */
559 qsort((char *)(newmap+vmaplen), newmaplen-vmaplen,
560 sizeof(int), valcmp);
561 mergalphas(newmap, valmap, vmaplen,
562 newmap+vmaplen, newmaplen-vmaplen);
563 }
564 if (valmap != NULL) /* free old map and assign new one */
565 free((char *)valmap);
566 valmap = newmap;
567 vmaplen = newmaplen;
568 lastalpha = 0; /* set new alpha values */
569 ccmp[0] = ccmp[1] = ccmp[2] = 256;
570 while (newmaplen--)
571 if (rV.rgb[*newmap][0] == ccmp[0] &&
572 rV.rgb[*newmap][1] == ccmp[1] &&
573 rV.rgb[*newmap][2] == ccmp[2]) {
574 if (lastalpha >= 255)
575 newmap++;
576 else if (rV.alpha[*newmap] < 255)
577 lastalpha = rV.alpha[*newmap++];
578 else
579 rV.alpha[*newmap++] = ++lastalpha;
580 } else {
581 ccmp[0] = rV.rgb[*newmap][0];
582 ccmp[1] = rV.rgb[*newmap][1];
583 ccmp[2] = rV.rgb[*newmap][2];
584 if (rV.alpha[*newmap] < 255)
585 lastalpha = rV.alpha[*newmap++];
586 else
587 rV.alpha[*newmap++] = lastalpha = 1;
588 }
589 }
590
591
592 static
593 TMapValues(redo) /* map our values to RGB */
594 int redo;
595 {
596 int aorg, alen, borg, blen;
597 /* recompute mapping? */
598 if (redo)
599 rV.tml = rV.bl;
600 /* already done? */
601 if (rV.tml == rV.tl)
602 return(1);
603 /* compute segments */
604 aorg = rV.tml;
605 if (rV.tl >= aorg) {
606 alen = rV.tl - aorg;
607 blen = 0;
608 } else {
609 alen = rV.nl - aorg;
610 borg = 0;
611 blen = rV.tl;
612 }
613 /* (re)compute tone mapping? */
614 if (rV.tml == rV.bl) {
615 tmClearHisto();
616 tmAddHisto(rV.brt+aorg, alen, 1);
617 if (blen > 0)
618 tmAddHisto(rV.brt+borg, blen, 1);
619 if (tmComputeMapping(0., 0., 0.) != TM_E_OK)
620 return(0);
621 clralphas(); /* restart value list */
622 rV.drl = rV.bl; /* need to redraw */
623 }
624 if (tmMapPixels(rV.rgb+aorg, rV.brt+aorg,
625 rV.chr+aorg, alen) != TM_E_OK)
626 return(0);
627 if (blen > 0)
628 tmMapPixels(rV.rgb+borg, rV.brt+borg,
629 rV.chr+borg, blen);
630 setalphas(aorg, alen); /* compute add'l alpha values */
631 if (blen > 0)
632 setalphas(borg, blen);
633 rV.tml = rV.tl; /* we're all up to date */
634 return(1);
635 }
636
637
638 static int
639 Compost(pct) /* free up some values */
640 int pct;
641 {
642 int nused, nclear, nmapped, ndrawn;
643 /* figure out how many values to clear */
644 nclear = rV.nl * pct / 100;
645 nused = rV.tl - rV.bl;
646 if (nused <= 0) nused += rV.nl;
647 nclear -= rV.nl - nused;
648 if (nclear <= 0)
649 return(0);
650 if (nclear >= nused) { /* clear them all? */
651 rV.drl = rV.tml = rV.bl = rV.tl = 0;
652 return(nused);
653 }
654 /* else clear values from bottom */
655 ndrawn = rV.drl - rV.bl;
656 if (ndrawn < 0) ndrawn += rV.nl;
657 nmapped = rV.tml - rV.bl;
658 if (nmapped < 0) nmapped += rV.nl;
659 rV.bl += nclear;
660 if (rV.bl >= rV.nl) rV.bl -= rV.nl;
661 if (ndrawn < nclear) rV.drl = rV.bl;
662 if (nmapped < nclear) rV.tml = rV.bl;
663 return(nclear);
664 }
665
666
667 static
668 getevent() /* get next event */
669 {
670 XNextEvent(ourdisplay, levptr(XEvent));
671 switch (levptr(XEvent)->type) {
672 case ConfigureNotify:
673 resizewindow(levptr(XConfigureEvent));
674 break;
675 case UnmapNotify:
676 mapped = 0;
677 break;
678 case MapNotify:
679 mapped = 1;
680 break;
681 case Expose:
682 fixwindow(levptr(XExposeEvent));
683 break;
684 case KeyPress:
685 getkey(levptr(XKeyPressedEvent));
686 break;
687 case ButtonPress:
688 getmove(levptr(XButtonPressedEvent));
689 break;
690 }
691 }
692
693
694 static
695 draw3dline(wp) /* draw 3d line in world coordinates */
696 register FVECT wp[2];
697 {
698 glVertex3d(wp[0][0], wp[0][1], wp[0][2]);
699 glVertex3d(wp[1][0], wp[1][1], wp[1][2]);
700 }
701
702
703 static
704 draw_grids() /* draw holodeck section grids */
705 {
706 static BYTE gridrgba[4] = {0x0, 0xff, 0xff, 0x00};
707
708 if (!mapped)
709 return;
710 glColor4ub(gridrgba[0], gridrgba[1], gridrgba[2], gridrgba[3]);
711 /* draw each grid line */
712 glBegin(GL_LINES);
713 gridlines(draw3dline);
714 glEnd();
715 }
716
717
718 static int
719 IndexValue(rgba) /* locate a pixel by it's framebuffer value */
720 register BYTE rgba[4];
721 {
722 register int *vp;
723 /* check legality */
724 if (rgba[3] == 0 || rgba[3] == 255)
725 return(-1);
726 /* borrow a value slot */
727 rV.rgb[rV.tl][0] = rgba[0];
728 rV.rgb[rV.tl][1] = rgba[1];
729 rV.rgb[rV.tl][2] = rgba[2];
730 rV.alpha[rV.tl] = rgba[3];
731 /* find it */
732 vp = (int *)bsearch((char *)&rV.tl, (char *)valmap, vmaplen,
733 sizeof(int), valcmp);
734 if (vp == NULL)
735 return(-1);
736 return(*vp);
737 }
738
739
740 static int
741 FindValue(dx, dy) /* find a value on the display */
742 int dx, dy;
743 {
744 BYTE rgba[4];
745
746 if (dx < 0 || dy < 0 || dx >= odev.hres || dy >= odev.vres)
747 return(-1);
748 glReadPixels(dx, dy, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
749 return(IndexValue(rgba));
750 }
751
752
753 static
754 moveview(dx, dy, mov, orb) /* move our view */
755 int dx, dy, mov, orb;
756 {
757 VIEW nv;
758 FVECT odir, v1;
759 double d;
760 register int li;
761 /* start with old view */
762 copystruct(&nv, &odev.v);
763 /* change view direction */
764 if (mov | orb) {
765 if ((li = FindValue(dx, dy)) < 0)
766 return(0); /* not on window */
767 VSUM(odir, rV.wp[li], nv.vp, -1.);
768 } else {
769 if (viewray(nv.vp, nv.vdir, &odev.v,
770 (dx+.5)/odev.hres, (dy+.5)/odev.vres) < -FTINY)
771 return(0); /* outside view */
772 }
773 if (orb && mov) { /* orbit left/right */
774 spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov);
775 VSUM(nv.vp, rV.wp[li], odir, -1.);
776 spinvector(nv.vdir, nv.vdir, nv.vup, d);
777 } else if (orb) { /* orbit up/down */
778 fcross(v1, odir, nv.vup);
779 if (normalize(v1) == 0.)
780 return(0);
781 spinvector(odir, odir, v1, d=MOVDEG*PI/180.*orb);
782 VSUM(nv.vp, rV.wp[li], odir, -1.);
783 spinvector(nv.vdir, nv.vdir, v1, d);
784 } else if (mov) { /* move forward/backward */
785 d = MOVPCT/100. * mov;
786 VSUM(nv.vp, nv.vp, odir, d);
787 }
788 if (!mov ^ !orb && headlocked) { /* restore head height */
789 VSUM(v1, odev.v.vp, nv.vp, -1.);
790 d = DOT(v1, odev.v.vup);
791 VSUM(nv.vp, nv.vp, odev.v.vup, d);
792 }
793 if (setview(&nv) != NULL)
794 return(0); /* illegal view */
795 dev_view(&nv);
796 inpresflags |= DFL(DC_SETVIEW);
797 return(1);
798 }
799
800
801 static
802 getmove(ebut) /* get view change */
803 XButtonPressedEvent *ebut;
804 {
805 int movdir = MOVDIR(ebut->button);
806 int movorb = MOVORB(ebut->state);
807 Window rootw, childw;
808 int rootx, rooty, wx, wy;
809 unsigned int statemask;
810
811 XNoOp(ourdisplay);
812
813 while (!XCheckMaskEvent(ourdisplay,
814 ButtonReleaseMask, levptr(XEvent))) {
815
816 if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw,
817 &rootx, &rooty, &wx, &wy, &statemask))
818 break; /* on another screen */
819
820 if (!moveview(wx, odev.vres-1-wy, movdir, movorb)) {
821 sleep(1);
822 continue;
823 }
824 draw_grids();
825 dev_flush();
826 }
827 if (!(inpresflags & DFL(DC_SETVIEW))) { /* do final motion */
828 movdir = MOVDIR(levptr(XButtonReleasedEvent)->button);
829 wx = levptr(XButtonReleasedEvent)->x;
830 wy = levptr(XButtonReleasedEvent)->y;
831 moveview(wx, odev.vres-1-wy, movdir, movorb);
832 } else {
833 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
834 redraw();
835 }
836 dev_flush();
837 }
838
839
840 static
841 getkey(ekey) /* get input key */
842 register XKeyPressedEvent *ekey;
843 {
844 int n;
845 char buf[8];
846
847 n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL);
848 if (n != 1)
849 return;
850 switch (buf[0]) {
851 case 'h': /* turn on height motion lock */
852 headlocked = 1;
853 return;
854 case 'H': /* turn off height motion lock */
855 headlocked = 0;
856 return;
857 case 'l': /* retrieve last view */
858 inpresflags |= DFL(DC_LASTVIEW);
859 return;
860 case 'p': /* pause computation */
861 inpresflags |= DFL(DC_PAUSE);
862 return;
863 case 'v': /* spit out view */
864 inpresflags |= DFL(DC_GETVIEW);
865 return;
866 case '\n':
867 case '\r': /* resume computation */
868 inpresflags |= DFL(DC_RESUME);
869 return;
870 case CTRL('R'): /* redraw screen */
871 TMapValues(1);
872 glClear(GL_DEPTH_BUFFER_BIT);
873 redraw();
874 return;
875 case CTRL('L'): /* refresh from server */
876 if (inpresflags & DFL(DC_REDRAW))
877 return;
878 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
879 glDisable(GL_DEPTH_TEST);
880 draw_grids();
881 glEnable(GL_DEPTH_TEST);
882 glFlush();
883 Compost(100); /* get rid of old values */
884 inpresflags |= DFL(DC_REDRAW); /* resend values from server */
885 return;
886 case 'K': /* kill rtrace process(es) */
887 inpresflags |= DFL(DC_KILL);
888 break;
889 case 'R': /* restart rtrace */
890 inpresflags |= DFL(DC_RESTART);
891 break;
892 case 'C': /* clobber holodeck */
893 inpresflags |= DFL(DC_CLOBBER);
894 break;
895 case 'q': /* quit the program */
896 inpresflags |= DFL(DC_QUIT);
897 return;
898 default:
899 XBell(ourdisplay, 0);
900 return;
901 }
902 }
903
904
905 static
906 fixwindow(eexp) /* repair damage to window */
907 register XExposeEvent *eexp;
908 {
909 if (odev.hres == 0 || odev.vres == 0) /* first exposure */
910 resizewindow((XConfigureEvent *)eexp);
911 if (eexp->width == odev.hres && eexp->height == odev.vres)
912 TMapValues(1);
913 if (!eexp->count) {
914 glClear(GL_DEPTH_BUFFER_BIT);
915 redraw();
916 }
917 }
918
919
920 static
921 resizewindow(ersz) /* resize window */
922 register XConfigureEvent *ersz;
923 {
924 if (ersz->width == odev.hres && ersz->height == odev.vres)
925 return;
926
927 odev.hres = ersz->width;
928 odev.vres = ersz->height;
929
930 odev.v.horiz = 2.*180./PI * atan(0.5/VIEWDIST*pwidth*odev.hres);
931 odev.v.vert = 2.*180./PI * atan(0.5/VIEWDIST*pheight*odev.vres);
932
933 setGLview();
934
935 inpresflags |= DFL(DC_SETVIEW);
936 }