ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.1
Committed: Wed May 25 04:45:22 2005 UTC (18 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
Created rtcontrib program for computing ray contributions and coefficients

File Contents

# User Rev Content
1 greg 1.1 #ifndef lint
2     static const char RCSid[] = "$Id$";
3     #endif
4     /*
5     * Gather rtrace output to compute contributions from particular sources
6     */
7    
8     #include <ctype.h>
9     #include "rtio.h"
10     #include "rterror.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     #ifdef getc_unlocked /* avoid horrendous overhead of flockfile */
20     #undef getc
21     #undef putc
22     #define getc getc_unlocked
23     #define putc putc_unlocked
24     #define ferror ferror_unlocked
25     #endif
26    
27     #define MAXMODLIST 1024 /* maximum modifiers we'll track */
28    
29     int treebufsiz = BUFSIZ; /* current tree buffer size */
30    
31     /*
32     * The modcont structure is used to accumulate ray contributions
33     * for a particular modifier, which may be subdivided into bins
34     * if binv is non-NULL. If outspec contains a %s in it, this will
35     * be replaced with the modifier name. If outspec contains a %d in it,
36     * this will be used to create one output file per bin, otherwise all bins
37     * will be written to the same file, in order. If the global outfmt
38     * is 'c', then a 4-byte RGBE pixel will be output for each bin value
39     * and the file will conform to a RADIANCE image if xres & yres are set.
40     */
41     typedef struct {
42     const char *outspec; /* output file specification */
43     const char *modname; /* modifier name */
44     EPNODE *binv; /* bin value expression */
45     int nbins; /* number of accumulation bins */
46     COLOR cbin[1]; /* contribution bins (extends struct) */
47     } MODCONT; /* modifier contribution */
48    
49     static void mcfree(void *p) { epfree((*(MODCONT *)p).binv); free(p); }
50    
51     LUTAB modconttab = LU_SINIT(NULL,mcfree); /* modifier lookup table */
52    
53     /* close output stream */
54     static void closefile(void *p) { fclose((FILE *)p); }
55    
56     LUTAB ofiletab = LU_SINIT(free,closefile); /* output file table */
57    
58     FILE *getofile(const char *ospec, const char *mname, int bn);
59    
60     /*
61     * The rcont structure is used to manage i/o with a particular
62     * rtrace child process. Input is passed unchanged from stdin,
63     * and output is processed in input order and accumulated according
64     * to the corresponding modifier and bin number.
65     */
66     struct rtproc {
67     struct rtproc *next; /* next in list of processes */
68     SUBPROC pd; /* rtrace pipe descriptors */
69     unsigned long raynum; /* ray number for this buffer */
70     int bsiz; /* rtrace buffer length */
71     char *buf; /* rtrace i/o buffer */
72     char *bpos; /* current buffer position */
73     }; /* rtrace process */
74    
75     /* rtrace command and defaults */
76     char *rtargv[256] = { "rtrace", "-dj", ".5", "-dr", "3",
77     "-ab", "1", "-ad", "512", };
78     int rtargc = 9;
79     /* overriding rtrace options */
80     char *myrtopts[] = { "-o-TmWdp", "-h-",
81     "-x", "1", "-y", "0", NULL };
82    
83     struct rtproc rt0; /* head of rtrace process list */
84    
85     struct rtproc *rt_unproc = NULL; /* unprocessed ray trees */
86    
87     char persistfn[] = "pfXXXXXX"; /* persist file name */
88     char fmt[8]; /* rtrace i/o format */
89    
90     int gargc; /* global argc */
91     char **gargv; /* global argv */
92     #define progname gargv[0]
93    
94     char *octree; /* global octree argument */
95    
96     int inpfmt = 'a'; /* input format */
97     int outfmt = 'a'; /* output format */
98    
99     int header = 1; /* output header? */
100     int xres = 0; /* horiz. output resolution */
101     int yres = 0; /* vert. output resolution */
102    
103     long raysleft; /* number of rays left to trace */
104     long waitflush; /* how long until next flush */
105    
106     unsigned long lastray = 0; /* last ray number sent */
107     unsigned long lastdone = 0; /* last ray processed */
108    
109     const char *modname[MAXMODLIST]; /* ordered modifier name list */
110     int nmods = 0; /* number of modifiers */
111    
112     MODCONT *addmodifier(char *modn, char *outf, char *binv);
113    
114     int done_rprocs(struct rtproc *rtp);
115     void init(int np);
116     void tracecontribs(FILE *fp);
117     struct rtproc *wait_rproc(void);
118     struct rtproc *get_rproc(void);
119     void process_rays(struct rtproc *rtp);
120    
121     void add_contrib(const char *modn, float rayval[7]);
122     void done_contrib(void);
123    
124     /* set input/output format */
125     static void
126     setformat(const char *fmt)
127     {
128     switch (fmt[0]) {
129     case 'a':
130     case 'f':
131     case 'd':
132     inpfmt = fmt[0];
133     break;
134     default:
135     goto fmterr;
136     }
137     switch (fmt[1]) {
138     case '\0':
139     outfmt = inpfmt;
140     return;
141     case 'a':
142     case 'f':
143     case 'd':
144     case 'c':
145     outfmt = fmt[1];
146     break;
147     default:
148     goto fmterr;
149     }
150     if (!fmt[2])
151     return;
152     fmterr:
153     sprintf(errmsg, "Illegal i/o format: -f%s", fmt);
154     error(USER, errmsg);
155     }
156    
157     /* gather rays from rtrace and output contributions */
158     int
159     main(int argc, char *argv[])
160     {
161     int nprocs = 1;
162     char *curout = NULL;
163     char *binval = NULL;
164     int i;
165     /* set global argument list */
166     gargc = argc;
167     gargv = argv;
168     /* set up calcomp functions */
169     esupport |= E_VARIABLE|E_FUNCTION|E_INCHAN|E_RCONST|E_REDEFW;
170     esupport &= ~(E_OUTCHAN);
171     /* get our options */
172     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
173     switch (argv[i][1]) {
174     case 'n': /* number of processes */
175     if (argv[i][2] || i >= argc-1) break;
176     nprocs = atoi(argv[++i]);
177     if (nprocs <= 0)
178     error(USER, "illegal number of processes");
179     continue;
180     case 'h': /* output header? */
181     switch (argv[i][2]) {
182     case '\0':
183     header = !header;
184     continue;
185     case '+': case '1': case 'T': case 't':
186     header = 1;
187     continue;
188     case '-': case '0': case 'F': case 'f':
189     header = 0;
190     continue;
191     }
192     break;
193     case 'f': /* file or i/o format */
194     if (!argv[i][2]) {
195     if (i >= argc-1) break;
196     fcompile(argv[++i]);
197     continue;
198     }
199     setformat(argv[i]+2);
200     continue;
201     case 'e': /* expression */
202     if (argv[i][2] || i >= argc-1) break;
203     scompile(argv[++i], NULL, 0);
204     continue;
205     case 'o': /* output file spec. */
206     if (argv[i][2] || i >= argc-1) break;
207     curout = argv[++i];
208     continue;
209     case 'x': /* horiz. output resolution */
210     if (argv[i][2] || i >= argc-1) break;
211     xres = atoi(argv[++i]);
212     continue;
213     case 'y': /* vert. output resolution */
214     if (argv[i][2] || i >= argc-1) break;
215     yres = atoi(argv[++i]);
216     continue;
217     case 'l': /* limit distance? */
218     if (argv[i][2] != 'd') break;
219     rtargv[rtargc++] = argv[i];
220     continue;
221     case 'b': /* bin expression */
222     if (argv[i][2] || i >= argc-1) break;
223     binval = argv[++i];
224     continue;
225     case 'm': /* modifier name */
226     if (argv[i][2] || i >= argc-1) break;
227     rtargv[rtargc++] = "-ti";
228     rtargv[rtargc++] = argv[++i];
229     addmodifier(argv[i], curout, binval);
230     continue;
231     }
232     break; /* break into rtrace options */
233     }
234     for ( ; i < argc; i++) /* transfer rtrace-specific options */
235     rtargv[rtargc++] = argv[i];
236     if (!strcmp(rtargv[--rtargc], "-defaults"))
237     nprocs = 0;
238     if (nprocs > 1) { /* add persist file if parallel invocation */
239     rtargv[rtargc++] = "-PP";
240     rtargv[rtargc++] = mktemp(persistfn);
241     }
242     /* add "mandatory" rtrace settings */
243     for (i = 0; myrtopts[i] != NULL; i++)
244     rtargv[rtargc++] = myrtopts[i];
245     if (!nprocs) { /* just asking for defaults? */
246     char sxres[16], syres[16];
247     char *rtpath;
248     printf("-n 1\t\t\t\t# number of processes\n", nprocs);
249     fflush(stdout); /* report OUR options */
250     rtargv[rtargc++] = header ? "-h+" : "-h-";
251     sprintf(fmt, "-f%c%c", inpfmt, outfmt);
252     rtargv[rtargc++] = fmt;
253     rtargv[rtargc++] = "-x";
254     sprintf(sxres, "%d", xres);
255     rtargv[rtargc++] = sxres;
256     rtargv[rtargc++] = "-y";
257     sprintf(syres, "%d", yres);
258     rtargv[rtargc++] = syres;
259     rtargv[rtargc++] = "-oW";
260     rtargv[rtargc++] = "-defaults";
261     rtargv[rtargc] = NULL;
262     rtpath = getpath(rtargv[0], getenv("PATH"), X_OK);
263     if (rtpath == NULL) {
264     eputs(rtargv[0]);
265     eputs(": command not found\n");
266     exit(1);
267     }
268     execv(rtpath, rtargv);
269     perror(rtpath); /* execv() should not return */
270     exit(1);
271     }
272     if (!nmods)
273     error(USER, "No modifiers specified");
274     if (argc < 2 || argv[argc-1][0] == '-')
275     error(USER, "missing octree argument");
276     /* add format string */
277     sprintf(fmt, "-f%cf", inpfmt);
278     rtargv[rtargc++] = fmt;
279     /* octree argument is last */
280     rtargv[rtargc++] = octree = argv[argc-1];
281     rtargv[rtargc] = NULL;
282     /* start rtrace & sum contributions */
283     init(nprocs);
284     tracecontribs(stdin);
285     quit(0);
286     }
287    
288     /* kill persistent rtrace process */
289     static void
290     killpersist(void)
291     {
292     FILE *fp = fopen(persistfn, "r");
293     int pid;
294    
295     if (fp == NULL)
296     return;
297     if (fscanf(fp, "%*s %d", &pid) != 1 || kill(pid, SIGALRM) < 0)
298     unlink(persistfn);
299     fclose(fp);
300     }
301    
302     /* close rtrace processes and clean up */
303     int
304     done_rprocs(struct rtproc *rtp)
305     {
306     int st0, st1 = 0;
307    
308     if (rtp->next != NULL) { /* close last opened first! */
309     st1 = done_rprocs(rtp->next);
310     free((void *)rtp->next);
311     rtp->next = NULL;
312     }
313     st0 = close_process(&rtp->pd);
314     if (st0 < 0)
315     error(WARNING, "unknown return status from rtrace process");
316     else if (st0 > 0)
317     return(st0);
318     return(st1);
319     }
320    
321     /* exit with status */
322     void
323     quit(int status)
324     {
325     int rtstat;
326    
327     if (rt0.next != NULL) /* terminate persistent rtrace */
328     killpersist();
329     /* clean up rtrace process(es) */
330     rtstat = done_rprocs(&rt0);
331     if (status == 0)
332     status = rtstat;
333     exit(status); /* flushes all output streams */
334     }
335    
336     /* start rtrace and initialize buffers */
337     void
338     init(int np)
339     {
340     struct rtproc *rtp;
341     int i;
342     int maxbytes;
343     /* assign ray variables */
344     scompile("Dx=$1;Dy=$2;Dz=$3;", NULL, 0);
345     scompile("Px=$4;Py=$5;Pz=$6;", NULL, 0);
346     /* set up signal handling */
347     #ifdef SIGPIPE /* not present on Windows */
348     signal(SIGPIPE, quit);
349     #endif
350     rtp = &rt0; /* start rtrace process(es) */
351     for (i = 0; i++ < np; ) {
352     errno = 0;
353     maxbytes = open_process(&rtp->pd, rtargv);
354     if (maxbytes == 0) {
355     eputs(rtargv[0]);
356     eputs(": command not found\n");
357     exit(1);
358     }
359     if (maxbytes < 0)
360     error(SYSTEM, "cannot start rtrace process");
361     if (maxbytes > treebufsiz)
362     treebufsiz = maxbytes;
363     rtp->bsiz = 0;
364     rtp->buf = NULL;
365     rtp->raynum = 0;
366     if (i == np) /* last process? */
367     break;
368     if (i == 1)
369     sleep(2); /* wait for persist file */
370     rtp->next = (struct rtproc *)malloc(sizeof(struct rtproc));
371     if (rtp->next == NULL)
372     error(SYSTEM, "out of memory in init");
373     rtp = rtp->next;
374     }
375     rtp->next = NULL; /* terminate list */
376     if (yres > 0) {
377     if (xres > 0)
378     raysleft = xres*yres;
379     else
380     raysleft = yres;
381     } else
382     raysleft = 0;
383     waitflush = xres;
384     }
385    
386     /* add modifier to our list to track */
387     MODCONT *
388     addmodifier(char *modn, char *outf, char *binv)
389     {
390     LUENT *lep = lu_find(&modconttab, modn);
391     MODCONT *mp;
392    
393     if (lep->data != NULL) {
394     sprintf(errmsg, "duplicate modifier '%s'", modn);
395     error(USER, errmsg);
396     }
397     if (nmods >= MAXMODLIST)
398     error(USER, "too many modifiers");
399     modname[nmods++] = modn; /* XXX assumes static string */
400     lep->key = modn; /* XXX assumes static string */
401     mp = (MODCONT *)malloc(sizeof(MODCONT));
402     if (mp == NULL)
403     error(SYSTEM, "out of memory in addmodifier");
404     lep->data = (char *)mp;
405     mp->outspec = outf; /* XXX assumes static string */
406     mp->modname = modn; /* XXX assumes static string */
407     if (binv != NULL)
408     mp->binv = eparse(binv);
409     else
410     mp->binv = eparse("0");
411     mp->nbins = 1;
412     setcolor(mp->cbin[0], 0., 0., 0.);
413     return mp;
414     }
415    
416     /* put string to stderr */
417     void
418     eputs(char *s)
419     {
420     static int midline = 0;
421    
422     if (!*s) return;
423     if (!midline) {
424     fputs(progname, stderr);
425     fputs(": ", stderr);
426     }
427     fputs(s, stderr);
428     midline = s[strlen(s)-1] != '\n';
429     }
430    
431     /* write header to the given output stream */
432     void
433     printheader(FILE *fout)
434     {
435     extern char VersionID[];
436     FILE *fin = fopen(octree, "r");
437    
438     if (fin == NULL)
439     quit(1);
440     getheader(fin, (gethfunc *)fputs, fout); /* copy octree header */
441     fclose(fin);
442     printargs(gargc-1, gargv, fout); /* add our command */
443     fprintf(fout, "SOFTWARE= %s\n", VersionID);
444     fputnow(fout);
445     switch (outfmt) { /* add output format */
446     case 'a':
447     fputformat("ascii", fout);
448     break;
449     case 'f':
450     fputformat("float", fout);
451     break;
452     case 'd':
453     fputformat("double", fout);
454     break;
455     case 'c':
456     fputformat(COLRFMT, fout);
457     break;
458     }
459     fputc('\n', fout);
460     if (xres > 0) {
461     if (yres > 0) /* resolution string */
462     fprtresolu(xres, yres, fout);
463     fflush(fout);
464     }
465     }
466    
467     /* Get output file pointer (open and write header if new) */
468     FILE *
469     getofile(const char *ospec, const char *mname, int bn)
470     {
471     static int using_stdout = 0;
472     const char *mnp = NULL;
473     const char *bnp = NULL;
474     const char *cp;
475     char ofname[1024];
476     LUENT *lep;
477    
478     if (ospec == NULL) { /* use stdout? */
479     if (!using_stdout && header)
480     printheader(stdout);
481     using_stdout = 1;
482     return stdout;
483     }
484     for (cp = ospec; *cp; cp++) /* check format position(s) */
485     if (*cp == '%') {
486     do
487     ++cp;
488     while (isdigit(*cp));
489     switch (*cp) {
490     case '%':
491     break;
492     case 's':
493     if (mnp != NULL)
494     goto badspec;
495     mnp = cp;
496     break;
497     case 'd':
498     if (bnp != NULL)
499     goto badspec;
500     bnp = cp;
501     break;
502     default:
503     goto badspec;
504     }
505     }
506     if (mnp != NULL) { /* create file name */
507     if (bnp != NULL) {
508     if (bnp > mnp)
509     sprintf(ofname, ospec, mname, bn);
510     else
511     sprintf(ofname, ospec, bn, mname);
512     } else
513     sprintf(ofname, ospec, mname);
514     } else if (bnp != NULL)
515     sprintf(ofname, ospec, bn);
516     else
517     strcpy(ofname, ospec);
518     lep = lu_find(&ofiletab, ofname); /* look it up */
519     if (lep->key == NULL) /* new entry */
520     lep->key = strcpy((char *)malloc(strlen(ofname)+1), ofname);
521     if (lep->data == NULL) { /* open output file */
522     lep->data = (char *)fopen(ofname, "w");
523     if (lep->data == NULL) {
524     sprintf(errmsg, "cannot open '%s' for writing", ofname);
525     error(SYSTEM, errmsg);
526     }
527     if (header)
528     printheader((FILE *)lep->data);
529     }
530     return (FILE *)lep->data; /* return open file pointer */
531     badspec:
532     sprintf(errmsg, "bad output format '%s'", ospec);
533     error(USER, errmsg);
534     return NULL; /* pro forma return */
535     }
536    
537     /* read input ray into buffer */
538     int
539     getinp(char *buf, FILE *fp)
540     {
541     switch (inpfmt) {
542     case 'a':
543     if (fgets(buf, 128, fp) == NULL)
544     return 0;
545     return strlen(buf);
546     case 'f':
547     if (fread(buf, sizeof(float), 6, fp) < 6)
548     return 0;
549     return sizeof(float)*6;
550     case 'd':
551     if (fread(buf, sizeof(double), 6, fp) < 6)
552     return 0;
553     return sizeof(double)*6;
554     }
555     error(INTERNAL, "botched input format");
556     return 0; /* pro forma return */
557     }
558    
559     static const float *rparams = NULL; /* ray parameter pointer */
560    
561     /* return channel (ray) value */
562     double
563     chanvalue(int n)
564     {
565     if (--n < 0 || n >= 6)
566     error(USER, "illegal channel number ($N)");
567     if (rparams == NULL)
568     error(USER, "illegal use of $N in constant expression");
569     return rparams[n];
570     }
571    
572     /* add ray contribution to the appropriate modifier bin */
573     void
574     add_contrib(const char *modn, float rayval[9])
575     {
576     LUENT *le = lu_find(&modconttab, modn);
577     MODCONT *mp = (MODCONT *)le->data;
578     int bn;
579    
580     if (mp == NULL) {
581     sprintf(errmsg, "unexpected modifier '%s' from rtrace", modn);
582     error(USER, errmsg);
583     }
584     rparams = rayval + 3; /* for chanvalue */
585     eclock++;
586     bn = (int)(evalue(mp->binv) + .5);
587     if (bn <= 0)
588     bn = 0;
589     else if (bn > mp->nbins) { /* new bin */
590     mp = (MODCONT *)realloc(mp, sizeof(MODCONT) +
591     bn*sizeof(COLOR));
592     if (mp == NULL)
593     error(SYSTEM, "out of memory in add_contrib");
594     memset(mp->cbin+mp->nbins, 0, sizeof(COLOR)*(bn+1-mp->nbins));
595     mp->nbins = bn+1;
596     le->data = (char *)mp;
597     }
598     addcolor(mp->cbin[bn], rayval);
599     rparams = NULL;
600     }
601    
602     /* output newline to ASCII file and/or flush as requested */
603     static int
604     puteol(const LUENT *e, void *p)
605     {
606     FILE *fp = (FILE *)e->data;
607    
608     if (outfmt == 'a')
609     putc('\n', fp);
610     if (!waitflush)
611     fflush(fp);
612     if (ferror(fp)) {
613     sprintf(errmsg, "write error on file '%s'", e->key);
614     error(SYSTEM, errmsg);
615     }
616     return 0;
617     }
618    
619     /* output ray tallies and clear for next primary */
620     void
621     done_contrib(void)
622     {
623     int i, j;
624     MODCONT *mp;
625     FILE *fout;
626     double dv[3];
627     COLR cv;
628     /* output modifiers in order */
629     for (i = 0; i < nmods; i++) {
630     mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
631     for (j = 0; j < mp->nbins; j++) {
632     fout = getofile(mp->outspec, mp->modname, j);
633     switch (outfmt) {
634     case 'a':
635     fprintf(fout, "%.6e\t%.6e\t%.6e\t",
636     mp->cbin[j][RED],
637     mp->cbin[j][GRN],
638     mp->cbin[j][BLU]);
639     break;
640     case 'f':
641     fwrite(mp->cbin[j], sizeof(float), 3, fout);
642     break;
643     case 'd':
644     dv[0] = mp->cbin[j][0];
645     dv[1] = mp->cbin[j][1];
646     dv[2] = mp->cbin[j][2];
647     fwrite(dv, sizeof(double), 3, fout);
648     break;
649     case 'c':
650     setcolr(cv, mp->cbin[j][RED],
651     mp->cbin[j][GRN], mp->cbin[j][BLU]);
652     fwrite(cv, sizeof(COLR), 1, fout);
653     break;
654     default:
655     error(INTERNAL, "botched output format");
656     }
657     }
658     /* clear for next ray tree */
659     memset(mp->cbin, 0, sizeof(COLOR)*mp->nbins);
660     }
661     --waitflush; /* terminate records */
662     lu_doall(&ofiletab, puteol, NULL);
663     if (!waitflush)
664     waitflush = xres;
665     }
666    
667     /* process (or save) ray tree produced by rtrace process */
668     void
669     process_rays(struct rtproc *rtp)
670     {
671     struct rtproc *rtu;
672     /* check if time to process it */
673     if (rtp->raynum == lastdone+1) {
674     int n = rtp->bpos - rtp->buf;
675     const char *cp = rtp->buf;
676     while (n > 0) { /* process rays */
677     char matname[128];
678     char *mnp = matname;
679     /* skip leading tabs */
680     while (n > 0 && *cp == '\t') {
681     cp++; n--;
682     }
683     /* get material name */
684     while (n > 0 && *cp != '\t') {
685     *mnp++ = *cp++; n--;
686     }
687     if (!n)
688     error(USER, "botched modifer name from rtrace");
689     *mnp = '\0';
690     cp++; n--; /* skip terminating tab */
691     if (n < (int)(sizeof(float)*9))
692     error(USER, "incomplete ray value from rtrace");
693     /* add ray contribution */
694     add_contrib(matname, (float *)cp);
695     cp += sizeof(float)*9; n -= sizeof(float)*9;
696     }
697     done_contrib(); /* sum up contributions & output */
698     lastdone = rtp->raynum;
699     free(rtp->buf);
700     /* catch up with unprocessed list */
701     while (rt_unproc != NULL && rt_unproc->raynum == lastdone+1) {
702     process_rays(rt_unproc);
703     rt_unproc = (rtu=rt_unproc)->next;
704     free(rtu);
705     }
706     } else { /* else insert in unprocessed list */
707     struct rtproc *rtl = NULL;
708     for (rtu = rt_unproc; rtu != NULL; rtu = (rtl=rtu)->next)
709     if (rtp->raynum < rtu->raynum)
710     break;
711     rtu = (struct rtproc *)malloc(sizeof(struct rtproc));
712     if (rtu == NULL)
713     error(SYSTEM, "out of memory in process_rays");
714     *rtu = *rtp;
715     if (rtl == NULL) {
716     rtu->next = rt_unproc;
717     rt_unproc = rtu;
718     } else {
719     rtu->next = rtl->next;
720     rtl->next = rtu;
721     }
722     }
723     rtp->raynum = 0; /* clear path for next ray tree */
724     rtp->bsiz = 0;
725     rtp->buf = NULL;
726     }
727    
728     /* wait for rtrace process to finish with ray tree */
729     struct rtproc *
730     wait_rproc(void)
731     {
732     struct rtproc *rtfree = NULL;
733     fd_set readset, errset;
734     int nr;
735     struct rtproc *rt;
736     int n;
737    
738     do {
739     nr = 0; /* prepare select call */
740     FD_ZERO(&readset); FD_ZERO(&errset); n = 0;
741     for (rt = &rt0; rt != NULL; rt = rt->next) {
742     if (rt->raynum) {
743     FD_SET(rt->pd.r, &readset);
744     ++nr;
745     }
746     FD_SET(rt->pd.r, &errset);
747     if (rt->pd.r >= n)
748     n = rt->pd.r + 1;
749     }
750     if (!nr) /* no rays pending */
751     break;
752     if (nr > 1) { /* call select for multiple proc's */
753     errno = 0;
754     if (select(n, &readset, NULL, &errset, NULL) < 0)
755     error(SYSTEM, "select call error in wait_rproc()");
756     } else
757     FD_ZERO(&errset);
758     nr = 0;
759     for (rt = &rt0; rt != NULL; rt = rt->next) {
760     if (!FD_ISSET(rt->pd.r, &readset) &&
761     !FD_ISSET(rt->pd.r, &errset))
762     continue;
763     if (rt->buf == NULL) {
764     rt->bsiz = treebufsiz;
765     rt->buf = (char *)malloc(treebufsiz);
766     } else if (rt->bpos + BUFSIZ > rt->buf + rt->bsiz) {
767     if (rt->bsiz + BUFSIZ <= treebufsiz)
768     rt->bsiz = treebufsiz;
769     else
770     rt->bsiz = treebufsiz += BUFSIZ;
771     rt->buf = (char *)realloc(rt->buf, rt->bsiz);
772     }
773     if (rt->buf == NULL)
774     error(SYSTEM, "out of memory in wait_rproc");
775     nr = read(rt->pd.r, rt->bpos,
776     rt->buf + rt->bsiz - rt->bpos);
777     if (!nr)
778     error(USER, "rtrace process died");
779     rt->bpos += nr; /* advance buffer & check */
780     if (rt->bpos[-1] == '\t' && rt->bpos[-2] == '-') {
781     rt->bpos -= 2; /* elide terminator */
782     process_rays(rt);
783     rtfree = rt; /* ready for next ray */
784     }
785     }
786     } while ((rtfree == NULL) & (nr > 0)); /* repeat until ready or out */
787     return rtfree;
788     }
789    
790     /* return next available rtrace process */
791     struct rtproc *
792     get_rproc(void)
793     {
794     struct rtproc *rtp;
795     /* check for idle rtrace */
796     for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
797     if (!rtp->raynum)
798     return rtp;
799     return wait_rproc(); /* need to wait for one */
800     }
801    
802     /* trace ray contributions (main loop) */
803     void
804     tracecontribs(FILE *fin)
805     {
806     char inpbuf[128];
807     int iblen;
808     struct rtproc *rtp;
809     /* loop over input */
810     while ((iblen = getinp(inpbuf, fin)) > 0) {
811     if (lastray+1 < lastray) { /* counter rollover? */
812     while (wait_rproc() != NULL)
813     ;
814     lastdone = lastray = 0;
815     }
816     rtp = get_rproc(); /* get avail. rtrace process */
817     rtp->raynum = ++lastray; /* assign this ray to it */
818     writebuf(rtp->pd.w, inpbuf, iblen);
819     if (!--raysleft)
820     break; /* explicit EOF */
821     }
822     while (wait_rproc() != NULL) /* process outstanding rays */
823     ;
824     }