ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.45
Committed: Sat Nov 17 01:13:51 2007 UTC (16 years, 5 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 1.44: +164 -46 lines
Log Message:
Added -c option to rtcontrib (barely tested, and -r does not work)

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rtcontrib.c,v 1.44 2007/11/15 18:32:34 greg Exp $";
3 #endif
4 /*
5 * Gather rtrace output to compute contributions from particular sources
6 */
7
8 #include "standard.h"
9 #include <ctype.h>
10 #include <signal.h>
11 #include "platform.h"
12 #include "rtprocess.h"
13 #include "selcall.h"
14 #include "color.h"
15 #include "resolu.h"
16 #include "lookup.h"
17 #include "calcomp.h"
18
19 #ifndef MAXMODLIST
20 #define MAXMODLIST 1024 /* maximum modifiers we'll track */
21 #endif
22
23 int treebufsiz = BUFSIZ; /* current tree buffer size */
24
25 typedef double DCOLOR[3]; /* double-precision color */
26
27 /*
28 * The MODCONT structure is used to accumulate ray contributions
29 * for a particular modifier, which may be subdivided into bins
30 * if binv evaluates > 0. If outspec contains a %s in it, this will
31 * be replaced with the modifier name. If outspec contains a %d in it,
32 * this will be used to create one output file per bin, otherwise all bins
33 * will be written to the same file, in order. If the global outfmt
34 * is 'c', then a 4-byte RGBE pixel will be output for each bin value
35 * and the file will conform to a RADIANCE image if xres & yres are set.
36 */
37 typedef struct {
38 const char *outspec; /* output file specification */
39 const char *modname; /* modifier name */
40 EPNODE *binv; /* bin value expression */
41 int nbins; /* number of accumulation bins */
42 DCOLOR cbin[1]; /* contribution bins (extends struct) */
43 } MODCONT; /* modifier contribution */
44
45 static void mcfree(void *p) { epfree((*(MODCONT *)p).binv); free(p); }
46
47 LUTAB modconttab = LU_SINIT(NULL,mcfree); /* modifier lookup table */
48
49 /*
50 * The STREAMOUT structure holds an open FILE pointer and a count of
51 * the number of RGB triplets per record, or 0 if unknown.
52 */
53 typedef struct {
54 FILE *ofp; /* output file pointer */
55 int outpipe; /* output is to a pipe */
56 int reclen; /* triplets/record */
57 int xr, yr; /* output resolution for picture */
58 } STREAMOUT;
59
60 /* close output stream and free record */
61 static void
62 closestream(void *p)
63 {
64 STREAMOUT *sop = (STREAMOUT *)p;
65 int status;
66 if (sop->outpipe)
67 status = pclose(sop->ofp);
68 else
69 status = fclose(sop->ofp);
70 if (status)
71 error(SYSTEM, "error closing output stream");
72 free(p);
73 }
74
75 LUTAB ofiletab = LU_SINIT(free,closestream); /* output file table */
76
77 #define OF_MODIFIER 01
78 #define OF_BIN 02
79
80 STREAMOUT *getostream(const char *ospec, const char *mname, int bn, int noopen);
81 int ofname(char *oname, const char *ospec, const char *mname, int bn);
82 void printheader(FILE *fout, const char *info);
83 void printresolu(FILE *fout, int xr, int yr);
84
85 /*
86 * The rcont structure is used to manage i/o with a particular
87 * rtrace child process. Input is passed unchanged from stdin,
88 * and output is processed in input order and accumulated according
89 * to the corresponding modifier and bin number.
90 */
91 struct rtproc {
92 struct rtproc *next; /* next in list of processes */
93 SUBPROC pd; /* rtrace pipe descriptors */
94 unsigned long raynum; /* ray number for this tree */
95 int bsiz; /* ray tree buffer length */
96 char *buf; /* ray tree buffer */
97 int nbr; /* number of bytes from rtrace */
98 }; /* rtrace process buffer */
99
100 /* rtrace command and defaults */
101 char *rtargv[256+2*MAXMODLIST] = { "rtrace",
102 "-dj", ".5", "-dr", "3",
103 "-ab", "1", "-ad", "350", };
104
105 int rtargc = 9;
106 /* overriding rtrace options */
107 char *myrtopts[] = { "-h-", "-x", "1", "-y", "0",
108 "-dt", "0", "-as", "0", "-aa", "0", NULL };
109
110 #define RTCOEFF "-o~~TmWdp" /* compute coefficients only */
111 #define RTCONTRIB "-o~~TmVdp" /* compute ray contributions */
112
113 struct rtproc rt0; /* head of rtrace process list */
114
115 struct rtproc *rt_unproc = NULL; /* unprocessed ray trees */
116
117 #define PERSIST_NONE 0 /* no persist file */
118 #define PERSIST_SINGLE 1 /* user set -P persist */
119 #define PERSIST_PARALL 2 /* user set -PP persist */
120 #define PERSIST_OURS 3 /* -PP persist belongs to us */
121 int persist_state = PERSIST_NONE; /* persist file state */
122 char persistfn[] = "pfXXXXXX"; /* our persist file name, if set */
123
124 int gargc; /* global argc */
125 char **gargv; /* global argv */
126 #define progname gargv[0]
127
128 char *octree; /* global octree argument */
129
130 int inpfmt = 'a'; /* input format */
131 int outfmt = 'a'; /* output format */
132
133 int header = 1; /* output header? */
134 int accumulate = 0; /* accumulate ray values? */
135 int force_open = 0; /* truncate existing output? */
136 int xres = 0; /* horiz. output resolution */
137 int yres = 0; /* vert. output resolution */
138
139 unsigned long raysleft; /* number of rays left to trace */
140 long waitflush; /* how long until next flush */
141
142 unsigned long lastray = 0; /* last ray number sent */
143 unsigned long lastdone = 0; /* last ray processed */
144
145 int using_stdout = 0; /* are we using stdout? */
146
147 const char *modname[MAXMODLIST]; /* ordered modifier name list */
148 int nmods = 0; /* number of modifiers */
149
150 #define queue_length() (lastray - lastdone)
151
152 MODCONT *growmodifier(MODCONT *mp, int nb);
153 MODCONT *addmodifier(char *modn, char *outf, char *binv, int bincnt);
154 void addmodfile(char *fname, char *outf, char *binv, int bincnt);
155
156 void init(int np);
157 int done_rprocs(struct rtproc *rtp);
158 void reload_output(void);
159 void recover_output(FILE *fin);
160 void trace_contribs(FILE *fin);
161 struct rtproc *wait_rproc(void);
162 struct rtproc *get_rproc(void);
163 void queue_raytree(struct rtproc *rtp);
164 void process_queue(void);
165
166 void put_contrib(const DCOLOR cnt, FILE *fout);
167 void add_contrib(const char *modn);
168 void done_contrib(void);
169
170 /* return number of open rtrace processes */
171 static int
172 nrtprocs(void)
173 {
174 int nrtp = 0;
175 struct rtproc *rtp;
176
177 for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
178 nrtp += rtp->pd.running;
179 return(nrtp);
180 }
181
182 /* set input/output format */
183 static void
184 setformat(const char *fmt)
185 {
186 switch (fmt[0]) {
187 case 'f':
188 case 'd':
189 SET_FILE_BINARY(stdin);
190 /* fall through */
191 case 'a':
192 inpfmt = fmt[0];
193 break;
194 default:
195 goto fmterr;
196 }
197 switch (fmt[1]) {
198 case '\0':
199 outfmt = inpfmt;
200 return;
201 case 'a':
202 case 'f':
203 case 'd':
204 case 'c':
205 outfmt = fmt[1];
206 break;
207 default:
208 goto fmterr;
209 }
210 if (!fmt[2])
211 return;
212 fmterr:
213 sprintf(errmsg, "Illegal i/o format: -f%s", fmt);
214 error(USER, errmsg);
215 }
216
217 /* gather rays from rtrace and output contributions */
218 int
219 main(int argc, char *argv[])
220 {
221 int contrib = 0;
222 int nprocs = 1;
223 int recover = 0;
224 char *curout = NULL;
225 char *binval = NULL;
226 int bincnt = 0;
227 char fmt[8];
228 int i, j;
229 /* need at least one argument */
230 if (argc < 2) {
231 fprintf(stderr,
232 "Usage: %s [-n nprocs][-V][-r][-e expr][-f source][-o ospec][-b binv] {-m mod | -M file} [rtrace options] octree\n",
233 argv[0]);
234 exit(1);
235 }
236 /* global program name */
237 gargv = argv;
238 /* initialize calcomp routines */
239 esupport |= E_VARIABLE|E_FUNCTION|E_INCHAN|E_RCONST|E_REDEFW;
240 esupport &= ~(E_OUTCHAN);
241 varset("PI", ':', PI);
242 /* get our options */
243 for (i = 1; i < argc-1; i++) {
244 /* expand arguments */
245 while ((j = expandarg(&argc, &argv, i)) > 0)
246 ;
247 if (j < 0) {
248 fprintf(stderr, "%s: cannot expand '%s'",
249 argv[0], argv[i]);
250 exit(1);
251 }
252 if (argv[i][0] == '-')
253 switch (argv[i][1]) {
254 case 'n': /* number of processes */
255 if (argv[i][2] || i >= argc-2) break;
256 nprocs = atoi(argv[++i]);
257 if (nprocs <= 0)
258 error(USER, "illegal number of processes");
259 continue;
260 case 'V': /* output contributions */
261 switch (argv[i][2]) {
262 case '\0':
263 contrib = !contrib;
264 continue;
265 case '+': case '1':
266 case 'T': case 't':
267 case 'Y': case 'y':
268 contrib = 1;
269 continue;
270 case '-': case '0':
271 case 'F': case 'f':
272 case 'N': case 'n':
273 contrib = 0;
274 continue;
275 }
276 break;
277 case 'c': /* accumulate ray values */
278 switch (argv[i][2]) {
279 case '\0':
280 accumulate = !accumulate;
281 continue;
282 case '+': case '1':
283 case 'T': case 't':
284 case 'Y': case 'y':
285 accumulate = 1;
286 continue;
287 case '-': case '0':
288 case 'F': case 'f':
289 case 'N': case 'n':
290 accumulate = 0;
291 continue;
292 }
293 break;
294 case 'r': /* recover output */
295 if (argv[i][2]) break;
296 recover++;
297 continue;
298 case 'h': /* output header? */
299 switch (argv[i][2]) {
300 case '\0':
301 header = !header;
302 continue;
303 case '+': case '1':
304 case 'T': case 't':
305 case 'Y': case 'y':
306 header = 1;
307 continue;
308 case '-': case '0':
309 case 'F': case 'f':
310 case 'N': case 'n':
311 header = 0;
312 continue;
313 }
314 break;
315 case 'f': /* file or force or format */
316 if (!argv[i][2]) {
317 char *fpath;
318 if (i >= argc-2) break;
319 fpath = getpath(argv[++i],
320 getrlibpath(), R_OK);
321 if (fpath == NULL) {
322 sprintf(errmsg,
323 "cannot find file '%s'",
324 argv[i]);
325 error(USER, errmsg);
326 }
327 fcompile(fpath);
328 continue;
329 }
330 if (argv[i][2] == 'o') {
331 force_open++;
332 continue;
333 }
334 setformat(argv[i]+2);
335 continue;
336 case 'e': /* expression */
337 if (argv[i][2] || i >= argc-2) break;
338 scompile(argv[++i], NULL, 0);
339 continue;
340 case 'o': /* output file spec. */
341 if (argv[i][2] || i >= argc-2) break;
342 curout = argv[++i];
343 continue;
344 case 'x': /* horiz. output resolution */
345 if (argv[i][2] || i >= argc-2) break;
346 xres = atoi(argv[++i]);
347 continue;
348 case 'y': /* vert. output resolution */
349 if (argv[i][2] || i >= argc-2) break;
350 yres = atoi(argv[++i]);
351 continue;
352 case 'b': /* bin expression/count */
353 if (i >= argc-2) break;
354 if (argv[i][2] == 'n') {
355 bincnt = atoi(argv[++i]);
356 continue;
357 }
358 if (argv[i][2]) break;
359 binval = argv[++i];
360 continue;
361 case 'm': /* modifier name */
362 if (argv[i][2] || i >= argc-2) break;
363 rtargv[rtargc++] = "-ti";
364 rtargv[rtargc++] = argv[++i];
365 addmodifier(argv[i], curout, binval, bincnt);
366 continue;
367 case 'M': /* modifier file */
368 if (argv[i][2] || i >= argc-2) break;
369 rtargv[rtargc++] = "-tI";
370 rtargv[rtargc++] = argv[++i];
371 addmodfile(argv[i], curout, binval, bincnt);
372 continue;
373 case 'P': /* persist file */
374 if (i >= argc-2) break;
375 persist_state = (argv[i][2] == 'P') ?
376 PERSIST_PARALL : PERSIST_SINGLE;
377 rtargv[rtargc++] = argv[i];
378 rtargv[rtargc++] = argv[++i];
379 continue;
380 }
381 rtargv[rtargc++] = argv[i]; /* assume rtrace option */
382 }
383 if (accumulate) /* no output flushing for single record */
384 xres = yres = 0;
385 /* set global argument list */
386 gargc = argc; gargv = argv;
387 /* add "mandatory" rtrace settings */
388 for (j = 0; myrtopts[j] != NULL; j++)
389 rtargv[rtargc++] = myrtopts[j];
390 rtargv[rtargc++] = contrib ? RTCONTRIB : RTCOEFF;
391 /* just asking for defaults? */
392 if (!strcmp(argv[i], "-defaults")) {
393 char sxres[16], syres[16];
394 char *rtpath;
395 printf("-n %-2d\t\t\t\t# number of processes\n", nprocs);
396 printf("-V%c\t\t\t\t# output %s\n", contrib ? '+' : '-',
397 contrib ? "contributions" : "coefficients");
398 printf("-c%c\t\t\t\t# %s\n", accumulate ? '+' : '-',
399 accumulate ? "accumulate ray values" :
400 "one output record per ray");
401 fflush(stdout); /* report OUR options */
402 rtargv[rtargc++] = header ? "-h+" : "-h-";
403 sprintf(fmt, "-f%c%c", inpfmt, outfmt);
404 rtargv[rtargc++] = fmt;
405 rtargv[rtargc++] = "-x";
406 sprintf(sxres, "%d", xres);
407 rtargv[rtargc++] = sxres;
408 rtargv[rtargc++] = "-y";
409 sprintf(syres, "%d", yres);
410 rtargv[rtargc++] = syres;
411 rtargv[rtargc++] = "-defaults";
412 rtargv[rtargc] = NULL;
413 rtpath = getpath(rtargv[0], getenv("PATH"), X_OK);
414 if (rtpath == NULL) {
415 eputs(rtargv[0]);
416 eputs(": command not found\n");
417 exit(1);
418 }
419 execv(rtpath, rtargv);
420 perror(rtpath); /* execv() should not return */
421 exit(1);
422 }
423 if (nprocs > 1) { /* add persist file if parallel */
424 if (persist_state == PERSIST_SINGLE)
425 error(USER, "use -PP option for multiple processes");
426 if (persist_state == PERSIST_NONE) {
427 rtargv[rtargc++] = "-PP";
428 rtargv[rtargc++] = mktemp(persistfn);
429 persist_state = PERSIST_OURS;
430 }
431 }
432 /* add format string */
433 sprintf(fmt, "-f%cf", inpfmt);
434 rtargv[rtargc++] = fmt;
435 /* octree argument is last */
436 if (i <= 0 || i != argc-1 || argv[i][0] == '-')
437 error(USER, "missing octree argument");
438 rtargv[rtargc++] = octree = argv[i];
439 rtargv[rtargc] = NULL;
440 /* start rtrace */
441 init(nprocs);
442 if (recover) /* perform recovery if requested */
443 if ((force_open = accumulate))
444 reload_output();
445 else
446 recover_output(stdin);
447 trace_contribs(stdin); /* compute contributions */
448 quit(0);
449 }
450
451 #ifndef SIGALRM
452 #define SIGALRM SIGTERM
453 #endif
454 /* kill persistent rtrace process */
455 static void
456 killpersist(void)
457 {
458 FILE *fp = fopen(persistfn, "r");
459 RT_PID pid;
460
461 if (fp == NULL)
462 return;
463 if (fscanf(fp, "%*s %d", &pid) != 1 || kill(pid, SIGALRM) < 0)
464 unlink(persistfn);
465 fclose(fp);
466 }
467
468 /* close rtrace processes and clean up */
469 int
470 done_rprocs(struct rtproc *rtp)
471 {
472 int st0, st1 = 0;
473
474 if (rtp->next != NULL) { /* close last opened first! */
475 st1 = done_rprocs(rtp->next);
476 free((void *)rtp->next);
477 rtp->next = NULL;
478 }
479 st0 = close_process(&rtp->pd);
480 if (st0 < 0)
481 error(WARNING, "unknown return status from rtrace process");
482 else if (st0 > 0)
483 return(st0);
484 return(st1);
485 }
486
487 /* exit with status */
488 void
489 quit(int status)
490 {
491 int rtstat;
492
493 if (persist_state == PERSIST_OURS) /* terminate waiting rtrace */
494 killpersist();
495 /* clean up rtrace process(es) */
496 rtstat = done_rprocs(&rt0);
497 if (status == 0)
498 status = rtstat;
499 exit(status); /* flushes all output streams */
500 }
501
502 /* start rtrace processes and initialize */
503 void
504 init(int np)
505 {
506 struct rtproc *rtp;
507 int i;
508 int maxbytes;
509 /* make sure we have something to do */
510 if (!nmods)
511 error(USER, "No modifiers specified");
512 /* assign ray variables */
513 scompile("Dx=$1;Dy=$2;Dz=$3;", NULL, 0);
514 scompile("Px=$4;Py=$5;Pz=$6;", NULL, 0);
515 /* set up signal handling */
516 signal(SIGINT, quit);
517 #ifdef SIGHUP
518 signal(SIGHUP, quit);
519 #endif
520 #ifdef SIGTERM
521 signal(SIGTERM, quit);
522 #endif
523 #ifdef SIGPIPE
524 signal(SIGPIPE, quit);
525 #endif
526 rtp = &rt0; /* start rtrace process(es) */
527 for (i = 0; i++ < np; ) {
528 errno = 0;
529 maxbytes = open_process(&rtp->pd, rtargv);
530 if (maxbytes == 0) {
531 eputs(rtargv[0]);
532 eputs(": command not found\n");
533 exit(1);
534 }
535 if (maxbytes < 0)
536 error(SYSTEM, "cannot start rtrace process");
537 if (maxbytes > treebufsiz)
538 treebufsiz = maxbytes;
539 rtp->raynum = 0;
540 rtp->bsiz = 0;
541 rtp->buf = NULL;
542 rtp->nbr = 0;
543 if (i == np) /* last process? */
544 break;
545 if (i == 1)
546 sleep(2); /* wait for persist file */
547 rtp->next = (struct rtproc *)malloc(sizeof(struct rtproc));
548 if (rtp->next == NULL)
549 error(SYSTEM, "out of memory in init");
550 rtp = rtp->next;
551 }
552 rtp->next = NULL; /* terminate list */
553 if (yres > 0) {
554 if (xres > 0)
555 raysleft = (unsigned long)xres*yres;
556 else
557 raysleft = yres;
558 } else
559 raysleft = 0;
560 waitflush = xres;
561 }
562
563 /* grow modifier to accommodate more bins */
564 MODCONT *
565 growmodifier(MODCONT *mp, int nb)
566 {
567 if (nb <= mp->nbins)
568 return mp;
569 mp = (MODCONT *)realloc(mp, sizeof(MODCONT) + sizeof(DCOLOR)*(nb-1));
570 if (mp == NULL)
571 error(SYSTEM, "out of memory in growmodifier");
572 memset(mp->cbin+mp->nbins, 0, sizeof(DCOLOR)*(nb-mp->nbins));
573 mp->nbins = nb;
574 return mp;
575 }
576
577 /* add modifier to our list to track */
578 MODCONT *
579 addmodifier(char *modn, char *outf, char *binv, int bincnt)
580 {
581 LUENT *lep = lu_find(&modconttab, modn);
582 MODCONT *mp;
583 int i;
584
585 if (lep->data != NULL) {
586 sprintf(errmsg, "duplicate modifier '%s'", modn);
587 error(USER, errmsg);
588 }
589 if (nmods >= MAXMODLIST)
590 error(USER, "too many modifiers");
591 modname[nmods++] = modn; /* XXX assumes static string */
592 lep->key = modn; /* XXX assumes static string */
593 mp = (MODCONT *)malloc(sizeof(MODCONT));
594 if (mp == NULL)
595 error(SYSTEM, "out of memory in addmodifier");
596 mp->outspec = outf; /* XXX assumes static string */
597 mp->modname = modn; /* XXX assumes static string */
598 if (binv != NULL)
599 mp->binv = eparse(binv);
600 else
601 mp->binv = eparse("0");
602 mp->nbins = 1;
603 setcolor(mp->cbin[0], 0., 0., 0.);
604 if (mp->binv->type == NUM) /* assume one bin if constant */
605 bincnt = 1;
606 else if (bincnt > 1)
607 mp = growmodifier(mp, bincnt);
608 lep->data = (char *)mp;
609 /* allocate output streams */
610 for (i = bincnt; i-- > 0; )
611 getostream(mp->outspec, mp->modname, i, 1);
612 return mp;
613 }
614
615 /* add modifiers from a file list */
616 void
617 addmodfile(char *fname, char *outf, char *binv, int bincnt)
618 {
619 char *mname[MAXMODLIST];
620 int i;
621 /* find the file & store strings */
622 if (wordfile(mname, getpath(fname, getrlibpath(), R_OK)) < 0) {
623 sprintf(errmsg, "cannot find modifier file '%s'", fname);
624 error(SYSTEM, errmsg);
625 }
626 for (i = 0; mname[i]; i++) /* add each one */
627 addmodifier(mname[i], outf, binv, bincnt);
628 }
629
630 /* put string to stderr */
631 void
632 eputs(char *s)
633 {
634 static int midline = 0;
635
636 if (!*s) return;
637 if (!midline) {
638 fputs(progname, stderr);
639 fputs(": ", stderr);
640 }
641 fputs(s, stderr);
642 midline = s[strlen(s)-1] != '\n';
643 }
644
645 /* construct output file name and return flags whether modifier/bin present */
646 int
647 ofname(char *oname, const char *ospec, const char *mname, int bn)
648 {
649 const char *mnp = NULL;
650 const char *bnp = NULL;
651 const char *cp;
652
653 if (ospec == NULL)
654 return -1;
655 for (cp = ospec; *cp; cp++) /* check format position(s) */
656 if (*cp == '%') {
657 do
658 ++cp;
659 while (isdigit(*cp));
660 switch (*cp) {
661 case '%':
662 break;
663 case 's':
664 if (mnp != NULL)
665 return -1;
666 mnp = cp;
667 break;
668 case 'd':
669 if (bnp != NULL)
670 return -1;
671 bnp = cp;
672 break;
673 default:
674 return -1;
675 }
676 }
677 if (mnp != NULL) { /* create file name */
678 if (bnp != NULL) {
679 if (bnp > mnp)
680 sprintf(oname, ospec, mname, bn);
681 else
682 sprintf(oname, ospec, bn, mname);
683 return OF_MODIFIER|OF_BIN;
684 }
685 sprintf(oname, ospec, mname);
686 return OF_MODIFIER;
687 }
688 if (bnp != NULL) {
689 sprintf(oname, ospec, bn);
690 return OF_BIN;
691 }
692 strcpy(oname, ospec);
693 return 0;
694 }
695
696 /* write header to the given output stream */
697 void
698 printheader(FILE *fout, const char *info)
699 {
700 extern char VersionID[];
701 FILE *fin = fopen(octree, "r");
702
703 if (fin == NULL)
704 quit(1);
705 checkheader(fin, "ignore", fout); /* copy octree header */
706 fclose(fin);
707 printargs(gargc-1, gargv, fout); /* add our command */
708 fprintf(fout, "SOFTWARE= %s\n", VersionID);
709 fputnow(fout);
710 if (info != NULL) /* add extra info if given */
711 fputs(info, fout);
712 switch (outfmt) { /* add output format */
713 case 'a':
714 fputformat("ascii", fout);
715 break;
716 case 'f':
717 fputformat("float", fout);
718 break;
719 case 'd':
720 fputformat("double", fout);
721 break;
722 case 'c':
723 fputformat(COLRFMT, fout);
724 break;
725 }
726 fputc('\n', fout);
727 }
728
729 /* write resolution string to given output stream */
730 void
731 printresolu(FILE *fout, int xr, int yr)
732 {
733 if ((xr > 0) & (yr > 0)) /* resolution string */
734 fprtresolu(xr, yr, fout);
735 if (xres > 0) /* global flush flag */
736 fflush(fout);
737 }
738
739 /* Get output stream pointer (open and write header if new and noopen==0) */
740 STREAMOUT *
741 getostream(const char *ospec, const char *mname, int bn, int noopen)
742 {
743 static const DCOLOR nocontrib = BLKCOLOR;
744 static STREAMOUT stdos;
745 int ofl;
746 char oname[1024];
747 LUENT *lep;
748 STREAMOUT *sop;
749
750 if (ospec == NULL) { /* use stdout? */
751 if (!noopen && !using_stdout) {
752 if (outfmt != 'a')
753 SET_FILE_BINARY(stdout);
754 if (header)
755 printheader(stdout, NULL);
756 printresolu(stdout, xres, yres);
757 stdos.xr = xres; stdos.yr = yres;
758 using_stdout = 1;
759 }
760 stdos.ofp = stdout;
761 stdos.reclen += noopen;
762 return &stdos;
763 }
764 ofl = ofname(oname, ospec, mname, bn); /* get output name */
765 if (ofl < 0) {
766 sprintf(errmsg, "bad output format '%s'", ospec);
767 error(USER, errmsg);
768 }
769 lep = lu_find(&ofiletab, oname); /* look it up */
770 if (lep->key == NULL) /* new entry */
771 lep->key = strcpy((char *)malloc(strlen(oname)+1), oname);
772 sop = (STREAMOUT *)lep->data;
773 if (sop == NULL) { /* allocate stream */
774 sop = (STREAMOUT *)malloc(sizeof(STREAMOUT));
775 if (sop == NULL)
776 error(SYSTEM, "out of memory in getostream");
777 sop->outpipe = oname[0] == '!';
778 sop->reclen = 0;
779 sop->ofp = NULL; /* open iff noopen==0 */
780 sop->xr = xres; sop->yr = yres;
781 lep->data = (char *)sop;
782 if (!force_open && access(oname, F_OK) == 0) {
783 errno = EEXIST; /* file exists */
784 goto openerr;
785 }
786 }
787 if (!noopen && sop->ofp == NULL) { /* open output stream */
788 long i;
789 if (oname[0] == '!') /* output to command */
790 sop->ofp = popen(oname+1, "w");
791 else /* else open file */
792 sop->ofp = fopen(oname, "w");
793 if (sop->ofp == NULL)
794 goto openerr;
795 if (outfmt != 'a')
796 SET_FILE_BINARY(sop->ofp);
797 if (header) {
798 char info[512];
799 char *cp = info;
800 if (ofl & OF_MODIFIER || sop->reclen == 1) {
801 sprintf(cp, "MODIFIER=%s\n", mname);
802 while (*cp) ++cp;
803 }
804 if (ofl & OF_BIN) {
805 sprintf(cp, "BIN=%d\n", bn);
806 while (*cp) ++cp;
807 }
808 *cp = '\0';
809 printheader(sop->ofp, info);
810 }
811 printresolu(sop->ofp, sop->xr, sop->yr);
812 /* play catch-up */
813 for (i = accumulate ? 0 : lastdone; i--; ) {
814 int j = sop->reclen;
815 if (j <= 0) j = 1;
816 while (j--)
817 put_contrib(nocontrib, sop->ofp);
818 if (outfmt == 'a')
819 putc('\n', sop->ofp);
820 }
821 if (xres > 0)
822 fflush(sop->ofp);
823 }
824 sop->reclen += noopen; /* add to length if noopen */
825 return sop; /* return output stream */
826 openerr:
827 sprintf(errmsg, "cannot open '%s' for writing", oname);
828 error(SYSTEM, errmsg);
829 return NULL; /* pro forma return */
830 }
831
832 /* read input ray into buffer */
833 int
834 getinp(char *buf, FILE *fp)
835 {
836 double dv[3], *dvp;
837 float *fvp;
838 char *cp;
839 int i;
840
841 switch (inpfmt) {
842 case 'a':
843 cp = buf; /* make sure we get 6 floats */
844 for (i = 0; i < 6; i++) {
845 if (fgetword(cp, buf+127-cp, fp) == NULL)
846 return -1;
847 if (i >= 3)
848 dv[i-3] = atof(cp);
849 if ((cp = fskip(cp)) == NULL || *cp)
850 return -1;
851 *cp++ = ' ';
852 }
853 getc(fp); /* get/put eol */
854 *cp-- = '\0'; *cp = '\n';
855 if (DOT(dv,dv) <= FTINY*FTINY)
856 return 0; /* dummy ray */
857 return strlen(buf);
858 case 'f':
859 if (fread(buf, sizeof(float), 6, fp) < 6)
860 return -1;
861 fvp = (float *)buf + 3;
862 if (DOT(fvp,fvp) <= FTINY*FTINY)
863 return 0; /* dummy ray */
864 return sizeof(float)*6;
865 case 'd':
866 if (fread(buf, sizeof(double), 6, fp) < 6)
867 return -1;
868 dvp = (double *)buf + 3;
869 if (DOT(dvp,dvp) <= FTINY*FTINY)
870 return 0; /* dummy ray */
871 return sizeof(double)*6;
872 }
873 error(INTERNAL, "botched input format");
874 return -1; /* pro forma return */
875 }
876
877 static float rparams[9]; /* traced ray parameters */
878
879 /* return channel (ray) value */
880 double
881 chanvalue(int n)
882 {
883 if (--n < 0 || n >= 6)
884 error(USER, "illegal channel number ($N)");
885 return rparams[n+3];
886 }
887
888 /* add current ray contribution to the appropriate modifier bin */
889 void
890 add_contrib(const char *modn)
891 {
892 LUENT *le = lu_find(&modconttab, modn);
893 MODCONT *mp = (MODCONT *)le->data;
894 int bn;
895
896 if (mp == NULL) {
897 sprintf(errmsg, "unexpected modifier '%s' from rtrace", modn);
898 error(USER, errmsg);
899 }
900 eclock++; /* get bin number */
901 bn = (int)(evalue(mp->binv) + .5);
902 if (bn <= 0)
903 bn = 0;
904 else if (bn >= mp->nbins) /* new bin */
905 le->data = (char *)(mp = growmodifier(mp, bn+1));
906 addcolor(mp->cbin[bn], rparams);
907 }
908
909 /* output newline to ASCII file and/or flush as requested */
910 static int
911 puteol(const LUENT *e, void *p)
912 {
913 STREAMOUT *sop = (STREAMOUT *)e->data;
914
915 if (outfmt == 'a')
916 putc('\n', sop->ofp);
917 if (!waitflush)
918 fflush(sop->ofp);
919 if (ferror(sop->ofp)) {
920 sprintf(errmsg, "write error on file '%s'", e->key);
921 error(SYSTEM, errmsg);
922 }
923 return 0;
924 }
925
926 /* put out ray contribution to file */
927 void
928 put_contrib(const DCOLOR cnt, FILE *fout)
929 {
930 float fv[3];
931 COLR cv;
932
933 switch (outfmt) {
934 case 'a':
935 fprintf(fout, "%.6e\t%.6e\t%.6e\t", cnt[0], cnt[1], cnt[2]);
936 break;
937 case 'f':
938 fv[0] = cnt[0];
939 fv[1] = cnt[1];
940 fv[2] = cnt[2];
941 fwrite(fv, sizeof(float), 3, fout);
942 break;
943 case 'd':
944 fwrite(cnt, sizeof(double), 3, fout);
945 break;
946 case 'c':
947 setcolr(cv, cnt[0], cnt[1], cnt[2]);
948 fwrite(cv, sizeof(cv), 1, fout);
949 break;
950 default:
951 error(INTERNAL, "botched output format");
952 }
953 }
954
955 /* output ray tallies and clear for next accumulation */
956 void
957 done_contrib(void)
958 {
959 int i, j;
960 MODCONT *mp;
961 STREAMOUT *sop;
962 /* output modifiers in order */
963 for (i = 0; i < nmods; i++) {
964 mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
965 sop = getostream(mp->outspec, mp->modname, 0,0);
966 put_contrib(mp->cbin[0], sop->ofp);
967 if (mp->nbins > 3 && /* minor optimization */
968 sop == getostream(mp->outspec, mp->modname, 1,0))
969 for (j = 1; j < mp->nbins; j++)
970 put_contrib(mp->cbin[j], sop->ofp);
971 else
972 for (j = 1; j < mp->nbins; j++)
973 put_contrib(mp->cbin[j],
974 getostream(mp->outspec,mp->modname,j,0)->ofp);
975 /* clear for next time */
976 memset(mp->cbin, 0, sizeof(DCOLOR)*mp->nbins);
977 }
978 --waitflush; /* terminate records */
979 lu_doall(&ofiletab, puteol, NULL);
980 if (using_stdout & (outfmt == 'a'))
981 putc('\n', stdout);
982 if (!waitflush) {
983 waitflush = xres;
984 if (using_stdout)
985 fflush(stdout);
986 }
987 }
988
989 /* queue completed ray tree produced by rtrace process */
990 void
991 queue_raytree(struct rtproc *rtp)
992 {
993 struct rtproc *rtu, *rtl = NULL;
994 /* insert following ray order */
995 for (rtu = rt_unproc; rtu != NULL; rtu = (rtl=rtu)->next)
996 if (rtp->raynum < rtu->raynum)
997 break;
998 rtu = (struct rtproc *)malloc(sizeof(struct rtproc));
999 if (rtu == NULL)
1000 error(SYSTEM, "out of memory in queue_raytree");
1001 *rtu = *rtp;
1002 if (rtl == NULL) {
1003 rtu->next = rt_unproc;
1004 rt_unproc = rtu;
1005 } else {
1006 rtu->next = rtl->next;
1007 rtl->next = rtu;
1008 }
1009 rtp->raynum = 0; /* clear path for next ray tree */
1010 rtp->bsiz = 0;
1011 rtp->buf = NULL;
1012 rtp->nbr = 0;
1013 }
1014
1015 /* process completed ray trees from our queue */
1016 void
1017 process_queue(void)
1018 {
1019 char modname[128];
1020 /* ray-ordered queue */
1021 while (rt_unproc != NULL && rt_unproc->raynum == lastdone+1) {
1022 struct rtproc *rtp = rt_unproc;
1023 int n = rtp->nbr;
1024 const char *cp = rtp->buf;
1025 while (n > 0) { /* process rays */
1026 register char *mnp = modname;
1027 /* skip leading tabs */
1028 while (n > 0 && *cp == '\t') {
1029 cp++; n--;
1030 }
1031 if (!n || !(isalpha(*cp) | (*cp == '_')))
1032 error(USER, "bad modifier name from rtrace");
1033 /* get modifier name */
1034 while (n > 1 && *cp != '\t') {
1035 if (mnp - modname >= sizeof(modname)-2)
1036 error(INTERNAL, "modifier name too long");
1037 *mnp++ = *cp++; n--;
1038 }
1039 *mnp = '\0';
1040 cp++; n--; /* eat following tab */
1041 if (n < (int)(sizeof(float)*9))
1042 error(USER, "incomplete ray value from rtrace");
1043 /* add ray contribution */
1044 memcpy(rparams, cp, sizeof(float)*9);
1045 cp += sizeof(float)*9; n -= sizeof(float)*9;
1046 add_contrib(modname);
1047 }
1048 if (!accumulate)
1049 done_contrib(); /* sum up contributions & output */
1050 lastdone = rtp->raynum;
1051 if (rtp->buf != NULL) /* free up buffer space */
1052 free(rtp->buf);
1053 rt_unproc = rtp->next;
1054 free(rtp); /* done with this ray tree */
1055 }
1056 }
1057
1058 /* wait for rtrace process to finish with ray tree */
1059 struct rtproc *
1060 wait_rproc(void)
1061 {
1062 struct rtproc *rtfree = NULL;
1063 fd_set readset, errset;
1064 int nr;
1065 struct rtproc *rt;
1066 int n;
1067
1068 do {
1069 nr = 0; /* prepare select call */
1070 FD_ZERO(&readset); FD_ZERO(&errset); n = 0;
1071 for (rt = &rt0; rt != NULL; rt = rt->next) {
1072 if (rt->raynum) {
1073 FD_SET(rt->pd.r, &readset);
1074 ++nr;
1075 }
1076 FD_SET(rt->pd.r, &errset);
1077 if (rt->pd.r >= n)
1078 n = rt->pd.r + 1;
1079 }
1080 if (!nr) /* no rays pending */
1081 break;
1082 if (nr > 1) { /* call select for multiple proc's */
1083 errno = 0;
1084 if (select(n, &readset, NULL, &errset, NULL) < 0)
1085 error(SYSTEM, "select call error in wait_rproc()");
1086 } else
1087 FD_ZERO(&errset);
1088 nr = 0;
1089 for (rt = &rt0; rt != NULL; rt = rt->next) {
1090 if (!FD_ISSET(rt->pd.r, &readset) &&
1091 !FD_ISSET(rt->pd.r, &errset))
1092 continue;
1093 if (rt->buf == NULL) {
1094 rt->bsiz = treebufsiz;
1095 rt->buf = (char *)malloc(treebufsiz);
1096 } else if (rt->nbr + BUFSIZ > rt->bsiz) {
1097 if (rt->bsiz + BUFSIZ <= treebufsiz)
1098 rt->bsiz = treebufsiz;
1099 else
1100 treebufsiz = rt->bsiz += BUFSIZ;
1101 rt->buf = (char *)realloc(rt->buf, rt->bsiz);
1102 }
1103 if (rt->buf == NULL)
1104 error(SYSTEM, "out of memory in wait_rproc");
1105 nr = read(rt->pd.r, rt->buf+rt->nbr, rt->bsiz-rt->nbr);
1106 if (nr <= 0)
1107 error(USER, "rtrace process died");
1108 rt->nbr += nr; /* advance & check */
1109 if (rt->nbr >= 4 && !memcmp(rt->buf+rt->nbr-4,
1110 "~\t~\t", 4)) {
1111 rt->nbr -= 4; /* elide terminator */
1112 queue_raytree(rt);
1113 rtfree = rt; /* ready for next ray */
1114 }
1115 }
1116 } while ((rtfree == NULL) & (nr > 0)); /* repeat until ready or out */
1117 return rtfree;
1118 }
1119
1120 /* return next available rtrace process */
1121 struct rtproc *
1122 get_rproc(void)
1123 {
1124 struct rtproc *rtp;
1125 /* check for idle rtrace */
1126 for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
1127 if (!rtp->raynum)
1128 return rtp;
1129 return wait_rproc(); /* need to wait for one */
1130 }
1131
1132 /* trace ray contributions (main loop) */
1133 void
1134 trace_contribs(FILE *fin)
1135 {
1136 char inpbuf[128];
1137 int iblen;
1138 struct rtproc *rtp;
1139 /* loop over input */
1140 while ((iblen = getinp(inpbuf, fin)) >= 0) {
1141 if (!iblen || /* need reset? */
1142 queue_length() > 10*nrtprocs() ||
1143 lastray+1 < lastray) {
1144 while (wait_rproc() != NULL)
1145 process_queue();
1146 if (lastray+1 < lastray)
1147 lastdone = lastray = 0;
1148 }
1149 rtp = get_rproc(); /* get avail. rtrace process */
1150 rtp->raynum = ++lastray; /* assign ray */
1151 if (iblen) { /* trace ray if valid */
1152 writebuf(rtp->pd.w, inpbuf, iblen);
1153 } else { /* else bypass dummy ray */
1154 queue_raytree(rtp); /* empty tree */
1155 if ((yres <= 0) | (waitflush > 1))
1156 waitflush = 1; /* flush after this */
1157 }
1158 process_queue(); /* catch up with results */
1159 if (raysleft && !--raysleft)
1160 break; /* preemptive EOI */
1161 }
1162 while (wait_rproc() != NULL) /* process outstanding rays */
1163 process_queue();
1164 if (accumulate)
1165 done_contrib(); /* output tallies */
1166 if (raysleft)
1167 error(USER, "unexpected EOF on input");
1168 lu_done(&ofiletab); /* close output files */
1169 }
1170
1171 /* seek on the given output file */
1172 static int
1173 myseeko(const LUENT *e, void *p)
1174 {
1175 STREAMOUT *sop = (STREAMOUT *)e->data;
1176 off_t nbytes = *(off_t *)p;
1177
1178 if (sop->reclen > 1)
1179 nbytes = nbytes * sop->reclen;
1180 if (fseeko(sop->ofp, nbytes, SEEK_CUR) < 0) {
1181 sprintf(errmsg, "seek error on file '%s'", e->key);
1182 error(SYSTEM, errmsg);
1183 }
1184 return 0;
1185 }
1186
1187 /* load previously accumulated values */
1188 void
1189 reload_output(void)
1190 {
1191 int i, j;
1192 MODCONT *mp;
1193 int ofl;
1194 char oname[1024];
1195 char *fmode = "rb";
1196 char *outvfmt;
1197 LUENT *ment, *oent;
1198 STREAMOUT sout;
1199
1200 error(INTERNAL, "-r option not yet implemented with -c");
1201
1202 switch (outfmt) {
1203 case 'a':
1204 outvfmt = "ascii";
1205 fmode = "r";
1206 break;
1207 case 'f':
1208 outvfmt = "float";
1209 break;
1210 case 'd':
1211 outvfmt = "double";
1212 break;
1213 case 'c':
1214 outvfmt = COLRFMT;
1215 break;
1216 default:
1217 error(INTERNAL, "botched output format");
1218 return;
1219 }
1220 /* check modifier outputs */
1221 for (i = 0; i < nmods; i++) {
1222 ment = lu_find(&modconttab,modname[i]);
1223 mp = (MODCONT *)ment->data;
1224 if (mp->outspec == NULL)
1225 error(USER, "cannot reload from stdout");
1226 if (mp->outspec[0] == '!')
1227 error(USER, "cannot reload from command");
1228 for (j = 0; ; j++) { /* check each bin's file */
1229 ofl = ofname(oname, mp->outspec, mp->modname, j);
1230 if (ofl < 0)
1231 error(USER, "bad output file specification");
1232 oent = lu_find(&ofiletab, oname);
1233 if (oent->data != NULL) {
1234 sout = *(STREAMOUT *)oent->data;
1235 } else {
1236 sout.reclen = 0;
1237 sout.ofp = NULL;
1238 }
1239 if (sout.ofp != NULL) { /* already open? */
1240 if (ofl & OF_BIN)
1241 continue;
1242 break;
1243 }
1244 /* open output */
1245 sout.ofp = fopen(oname, fmode);
1246 if (sout.ofp == NULL) {
1247 if (j)
1248 break; /* assume end of modifier */
1249 sprintf(errmsg, "missing reload file '%s'",
1250 oname);
1251 error(WARNING, errmsg);
1252 break;
1253 }
1254 if (oent->key == NULL) /* new entry */
1255 oent->key = strcpy((char *)
1256 malloc(strlen(oname)+1), oname);
1257 if (oent->data == NULL)
1258 oent->data = (char *)malloc(sizeof(STREAMOUT));
1259 *(STREAMOUT *)oent->data = sout;
1260 if (!(ofl & OF_BIN))
1261 break; /* no bin separation */
1262 }
1263 if (j > mp->nbins) /* reallocate modifier bins */
1264 ment->data = (char *)(mp = growmodifier(mp, j));
1265 }
1266 lu_done(&ofiletab); /* close all files */
1267 }
1268
1269 /* recover output if possible */
1270 void
1271 recover_output(FILE *fin)
1272 {
1273 off_t lastout = -1;
1274 int outvsiz, recsiz;
1275 char *outvfmt;
1276 int i, j;
1277 MODCONT *mp;
1278 int ofl;
1279 char oname[1024];
1280 LUENT *ment, *oent;
1281 STREAMOUT sout;
1282 off_t nvals;
1283 int xr, yr;
1284
1285 switch (outfmt) {
1286 case 'a':
1287 error(USER, "cannot recover ASCII output");
1288 return;
1289 case 'f':
1290 outvsiz = sizeof(float)*3;
1291 outvfmt = "float";
1292 break;
1293 case 'd':
1294 outvsiz = sizeof(double)*3;
1295 outvfmt = "double";
1296 break;
1297 case 'c':
1298 outvsiz = sizeof(COLR);
1299 outvfmt = COLRFMT;
1300 break;
1301 default:
1302 error(INTERNAL, "botched output format");
1303 return;
1304 }
1305 /* check modifier outputs */
1306 for (i = 0; i < nmods; i++) {
1307 ment = lu_find(&modconttab,modname[i]);
1308 mp = (MODCONT *)ment->data;
1309 if (mp->outspec == NULL)
1310 error(USER, "cannot recover from stdout");
1311 if (mp->outspec[0] == '!')
1312 error(USER, "cannot recover from command");
1313 for (j = 0; ; j++) { /* check each bin's file */
1314 ofl = ofname(oname, mp->outspec, mp->modname, j);
1315 if (ofl < 0)
1316 error(USER, "bad output file specification");
1317 oent = lu_find(&ofiletab, oname);
1318 if (oent->data != NULL) {
1319 sout = *(STREAMOUT *)oent->data;
1320 } else {
1321 sout.reclen = 0;
1322 sout.ofp = NULL;
1323 }
1324 if (sout.ofp != NULL) { /* already open? */
1325 if (ofl & OF_BIN)
1326 continue;
1327 break;
1328 }
1329 /* open output */
1330 sout.ofp = fopen(oname, "rb+");
1331 if (sout.ofp == NULL) {
1332 if (j)
1333 break; /* assume end of modifier */
1334 sprintf(errmsg, "missing recover file '%s'",
1335 oname);
1336 error(WARNING, errmsg);
1337 break;
1338 }
1339 nvals = lseek(fileno(sout.ofp), 0, SEEK_END);
1340 if (nvals <= 0) {
1341 lastout = 0; /* empty output, quit here */
1342 fclose(sout.ofp);
1343 break;
1344 }
1345 if (!sout.reclen) {
1346 if (!(ofl & OF_BIN)) {
1347 sprintf(errmsg,
1348 "need -bn to recover file '%s'",
1349 oname);
1350 error(USER, errmsg);
1351 }
1352 recsiz = outvsiz;
1353 } else
1354 recsiz = outvsiz * sout.reclen;
1355
1356 lseek(fileno(sout.ofp), 0, SEEK_SET);
1357 if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) {
1358 sprintf(errmsg, "format mismatch for '%s'",
1359 oname);
1360 error(USER, errmsg);
1361 }
1362 if ((xres > 0) & (yres > 0) &&
1363 (!fscnresolu(&xr, &yr, sout.ofp) ||
1364 xr != xres ||
1365 yr != yres)) {
1366 sprintf(errmsg, "resolution mismatch for '%s'",
1367 oname);
1368 error(USER, errmsg);
1369 }
1370 nvals = (nvals - (off_t)ftell(sout.ofp)) / recsiz;
1371 if ((lastout < 0) | (nvals < lastout))
1372 lastout = nvals;
1373 if (oent->key == NULL) /* new entry */
1374 oent->key = strcpy((char *)
1375 malloc(strlen(oname)+1), oname);
1376 if (oent->data == NULL)
1377 oent->data = (char *)malloc(sizeof(STREAMOUT));
1378 *(STREAMOUT *)oent->data = sout;
1379 if (!(ofl & OF_BIN))
1380 break; /* no bin separation */
1381 }
1382 if (!lastout) { /* empty output */
1383 error(WARNING, "no previous data to recover");
1384 lu_done(&ofiletab); /* reclose all outputs */
1385 return;
1386 }
1387 if (j > mp->nbins) /* reallocate modifier bins */
1388 ment->data = (char *)(mp = growmodifier(mp, j));
1389 }
1390 if (lastout < 0) {
1391 error(WARNING, "no output files to recover");
1392 return;
1393 }
1394 if (raysleft && lastout >= raysleft) {
1395 error(WARNING, "output appears to be complete");
1396 /* XXX should read & discard input? */
1397 quit(0);
1398 }
1399 /* seek on all files */
1400 nvals = lastout * outvsiz;
1401 lu_doall(&ofiletab, myseeko, &nvals);
1402 /* skip repeated input */
1403 for (nvals = 0; nvals < lastout; nvals++)
1404 if (getinp(oname, fin) < 0)
1405 error(USER, "unexpected EOF on input");
1406 lastray = lastdone = (unsigned long)lastout;
1407 if (raysleft)
1408 raysleft -= lastray;
1409 }