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