ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rc2.c
Revision: 2.26
Committed: Wed Nov 15 18:02:53 2023 UTC (6 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.25: +57 -55 lines
Log Message:
feat(rpict,rtrace,rcontrib,rtpict): Hyperspectral rendering (except photon map)

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rc2.c,v 2.25 2023/04/07 00:03:26 greg Exp $";
3 #endif
4 /*
5 * Accumulate ray contributions for a set of materials
6 * File i/o and recovery
7 */
8
9 #include <ctype.h>
10 #include "platform.h"
11 #include "rcontrib.h"
12 #include "resolu.h"
13
14 /* Close output stream and free record */
15 static void
16 closestream(void *p)
17 {
18 STREAMOUT *sop = (STREAMOUT *)p;
19
20 if (sop->ofp != NULL) {
21 int status = 0;
22 if (sop->outpipe)
23 status = pclose(sop->ofp);
24 else if (sop->ofp != stdout)
25 status = fclose(sop->ofp);
26 if (status)
27 error(SYSTEM, "error closing output stream");
28 }
29 free(p);
30 }
31
32 LUTAB ofiletab = LU_SINIT(free,closestream); /* output file table */
33
34 #define OF_MODIFIER 01
35 #define OF_BIN 02
36
37 /************************** STREAM & FILE I/O ***************************/
38
39 /* Construct output file name and return flags whether modifier/bin present */
40 static int
41 ofname(char *oname, const char *ospec, const char *mname, int bn)
42 {
43 const char *mnp = NULL;
44 const char *bnp = NULL;
45 const char *cp;
46
47 if (ospec == NULL)
48 return(-1);
49 for (cp = ospec; *cp; cp++) /* check format position(s) */
50 if (*cp == '%') {
51 do
52 ++cp;
53 while (isdigit(*cp));
54 switch (*cp) {
55 case '%':
56 break;
57 case 's':
58 if (mnp != NULL)
59 return(-1);
60 mnp = cp;
61 break;
62 case 'd':
63 case 'i':
64 case 'o':
65 case 'x':
66 case 'X':
67 if (bnp != NULL)
68 return(-1);
69 bnp = cp;
70 break;
71 default:
72 return(-1);
73 }
74 }
75 if (mnp != NULL) { /* create file name */
76 if (bnp != NULL) {
77 if (bnp > mnp)
78 sprintf(oname, ospec, mname, bn);
79 else
80 sprintf(oname, ospec, bn, mname);
81 return(OF_MODIFIER|OF_BIN);
82 }
83 sprintf(oname, ospec, mname);
84 return(OF_MODIFIER);
85 }
86 if (bnp != NULL) {
87 sprintf(oname, ospec, bn);
88 return(OF_BIN);
89 }
90 strcpy(oname, ospec);
91 return(0);
92 }
93
94
95 /* Write header to the given output stream */
96 static void
97 printheader(FILE *fout, const char *info)
98 {
99 extern char VersionID[];
100 /* copy octree header */
101 if (octname[0] == '!') {
102 newheader("RADIANCE", fout);
103 fputs(octname+1, fout);
104 if (octname[strlen(octname)-1] != '\n')
105 fputc('\n', fout);
106 } else {
107 FILE *fin = fopen(octname, (outfmt=='a') ? "r" : "rb");
108 if (fin == NULL)
109 quit(1);
110 checkheader(fin, OCTFMT, fout);
111 fclose(fin);
112 }
113 printargs(gargc-1, gargv, fout); /* add our command */
114 fprintf(fout, "SOFTWARE= %s\n", VersionID);
115 fputnow(fout);
116 fputncomp(NCSAMP, fout);
117 if (info != NULL) /* add extra info if given */
118 fputs(info, fout);
119 if ((outfmt == 'f') | (outfmt == 'd'))
120 fputendian(fout);
121 fputformat(formstr(outfmt), fout);
122 fputc('\n', fout); /* empty line ends header */
123 }
124
125
126 /* Write resolution string to given output stream */
127 static void
128 printresolu(FILE *fout, int xr, int yr)
129 {
130 if ((xr > 0) & (yr > 0)) /* resolution string */
131 fprtresolu(xr, yr, fout);
132 }
133
134
135 /* Get output stream pointer (open and write header if new and noopen==0) */
136 STREAMOUT *
137 getostream(const char *ospec, const char *mname, int bn, int noopen)
138 {
139 static STREAMOUT stdos;
140 char info[1024];
141 int ofl;
142 char oname[1024];
143 LUENT *lep;
144 STREAMOUT *sop;
145 char *cp;
146
147 info[0] = '\0';
148 if (ospec == NULL) { /* use stdout? */
149 if (!noopen & !using_stdout) {
150 if (outfmt != 'a')
151 SET_FILE_BINARY(stdout);
152 #ifdef getc_unlocked
153 flockfile(stdout); /* avoid lock/unlock overhead */
154 #endif
155 if (header) {
156 cp = info;
157 if (yres > 0) {
158 sprintf(cp, "NROWS=%d\n", yres *
159 (xres + !xres) );
160 while (*cp) ++cp;
161 }
162 if ((xres <= 0) | (stdos.reclen > 1))
163 sprintf(cp, "NCOLS=%d\n", stdos.reclen);
164 printheader(stdout, info);
165 }
166 if (stdos.reclen == 1)
167 printresolu(stdout, xres, yres);
168 if (waitflush > 0)
169 fflush(stdout);
170 stdos.xr = xres; stdos.yr = yres;
171 using_stdout = 1;
172 }
173 stdos.ofp = stdout;
174 stdos.reclen += noopen;
175 return(&stdos);
176 }
177 ofl = ofname(oname, ospec, mname, bn); /* get output name */
178 if (ofl < 0) {
179 sprintf(errmsg, "bad output format '%s'", ospec);
180 error(USER, errmsg);
181 }
182 lep = lu_find(&ofiletab, oname); /* look it up */
183 if (lep->key == NULL) /* new entry */
184 lep->key = strcpy((char *)malloc(strlen(oname)+1), oname);
185 sop = (STREAMOUT *)lep->data;
186 if (sop == NULL) { /* allocate stream */
187 sop = (STREAMOUT *)malloc(sizeof(STREAMOUT));
188 if (sop == NULL)
189 error(SYSTEM, "out of memory in getostream");
190 sop->outpipe = (oname[0] == '!');
191 sop->reclen = 0;
192 sop->ofp = NULL; /* open iff noopen==0 */
193 sop->xr = xres; sop->yr = yres;
194 lep->data = (char *)sop;
195 if (!sop->outpipe & !force_open & !recover &&
196 access(oname, F_OK) == 0) {
197 errno = EEXIST; /* file exists */
198 goto openerr;
199 }
200 } else if (noopen && outfmt == 'c' && /* stream exists to picture? */
201 (sop->xr > 0) & (sop->yr > 0)) {
202 if (ofl & OF_BIN)
203 return(NULL); /* let caller offset bins */
204 sprintf(errmsg, "output '%s' not a valid picture", oname);
205 error(WARNING, errmsg);
206 }
207 if (!noopen && sop->ofp == NULL) { /* open output stream */
208 if (oname[0] == '!') /* output to command */
209 sop->ofp = popen(oname+1, "w");
210 else /* else open file */
211 sop->ofp = fopen(oname, "w");
212 if (sop->ofp == NULL)
213 goto openerr;
214 if (outfmt != 'a')
215 SET_FILE_BINARY(sop->ofp);
216 #ifdef getc_unlocked
217 flockfile(sop->ofp); /* avoid lock/unlock overhead */
218 #endif
219 if (accumulate > 0) { /* global resolution */
220 sop->xr = xres; sop->yr = yres;
221 }
222 if (header) {
223 cp = info;
224 if (ofl & OF_MODIFIER || sop->reclen == 1) {
225 sprintf(cp, "MODIFIER=%s\n", mname);
226 while (*cp) ++cp;
227 }
228 if (ofl & OF_BIN) {
229 sprintf(cp, "BIN=%d\n", bn);
230 while (*cp) ++cp;
231 }
232 if (sop->yr > 0) {
233 sprintf(cp, "NROWS=%d\n", sop->yr *
234 (sop->xr + !sop->xr) );
235 while (*cp) ++cp;
236 }
237 if ((sop->xr <= 0) | (sop->reclen > 1))
238 sprintf(cp, "NCOLS=%d\n", sop->reclen);
239 printheader(sop->ofp, info);
240 }
241 if (sop->reclen == 1)
242 printresolu(sop->ofp, sop->xr, sop->yr);
243 if (waitflush > 0)
244 fflush(sop->ofp);
245 }
246 sop->reclen += noopen; /* add to length if noopen */
247 return(sop); /* return output stream */
248 openerr:
249 sprintf(errmsg, "cannot open '%s' for writing", oname);
250 error(SYSTEM, errmsg);
251 return(NULL); /* pro forma return */
252 }
253
254
255 /* Get a vector from stdin */
256 int
257 getvec(FVECT vec)
258 {
259 float vf[3];
260 double vd[3];
261 char buf[32];
262 int i;
263
264 switch (inpfmt) {
265 case 'a': /* ascii */
266 for (i = 0; i < 3; i++) {
267 if (fgetword(buf, sizeof(buf), stdin) == NULL ||
268 !isflt(buf))
269 return(-1);
270 vec[i] = atof(buf);
271 }
272 break;
273 case 'f': /* binary float */
274 if (getbinary(vf, sizeof(float), 3, stdin) != 3)
275 return(-1);
276 VCOPY(vec, vf);
277 break;
278 case 'd': /* binary double */
279 if (getbinary(vd, sizeof(double), 3, stdin) != 3)
280 return(-1);
281 VCOPY(vec, vd);
282 break;
283 default:
284 error(CONSISTENCY, "botched input format");
285 }
286 return(0);
287 }
288
289
290 /* Put out ray contribution to file */
291 static void
292 put_contrib(const DCOLORV *cnt, FILE *fout)
293 {
294 double sf = 1;
295 SCOLOR fv;
296 SCOLR cv;
297 int i;
298
299 if (accumulate > 1)
300 sf = 1./(double)accumulate;
301 switch (outfmt) {
302 case 'a':
303 for (i = 0; i < NCSAMP; i++)
304 fprintf(fout, "%.6e\t", sf*cnt[i]);
305 break;
306 case 'f':
307 for (i = NCSAMP; i-- > 0; )
308 fv[i] = cnt[i];
309 if (accumulate > 1)
310 scalescolor(fv, sf);
311 putbinary(fv, sizeof(COLORV), NCSAMP, fout);
312 break;
313 case 'd':
314 if (accumulate > 1) {
315 DCOLORV dv[MAXCSAMP];
316 for (i = NCSAMP; i-- > 0; )
317 dv[i] = sf*cnt[i];
318 putbinary(dv, sizeof(DCOLORV), NCSAMP, fout);
319 } else
320 putbinary(cnt, sizeof(DCOLORV), NCSAMP, fout);
321 break;
322 case 'c':
323 for (i = NCSAMP; i-- > 0; )
324 fv[i] = cnt[i];
325 if (accumulate > 1)
326 scalescolor(fv, sf);
327 scolor_scolr(cv, fv);
328 putbinary(cv, 1, LSCOLR, fout);
329 break;
330 default:
331 error(INTERNAL, "botched output format");
332 }
333 }
334
335
336 /* Output modifier values to appropriate stream(s) */
337 void
338 mod_output(MODCONT *mp)
339 {
340 STREAMOUT *sop = getostream(mp->outspec, mp->modname, mp->bin0, 0);
341 int j;
342
343 put_contrib(mp->cbin, sop->ofp);
344 if (mp->nbins > 3 && /* minor optimization */
345 sop == getostream(mp->outspec, mp->modname, mp->bin0+1, 0)) {
346 for (j = 1; j < mp->nbins; j++)
347 put_contrib(mcbin(mp,j), sop->ofp);
348 } else {
349 for (j = 1; j < mp->nbins; j++) {
350 sop = getostream(mp->outspec, mp->modname, mp->bin0+j, 0);
351 put_contrib(mcbin(mp,j), sop->ofp);
352 }
353 }
354 }
355
356
357 /* callback to output newline to ASCII file and/or flush as requested */
358 static int
359 puteol(const LUENT *e, void *p)
360 {
361 STREAMOUT *sop = (STREAMOUT *)e->data;
362
363 if (outfmt == 'a')
364 putc('\n', sop->ofp);
365 if (!waitflush)
366 fflush(sop->ofp);
367 if (ferror(sop->ofp)) {
368 sprintf(errmsg, "write error on file '%s'", e->key);
369 error(SYSTEM, errmsg);
370 }
371 return(0);
372 }
373
374
375 /* Terminate record output and flush if time */
376 void
377 end_record()
378 {
379 --waitflush;
380 lu_doall(&ofiletab, &puteol, NULL);
381 if (using_stdout & (outfmt == 'a'))
382 putc('\n', stdout);
383 if (!waitflush) {
384 waitflush = (yres > 0) & (xres > 1) ? 0 : xres;
385 if (using_stdout)
386 fflush(stdout);
387 }
388 }
389
390 /************************** PARTIAL RESULTS RECOVERY ***********************/
391
392 /* Get ray contribution from previous file */
393 static int
394 get_contrib(DCOLORV *cnt, FILE *finp)
395 {
396 SCOLOR fv;
397 SCOLR cv;
398 int i;
399
400 switch (outfmt) {
401 case 'a':
402 for (i = 0; i < NCSAMP; i++)
403 if (fscanf(finp, "%lf", &cnt[i]) != 1)
404 return(0);
405 return(1);
406 case 'd':
407 return(getbinary(cnt, sizeof(DCOLORV), NCSAMP, finp) == NCSAMP);
408 case 'f':
409 if (getbinary(fv, sizeof(COLORV), NCSAMP, finp) != NCSAMP)
410 return(0);
411 break;
412 case 'c':
413 if (getbinary(cv, 1, LSCOLR, finp) != LSCOLR)
414 return(0);
415 scolr_scolor(fv, cv);
416 break;
417 default:
418 error(INTERNAL, "botched output format");
419 }
420 /* copy result from SCOLOR */
421 for (i = NCSAMP; i-- > 0; )
422 cnt[i] = fv[i];
423 return(1);
424 }
425
426
427 /* Close output file opened for input */
428 static int
429 myclose(const LUENT *e, void *p)
430 {
431 STREAMOUT *sop = (STREAMOUT *)e->data;
432
433 if (sop->ofp == NULL)
434 return(0);
435 fclose(sop->ofp);
436 sop->ofp = NULL;
437 return(0);
438 }
439
440
441 /* Load previously accumulated values */
442 void
443 reload_output()
444 {
445 int i, j;
446 MODCONT *mp;
447 int ofl;
448 char oname[1024];
449 char *fmode = "rb";
450 char *outvfmt;
451 LUENT *oent;
452 int xr, yr;
453 STREAMOUT *sop;
454 DCOLORV contr[MAXCSAMP];
455
456 if (outfmt == 'a')
457 fmode = "r";
458 outvfmt = (char *)formstr(outfmt);
459 /* reload modifier values */
460 for (i = 0; i < nmods; i++) {
461 mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
462 if (mp->outspec == NULL)
463 error(USER, "cannot reload from stdout");
464 if (mp->outspec[0] == '!')
465 error(USER, "cannot reload from command");
466 for (j = 0; j < mp->nbins; j++) { /* load each modifier bin */
467 ofl = ofname(oname, mp->outspec, mp->modname, mp->bin0+j);
468 if (ofl < 0)
469 error(USER, "bad output file specification");
470 oent = lu_find(&ofiletab, oname);
471 if (oent->data == NULL)
472 error(INTERNAL, "unallocated stream in reload_output()");
473 sop = (STREAMOUT *)oent->data;
474 if (sop->ofp == NULL) { /* open output as input */
475 sop->ofp = fopen(oname, fmode);
476 if (sop->ofp == NULL) {
477 sprintf(errmsg, "missing reload file '%s'",
478 oname);
479 error(WARNING, errmsg);
480 break;
481 }
482 #ifdef getc_unlocked
483 flockfile(sop->ofp);
484 #endif
485 if (header && checkheader(sop->ofp, outvfmt, NULL) != 1) {
486 sprintf(errmsg, "format mismatch for '%s'",
487 oname);
488 error(USER, errmsg);
489 }
490 if ((sop->reclen == 1) & (sop->xr > 0) & (sop->yr > 0) &&
491 (!fscnresolu(&xr, &yr, sop->ofp) ||
492 (xr != sop->xr) |
493 (yr != sop->yr))) {
494 sprintf(errmsg, "resolution mismatch for '%s'",
495 oname);
496 error(USER, errmsg);
497 }
498 }
499 /* read in spectral value */
500 if (!get_contrib(contr, sop->ofp)) {
501 if (!j) {
502 fclose(sop->ofp);
503 break; /* ignore empty file */
504 }
505 if (j < mp->nbins) {
506 sprintf(errmsg, "missing data in '%s'",
507 oname);
508 error(USER, errmsg);
509 }
510 break;
511 }
512 memcpy(mcbin(mp,j), contr, DCOLORSIZ);
513 }
514 }
515 lu_doall(&ofiletab, &myclose, NULL); /* close all files */
516 }
517
518
519 /* Seek on the given output file */
520 static int
521 myseeko(const LUENT *e, void *p)
522 {
523 STREAMOUT *sop = (STREAMOUT *)e->data;
524 off_t nbytes = *(off_t *)p;
525
526 if (sop->reclen > 1)
527 nbytes *= (off_t)sop->reclen;
528 if (fseeko(sop->ofp, nbytes, SEEK_CUR) < 0) {
529 sprintf(errmsg, "seek error on file '%s'", e->key);
530 error(SYSTEM, errmsg);
531 }
532 return(0);
533 }
534
535
536 /* Recover output if possible */
537 void
538 recover_output()
539 {
540 off_t lastout = -1;
541 int outvsiz, recsiz;
542 char *outvfmt;
543 int i, j;
544 MODCONT *mp;
545 int ofl;
546 char oname[1024];
547 LUENT *oent;
548 STREAMOUT *sop;
549 off_t nvals;
550 int xr, yr;
551
552 switch (outfmt) {
553 case 'a':
554 error(USER, "cannot recover ASCII output");
555 return;
556 case 'f':
557 outvsiz = sizeof(float)*NCSAMP;
558 break;
559 case 'd':
560 outvsiz = sizeof(double)*NCSAMP;
561 break;
562 case 'c':
563 outvsiz = LSCOLR;
564 break;
565 default:
566 error(INTERNAL, "botched output format");
567 return;
568 }
569 outvfmt = (char *)formstr(outfmt);
570 /* check modifier outputs */
571 for (i = 0; i < nmods; i++) {
572 mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
573 if (mp->outspec == NULL)
574 error(USER, "cannot recover from stdout");
575 if (mp->outspec[0] == '!')
576 error(USER, "cannot recover from command");
577 for (j = 0; j < mp->nbins; j++) { /* check each bin's file */
578 ofl = ofname(oname, mp->outspec, mp->modname, mp->bin0+j);
579 if (ofl < 0)
580 error(USER, "bad output file specification");
581 oent = lu_find(&ofiletab, oname);
582 if (oent->data == NULL)
583 error(INTERNAL, "unallocated stream in recover_output()");
584 sop = (STREAMOUT *)oent->data;
585 if (sop->ofp != NULL) { /* already open? */
586 if (ofl & OF_BIN)
587 continue;
588 break;
589 }
590 /* open output */
591 sop->ofp = fopen(oname, "rb+");
592 if (sop->ofp == NULL) {
593 sprintf(errmsg, "missing recover file '%s'",
594 oname);
595 error(WARNING, errmsg);
596 lastout = 0;
597 break;
598 }
599 nvals = lseek(fileno(sop->ofp), 0, SEEK_END);
600 if (nvals <= 0) {
601 lastout = 0; /* empty output, quit here */
602 fclose(sop->ofp);
603 break;
604 }
605 recsiz = outvsiz * sop->reclen;
606
607 lseek(fileno(sop->ofp), 0, SEEK_SET);
608 if (header && checkheader(sop->ofp, outvfmt, NULL) != 1) {
609 sprintf(errmsg, "format mismatch for '%s'",
610 oname);
611 error(USER, errmsg);
612 }
613 if ((sop->reclen == 1) & (sop->xr > 0) & (sop->yr > 0) &&
614 (!fscnresolu(&xr, &yr, sop->ofp) ||
615 (xr != sop->xr) |
616 (yr != sop->yr))) {
617 sprintf(errmsg, "resolution mismatch for '%s'",
618 oname);
619 error(USER, errmsg);
620 }
621 nvals = (nvals - (off_t)ftell(sop->ofp)) / recsiz;
622 if ((lastout < 0) | (nvals < lastout))
623 lastout = nvals;
624 if (!(ofl & OF_BIN))
625 break; /* no bin separation */
626 }
627 if (!lastout) { /* empty output */
628 error(WARNING, "no previous data to recover");
629 /* reclose all outputs */
630 lu_doall(&ofiletab, &myclose, NULL);
631 return;
632 }
633 }
634 if (lastout < 0) {
635 error(WARNING, "no output files to recover");
636 return;
637 }
638 if (raysleft && lastout >= raysleft/accumulate) {
639 error(WARNING, "output appears to be complete");
640 /* XXX should read & discard input? */
641 quit(0);
642 }
643 /* seek on all files */
644 nvals = lastout * outvsiz;
645 lu_doall(&ofiletab, &myseeko, &nvals);
646 /* skip repeated input */
647 lastout *= accumulate;
648 for (nvals = 0; nvals < lastout; nvals++) {
649 FVECT vdummy;
650 if (getvec(vdummy) < 0 || getvec(vdummy) < 0)
651 error(USER, "unexpected EOF on input");
652 }
653 lastray = lastdone = (RNUMBER)lastout;
654 if (raysleft)
655 raysleft -= lastray;
656 }