ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/glrad.c
Revision: 3.1
Committed: Tue Jun 9 11:43:18 1998 UTC (25 years, 9 months ago) by gwlarson
Content type: text/plain
Branch: MAIN
Log Message:
Initial revision

File Contents

# Content
1 /* Copyright (c) 1998 Silicon Graphics, Inc. */
2
3 #ifndef lint
4 static char SCCSid[] = "$SunId$ SGI";
5 #endif
6
7 /*
8 * Program to display Radiance scene using OpenGL.
9 */
10
11 #include "radogl.h"
12 #include "view.h"
13 #include "paths.h"
14 #include <sys/types.h>
15 #include <GL/glx.h>
16 #include <ctype.h>
17 #include "glradicon.h"
18
19 #ifndef MAXVIEW
20 #define MAXVIEW 63 /* maximum number of standard views */
21 #endif
22 #ifndef MAXSCENE
23 #define MAXSCENE 127 /* maximum number of scene files */
24 #endif
25
26 #define ZOOMPCT 9 /* percent to zoom at a time */
27
28 #define MOVPCT 7 /* percent distance to move /frame */
29 #define MOVDIR(b) ((b)==Button1 ? 1 : (b)==Button2 ? 0 : -1)
30 #define MOVDEG (-5) /* degrees to orbit CW/down /frame */
31 #define MOVORB(s) ((s)&ShiftMask ? 1 : (s)&ControlMask ? -1 : 0)
32
33 #define MINWIDTH 480 /* minimum graphics window width */
34 #define MINHEIGHT 400 /* minimum graphics window height */
35
36 #define BORWIDTH 5 /* border width */
37
38 #define ourscreen DefaultScreen(ourdisplay)
39 #define ourroot RootWindow(ourdisplay,ourscreen)
40 #define ourmask (StructureNotifyMask|ExposureMask|KeyPressMask|\
41 ButtonPressMask|ButtonReleaseMask)
42
43 #define levptr(etype) ((etype *)&currentevent)
44
45 XEvent currentevent; /* current event */
46
47 int mapped = 0; /* window is mapped? */
48 unsigned long ourblack=0, ourwhite=~0;
49
50 Display *ourdisplay = NULL; /* our display */
51 XVisualInfo *ourvinf; /* our visual information */
52 Window gwind = 0; /* our graphics window */
53 int hres, vres; /* rendering window dimensions */
54 int maxhres, maxvres; /* maximum given dimensions */
55 GLXContext gctx; /* our GLX context */
56
57 double pwidth, pheight; /* pixel dimensions (mm) */
58
59 int headlocked = 0; /* lock vertical motion */
60
61 struct {
62 char *nam; /* view name (NULL if none) */
63 VIEW *v; /* parameters (NULL term.) */
64 } vwl[MAXVIEW+1]; /* our list of views */
65
66 int currentview = 0; /* current view number */
67 VIEW thisview = STDVIEW; /* displayed view */
68 VIEW lastview; /* last recorded view */
69 char *lastvc = NULL; /* cause of last view change */
70
71 char *progname; /* global argv[0] */
72 char *radfile; /* rad input file */
73 char *scene[MAXSCENE+1]; /* material and scene file list */
74 int nscenef = 0; /* number of scene files */
75 char *octree; /* octree name (NULL if unnec.) */
76
77 int rtpd[3]; /* rtrace process descriptors */
78
79 int backvis = 1; /* back faces visible? */
80
81 int displist; /* our scene display list */
82
83 extern char *fgets(), *fgetline(), *atos(), *scan4var();
84 extern int nowarn; /* turn warnings off? */
85 extern time_t time();
86
87
88 main(argc, argv)
89 int argc;
90 char *argv[];
91 {
92 char *viewsel = NULL;
93 long vwintvl = 0;
94 int i;
95
96 progname = argv[0];
97 for (i = 1; i < argc && argv[i][0] == '-'; i++)
98 switch (argv[i][1]) {
99 case 'v':
100 viewsel = argv[++i];
101 break;
102 case 'w':
103 nowarn = !nowarn;
104 break;
105 case 'c':
106 vwintvl = atoi(argv[++i]);
107 break;
108 case 'b':
109 backvis = !backvis;
110 break;
111 default:
112 goto userr;
113 }
114 if (i >= argc)
115 goto userr;
116 /* run rad and get views */
117 runrad(argc-i, argv+i);
118 /* check view */
119 if (viewsel != NULL && (currentview = findvw(viewsel)) < 0) {
120 fprintf(stderr, "%s: no such view\n", viewsel);
121 quit(1);
122 }
123 /* open GL */
124 dev_open(radfile = argv[i]);
125 /* load octree or scene files */
126 if (octree != NULL) {
127 displist = rgl_octlist(octree, NULL, NULL);
128 startrtrace(octree);
129 } else
130 displist = rgl_filelist(nscenef, scene);
131 /* set initial view */
132 dev_view(vwl[currentview].v);
133 /* input/render loop */
134 while (dev_input(vwintvl))
135 ;
136 /* all done */
137 quit(0);
138 userr:
139 fprintf(stderr, "Usage: %s [-w][-c #secs][-v view] rfile [VAR=value]..\n",
140 argv[0]);
141 quit(1);
142 }
143
144
145 quit(code) /* exit gracefully */
146 int code;
147 {
148 if (ourdisplay != NULL)
149 dev_close();
150 if (rtpd[2] > 0) {
151 if (close_process(rtpd) > 0)
152 wputs("bad exit status from rtrace\n");
153 rtpd[2] = 0;
154 }
155 exit(code);
156 }
157
158
159 startrtrace(octname) /* start rtrace on octname */
160 char *octname;
161 {
162 static char *av[12] = {"rtrace", "-h", "-fff", "-ld+",
163 "-opL", "-x", "1"};
164 int ac = 7;
165
166 if (nowarn) av[ac++] = "-w-";
167 av[ac++] = octname;
168 av[ac] = NULL;
169 if (open_process(rtpd, av) <= 0)
170 error(SYSTEM, "cannot start rtrace process");
171 }
172
173
174 runrad(ac, av) /* run rad and load variables */
175 int ac;
176 char **av;
177 {
178 static char optfile[] = TEMPLATE;
179 int nvn = 0, nvv = 0;
180 FILE *fp;
181 int cval;
182 register char *cp;
183 char radcomm[256], buf[128], nam[32];
184 /* set rad commmand */
185 strcpy(radcomm, "rad -w -v 0 "); /* look out below! */
186 cp = radcomm + 19;
187 while (ac--) {
188 strcpy(cp, *av++);
189 while (*cp) cp++;
190 *cp++ = ' ';
191 }
192 strcpy(cp, "OPTFILE="); /* create temporary options file */
193 strcpy(cp+8, mktemp(optfile));
194 if (system(radcomm)) /* update octree */
195 error(USER, "error executing rad command");
196 /* replace "-v 0" with "-n -e -s -V" */
197 strcpy(radcomm+7, "-n -e -s -V");
198 radcomm[18] = ' ';
199 if ((fp = popen(radcomm, "r")) == NULL)
200 error(SYSTEM, "cannot start rad command");
201 buf[0] = '\0'; /* read variables alphabetically */
202 /* get exposure */
203 if ((cp = scan4var(buf, sizeof(buf), "EXPOSURE", fp)) != NULL) {
204 expval = atof(cp);
205 if (*cp == '-' | *cp == '+')
206 expval = pow(2., expval);
207 }
208 /* look for materials */
209 while ((cp = scan4var(buf, sizeof(buf), "materials", fp)) != NULL) {
210 nscenef += wordstring(scene+nscenef, cp);
211 buf[0] = '\0';
212 }
213 /* look for octree */
214 if ((cp = scan4var(buf, sizeof(buf), "OCTREE", fp)) != NULL)
215 octree = savqstr(cp);
216 /* look for scene files */
217 while ((cp = scan4var(buf, sizeof(buf), "scene", fp)) != NULL) {
218 nscenef += wordstring(scene+nscenef, cp);
219 buf[0] = '\0';
220 }
221 /* load view names */
222 while ((cp = scan4var(buf, sizeof(buf), "view", fp)) != NULL) {
223 if (nvn >= MAXVIEW)
224 error(INTERNAL, "too many views in rad file");
225 vwl[nvn++].nam = *cp == '-' ? (char *)NULL :
226 savqstr(atos(nam, sizeof(nam), cp));
227 buf[0] = '\0';
228 }
229 /* load actual views */
230 do
231 if (isview(buf)) {
232 vwl[nvv].v = (VIEW *)bmalloc(sizeof(VIEW));
233 copystruct(vwl[nvv].v, &stdview);
234 sscanview(vwl[nvv].v, buf);
235 if ((cp = setview(vwl[nvv++].v)) != NULL) {
236 fprintf(stderr, "%s: bad view %d - %s\n",
237 progname, nvv, cp);
238 quit(1);
239 }
240 }
241 while (fgets(buf, sizeof(buf), fp) != NULL);
242 if (nvv != nvn)
243 error(INTERNAL, "view miscount in runrad");
244 pclose(fp);
245 /* open options file */
246 if ((fp = fopen(optfile, "r")) == NULL)
247 error(SYSTEM, "cannot open options file");
248 /* get ambient value */
249 while (fgets(buf, sizeof(buf), fp) != NULL)
250 if (!strncmp(buf, "-av ", 4)) {
251 setcolor(ambval, atof(buf+4),
252 atof(sskip2(buf+4,1)),
253 atof(sskip2(buf+4,2)));
254 break;
255 }
256 fclose(fp);
257 unlink(optfile); /* delete options file */
258 }
259
260
261 int
262 findvw(nm) /* find named view */
263 register char *nm;
264 {
265 register int n;
266
267 if (*nm >= '1' & *nm <= '9' &&
268 (n = atoi(nm)-1) <= MAXVIEW && vwl[n].v != NULL)
269 return(n);
270 for (n = 0; vwl[n].v != NULL; n++)
271 if (vwl[n].nam != NULL && !strcmp(nm, vwl[n].nam))
272 return(n);
273 return(-1);
274 }
275
276
277 int
278 varmatch(s, vn) /* match line to variable */
279 register char *s, *vn;
280 {
281 register int c;
282
283 for ( ; *vn && *s == *vn; s++, vn++)
284 ;
285 while (isspace(*s))
286 s++;
287 if (*s == '=')
288 return(*vn);
289 while (!(c = toupper(*s++) - toupper(*vn)) && *vn++)
290 ;
291 return(c);
292 }
293
294
295 char *
296 scan4var(buf, buflen, vname, fp) /* scan for variable from fp */
297 char *buf;
298 int buflen;
299 char *vname;
300 FILE *fp;
301 {
302 int cval;
303 register char *cp;
304 /* search out matching line */
305 while ((cval = varmatch(buf, vname))) {
306 if (cval > 0) /* gone too far? */
307 return(NULL);
308 buf[0] = '\0'; /* else get next line */
309 if (fgetline(buf, buflen, fp) == NULL)
310 return(NULL);
311 }
312 /* skip variable name and '=' */
313 for (cp = buf; *cp++ != '='; )
314 ;
315 while (isspace(*cp)) cp++;
316 return(cp);
317 }
318
319
320 dev_open(id) /* initialize GLX driver */
321 char *id;
322 {
323 static int atlBest[] = {GLX_RGBA, GLX_RED_SIZE,4,
324 GLX_GREEN_SIZE,4, GLX_BLUE_SIZE,4,
325 GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE,15, None};
326 XSetWindowAttributes ourwinattr;
327 XWMHints ourxwmhints;
328 XSizeHints oursizhints;
329 /* open display server */
330 ourdisplay = XOpenDisplay(NULL);
331 if (ourdisplay == NULL)
332 error(USER, "cannot open X-windows; DISPLAY variable set?\n");
333 /* find a usable visual */
334 ourvinf = glXChooseVisual(ourdisplay, ourscreen, atlBest);
335 if (ourvinf == NULL)
336 error(USER, "no suitable visuals available");
337 /* get a context */
338 gctx = glXCreateContext(ourdisplay, ourvinf, NULL, GL_TRUE);
339 /* open window */
340 ourwinattr.background_pixel = ourblack;
341 ourwinattr.border_pixel = ourblack;
342 ourwinattr.event_mask = ourmask;
343 /* this is stupid */
344 ourwinattr.colormap = XCreateColormap(ourdisplay, ourroot,
345 ourvinf->visual, AllocNone);
346 gwind = XCreateWindow(ourdisplay, ourroot, 0, 0,
347 DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH,
348 DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH,
349 BORWIDTH, ourvinf->depth, InputOutput, ourvinf->visual,
350 CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &ourwinattr);
351 if (gwind == 0)
352 error(SYSTEM, "cannot create window\n");
353 XStoreName(ourdisplay, gwind, id);
354 /* set window manager hints */
355 ourxwmhints.flags = InputHint|IconPixmapHint;
356 ourxwmhints.input = True;
357 ourxwmhints.icon_pixmap = XCreateBitmapFromData(ourdisplay,
358 gwind, glradicon_bits, glradicon_width, glradicon_height);
359 XSetWMHints(ourdisplay, gwind, &ourxwmhints);
360 oursizhints.min_width = MINWIDTH;
361 oursizhints.min_height = MINHEIGHT;
362 oursizhints.flags = PMinSize;
363 XSetNormalHints(ourdisplay, gwind, &oursizhints);
364 /* set GLX context */
365 glXMakeCurrent(ourdisplay, gwind, gctx);
366 glEnable(GL_DEPTH_TEST);
367 glDepthFunc(GL_LESS);
368 glShadeModel(GL_SMOOTH);
369 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
370 glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
371 glEnable(GL_LIGHTING);
372 if (backvis)
373 glDisable(GL_CULL_FACE);
374 else {
375 glFrontFace(GL_CCW);
376 glCullFace(GL_BACK);
377 glEnable(GL_CULL_FACE);
378 }
379 glDrawBuffer(GL_BACK);
380 /* figure out sensible view */
381 pwidth = (double)DisplayWidthMM(ourdisplay, ourscreen) /
382 DisplayWidth(ourdisplay, ourscreen);
383 pheight = (double)DisplayHeightMM(ourdisplay, ourscreen) /
384 DisplayHeight(ourdisplay, ourscreen);
385 /* map the window */
386 XMapWindow(ourdisplay, gwind);
387 rgl_checkerr("initializing GLX");
388 }
389
390
391 dev_close() /* close our display and free resources */
392 {
393 glXMakeCurrent(ourdisplay, None, NULL);
394 glXDestroyContext(ourdisplay, gctx);
395 XDestroyWindow(ourdisplay, gwind);
396 gwind = 0;
397 XCloseDisplay(ourdisplay);
398 ourdisplay = NULL;
399 }
400
401
402 int
403 dev_view(nv) /* assign new driver view */
404 register VIEW *nv;
405 {
406 int newhres = hres, newvres = vres;
407 double wa, va;
408 /* check view legality */
409 if (nv->type != VT_PER) {
410 error(COMMAND, "illegal view type");
411 nv->type = VT_PER;
412 }
413 if (nv->horiz > 160. | nv->vert > 160.) {
414 error(COMMAND, "illegal view angle");
415 if (nv->horiz > 160.)
416 nv->horiz = 160.;
417 if (nv->vert > 160.)
418 nv->vert = 160.;
419 }
420 if (hres != 0 & vres != 0) {
421 wa = (vres*pheight)/(hres*pwidth);
422 va = viewaspect(&thisview);
423 if (va > wa+.05) {
424 newvres = (pwidth/pheight)*va*newhres + .5;
425 if (newvres > maxvres) {
426 newvres = maxvres;
427 newhres = (pheight/pwidth)/va*newvres + .5;
428 }
429 } else if (va < wa-.05) {
430 newhres = (pheight/pwidth)/va*newvres + .5;
431 if (newhres > maxhres) {
432 newhres = maxhres;
433 newvres = (pwidth/pheight)*va*newhres + .5;
434 }
435 }
436 if (newhres != hres | newvres != vres) {
437 XResizeWindow(ourdisplay, gwind, newhres, newvres);
438 do
439 dev_input(0); /* get resize event */
440 while (newhres != hres | newvres != vres);
441 }
442 }
443 copystruct(&thisview, nv);
444 setglpersp(&thisview);
445 render();
446 return(1);
447 }
448
449
450 int
451 dev_input(nsecs) /* get next input event */
452 int nsecs;
453 {
454 #if 0
455 static time_t lasttime = 0;
456 time_t thistime;
457
458 if (nsecs > 0) {
459 thistime = time(0);
460 nsecs -= (long)(thistime - lasttime);
461 lasttime = thistime;
462 }
463 if (nsecs > 0)
464 alarm(nsecs);
465 #endif
466 XNextEvent(ourdisplay, levptr(XEvent));
467 switch (levptr(XEvent)->type) {
468 case ConfigureNotify:
469 resizewindow(levptr(XConfigureEvent));
470 break;
471 case UnmapNotify:
472 mapped = 0;
473 break;
474 case MapNotify:
475 mapped = 1;
476 break;
477 case Expose:
478 fixwindow(levptr(XExposeEvent));
479 break;
480 case KeyPress:
481 return(getkey(levptr(XKeyPressedEvent)));
482 case ButtonPress:
483 getmove(levptr(XButtonPressedEvent));
484 break;
485 }
486 return(1);
487 }
488
489
490 render() /* render our display list and swap buffers */
491 {
492 if (!mapped)
493 return;
494 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
495 glCallList(displist);
496 glXSwapBuffers(ourdisplay, gwind); /* calls glFlush() */
497 rgl_checkerr("rendering display list");
498 }
499
500
501 moveview(dx, dy, mov, orb) /* move our view */
502 int dx, dy, mov, orb;
503 {
504 VIEW nv;
505 FVECT odir, v1, wp;
506 double d;
507 register int li;
508 /* start with old view */
509 copystruct(&nv, &thisview);
510 /* change view direction */
511 if ((d = viewray(v1, odir, &thisview,
512 (dx+.5)/hres, (dy+.5)/vres)) < -FTINY)
513 return(0); /* outside view */
514 if (mov | orb) {
515 if (!getintersect(wp, v1, odir, d))
516 return(0);
517 VSUM(odir, wp, nv.vp, -1.);
518 } else
519 VCOPY(nv.vdir, odir);
520 if (orb && mov) { /* orbit left/right */
521 spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov);
522 VSUM(nv.vp, wp, odir, -1.);
523 spinvector(nv.vdir, nv.vdir, nv.vup, d);
524 } else if (orb) { /* orbit up/down */
525 fcross(v1, odir, nv.vup);
526 if (normalize(v1) == 0.)
527 return(0);
528 spinvector(odir, odir, v1, d=MOVDEG*PI/180.*orb);
529 VSUM(nv.vp, wp, odir, -1.);
530 spinvector(nv.vdir, nv.vdir, v1, d);
531 } else if (mov) { /* move forward/backward */
532 d = MOVPCT/100. * mov;
533 VSUM(nv.vp, nv.vp, odir, d);
534 }
535 if (!mov ^ !orb && headlocked) { /* restore head height */
536 VSUM(v1, thisview.vp, nv.vp, -1.);
537 d = DOT(v1, thisview.vup);
538 VSUM(nv.vp, nv.vp, thisview.vup, d);
539 }
540 if (setview(&nv) != NULL)
541 return(0); /* illegal view */
542 dev_view(&nv);
543 return(1);
544 }
545
546
547 getmove(ebut) /* get view change */
548 XButtonPressedEvent *ebut;
549 {
550 int movdir = MOVDIR(ebut->button);
551 int movorb = MOVORB(ebut->state);
552 int moved = 0;
553 Window rootw, childw;
554 int rootx, rooty, wx, wy;
555 unsigned int statemask;
556
557 copylastv("moving");
558 XNoOp(ourdisplay);
559
560 while (!XCheckMaskEvent(ourdisplay,
561 ButtonReleaseMask, levptr(XEvent))) {
562
563 if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw,
564 &rootx, &rooty, &wx, &wy, &statemask))
565 break; /* on another screen */
566
567 if (!moveview(wx, vres-1-wy, movdir, movorb)) {
568 sleep(1);
569 continue;
570 } else
571 moved++;
572 }
573 if (!moved) { /* do final motion */
574 movdir = MOVDIR(levptr(XButtonReleasedEvent)->button);
575 wx = levptr(XButtonReleasedEvent)->x;
576 wy = levptr(XButtonReleasedEvent)->y;
577 moveview(wx, vres-1-wy, movdir, movorb);
578 }
579 }
580
581
582 getintersect(wp, org, dir, md) /* intersect ray with scene geometry */
583 FVECT wp; /* returned world intersection point */
584 FVECT org, dir;
585 double md;
586 {
587 float fbuf[6];
588 /* check to see if rtrace is running */
589 if (rtpd[2] <= 0)
590 return(0);
591 /* assign origin */
592 fbuf[0] = org[0]; fbuf[1] = org[1]; fbuf[2] = org[2];
593 /* compute clipping distance */
594 if (md <= FTINY) md = FHUGE;
595 fbuf[3] = dir[0]*md; fbuf[4] = dir[1]*md; fbuf[5] = dir[2]*md;
596 /* trace that ray */
597 if (process(rtpd, fbuf, fbuf, 4*sizeof(float), 6*sizeof(float)) !=
598 4*sizeof(float))
599 error(INTERNAL, "error getting data back from rtrace process");
600 if (fbuf[3] >= .99*FHUGE)
601 return(0); /* missed local objects */
602 wp[0] = fbuf[0]; wp[1] = fbuf[1]; wp[2] = fbuf[2];
603 return(1); /* else return world intersection */
604 }
605
606
607 setglpersp(vp) /* set perspective view in GL */
608 register VIEW *vp;
609 {
610 double d, xmin, xmax, ymin, ymax, zmin, zmax;
611
612 zmin = 0.05;
613 zmax = 5000.;
614 if (thisview.vfore > FTINY)
615 zmin = thisview.vfore;
616 if (thisview.vaft > FTINY)
617 zmax = thisview.vaft;
618 xmax = zmin * tan(PI/180./2. * thisview.horiz);
619 xmin = -xmax;
620 d = thisview.hoff * (xmax - xmin);
621 xmin += d; xmax += d;
622 ymax = zmin * tan(PI/180./2. * thisview.vert);
623 ymin = -ymax;
624 d = thisview.voff * (ymax - ymin);
625 ymin += d; ymax += d;
626 /* set view matrix */
627 glMatrixMode(GL_PROJECTION);
628 glLoadIdentity();
629 glFrustum(xmin, xmax, ymin, ymax, zmin, zmax);
630 gluLookAt(thisview.vp[0], thisview.vp[1], thisview.vp[2],
631 thisview.vp[0] + thisview.vdir[0],
632 thisview.vp[1] + thisview.vdir[1],
633 thisview.vp[2] + thisview.vdir[2],
634 thisview.vup[0], thisview.vup[1], thisview.vup[2]);
635 rgl_checkerr("setting perspective view");
636 }
637
638
639 int
640 getkey(ekey) /* get input key */
641 register XKeyPressedEvent *ekey;
642 {
643 int n;
644 char buf[8];
645
646 n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL);
647 if (n != 1)
648 return(1);
649 switch (buf[0]) {
650 case 'h': /* turn on height motion lock */
651 headlocked = 1;
652 break;
653 case 'H': /* turn off height motion lock */
654 headlocked = 0;
655 break;
656 case 'l': /* retrieve last (premouse) view */
657 if (lastvc != NULL) {
658 VIEW vtmp;
659 copystruct(&vtmp, &thisview);
660 dev_view(&lastview);
661 copystruct(&lastview, &vtmp);
662 } else
663 XBell(ourdisplay, 0);
664 break;
665 case 'n': /* move to next standard view */
666 gotoview(currentview+1);
667 break;
668 case 'p': /* move to last standard view */
669 gotoview(currentview-1);
670 break;
671 case '+': /* zoom in */
672 zoomview(100+ZOOMPCT, ekey->x, vres-1-ekey->y);
673 break;
674 case '-': /* zoom out */
675 zoomview(100-ZOOMPCT, ekey->x, vres-1-ekey->y);
676 break;
677 case 'v': /* spit current view to stdout */
678 fputs(VIEWSTR, stdout);
679 fprintview(&thisview, stdout);
680 fputc('\n', stdout);
681 break;
682 case 'V': /* append view to rad file */
683 appendview(NULL, &thisview);
684 break;
685 case 'q': /* quit the program */
686 return(0);
687 default:
688 XBell(ourdisplay, 0);
689 break;
690 }
691 return(1);
692 }
693
694
695 zoomview(pct, dx, dy) /* zoom in or out around (dx,dy) */
696 int pct;
697 int dx, dy;
698 {
699 double h, v;
700 FVECT direc;
701
702 if (pct == 100 | pct <= 0)
703 return;
704 copylastv("zooming");
705 h = (dx+.5)/hres - 0.5;
706 v = (dy+.5)/vres - 0.5;
707 h *= (1. - 100./pct);
708 v *= (1. - 100./pct);
709 thisview.vdir[0] += h*thisview.hvec[0] + v*thisview.vvec[0];
710 thisview.vdir[1] += h*thisview.hvec[1] + v*thisview.vvec[1];
711 thisview.vdir[2] += h*thisview.hvec[2] + v*thisview.vvec[2];
712 thisview.horiz = 2.*180./PI * atan( 100./pct *
713 tan(PI/180./2.*thisview.horiz) );
714 thisview.vert = 2.*180./PI * atan( 100./pct *
715 tan(PI/180./2.*thisview.vert) );
716 setview(&thisview);
717 dev_view(&thisview);
718 }
719
720
721 gotoview(vwnum) /* go to specified view number */
722 int vwnum;
723 {
724 if (vwnum < 0)
725 for (vwnum = currentview; vwl[vwnum+1].v != NULL; vwnum++)
726 ;
727 else if (vwnum >= MAXVIEW || vwl[vwnum].v == NULL)
728 vwnum = 0;
729 if (vwnum == currentview)
730 return;
731 dev_view(vwl[currentview=vwnum].v);
732 }
733
734
735 appendview(nm, vp) /* append standard view */
736 char *nm;
737 VIEW *vp;
738 {
739 FILE *fp;
740 /* check if already in there */
741 if (!bcmp(&thisview, vwl[currentview].v, sizeof(VIEW))) {
742 error(COMMAND, "view already in standard list");
743 return;
744 }
745 /* append to file */
746 if ((fp = fopen(radfile, "a")) == NULL) {
747 error(COMMAND, "cannot append rad input file");
748 return;
749 }
750 fputs("view=", fp);
751 if (nm != NULL) {
752 fputc(' ', fp); fputs(nm, fp);
753 }
754 fprintview(vp, fp); fputc('\n', fp);
755 fclose(fp);
756 /* append to our list */
757 while (vwl[currentview].v != NULL)
758 currentview++;
759 if (currentview >= MAXVIEW)
760 error(INTERNAL, "too many views in appendview");
761 vwl[currentview].v = (VIEW *)bmalloc(sizeof(VIEW));
762 copystruct(vwl[currentview].v, &thisview);
763 if (nm != NULL)
764 vwl[currentview].nam = savqstr(nm);
765 }
766
767
768 copylastv(cause) /* copy last view position */
769 char *cause;
770 {
771 if (cause == lastvc)
772 return; /* only record one view per cause */
773 lastvc = cause;
774 copystruct(&lastview, &thisview);
775 }
776
777
778 fixwindow(eexp) /* repair damage to window */
779 register XExposeEvent *eexp;
780 {
781 if (hres == 0 | vres == 0) { /* first exposure */
782 resizewindow((XConfigureEvent *)eexp);
783 return;
784 }
785 if (eexp->count) /* wait for final exposure */
786 return;
787 /* rerender everything */
788 render();
789 }
790
791
792 resizewindow(ersz) /* resize window */
793 register XConfigureEvent *ersz;
794 {
795 static char resizing[] = "resizing window";
796 double wa, va;
797
798 glViewport(0, 0, hres=ersz->width, vres=ersz->height);
799 if (hres > maxhres) maxhres = hres;
800 if (vres > maxvres) maxvres = vres;
801 wa = (vres*pheight)/(hres*pwidth);
802 va = viewaspect(&thisview);
803 if (va > wa+.05) {
804 copylastv(resizing);
805 thisview.vert = 2.*180./PI *
806 atan( tan(PI/180./2. * thisview.horiz) * wa );
807 } else if (va < wa-.05) {
808 copylastv(resizing);
809 thisview.horiz = 2.*180./PI *
810 atan( tan(PI/180./2. * thisview.vert) / wa );
811 } else
812 return;
813 setview(&thisview);
814 dev_view(&thisview);
815 }