ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rtrace.c
Revision: 2.107
Committed: Wed Nov 15 18:02:53 2023 UTC (6 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.106: +85 -33 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: rtrace.c,v 2.106 2023/02/06 22:40:21 greg Exp $";
3 #endif
4 /*
5 * rtrace.c - program and variables for individual ray tracing.
6 */
7
8 #include "copyright.h"
9
10 /*
11 * Input is in the form:
12 *
13 * xorg yorg zorg xdir ydir zdir
14 *
15 * The direction need not be normalized. Output is flexible.
16 * If the direction vector is (0,0,0), then the output is flushed.
17 * All values default to ascii representation of real
18 * numbers. Binary representations can be selected
19 * with '-ff' for float or '-fd' for double. By default,
20 * radiance is computed. The '-i' or '-I' options indicate that
21 * irradiance values are desired.
22 */
23
24 #include <time.h>
25
26 #include "platform.h"
27 #include "ray.h"
28 #include "ambient.h"
29 #include "source.h"
30 #include "otypes.h"
31 #include "otspecial.h"
32 #include "resolu.h"
33 #include "random.h"
34
35 extern int inform; /* input format */
36 extern int outform; /* output format */
37 extern char *outvals; /* output values */
38
39 extern int imm_irrad; /* compute immediate irradiance? */
40 extern int lim_dist; /* limit distance? */
41
42 extern char *tralist[]; /* list of modifers to trace (or no) */
43 extern int traincl; /* include == 1, exclude == 0 */
44
45 extern int hresolu; /* horizontal resolution */
46 extern int vresolu; /* vertical resolution */
47
48 extern int castonly; /* only doing ray-casting? */
49
50 extern double (*sens_curve)(SCOLOR scol); /* spectral conversion for 1-channel */
51 extern double out_scalefactor; /* output calibration scale factor */
52 extern RGBPRIMP out_prims; /* output color primitives (NULL if spectral) */
53
54 #ifndef MAXTSET
55 #define MAXTSET 8191 /* maximum number in trace set */
56 #endif
57 OBJECT traset[MAXTSET+1]={0}; /* trace include/exclude set */
58
59 static int Tflag = 0; /* source tracing enabled? */
60
61 static RAY thisray; /* for our convenience */
62
63 static FILE *inpfp = NULL; /* input stream pointer */
64
65 static FVECT *inp_queue = NULL; /* ray input queue if flushing */
66 static int inp_qpos = 0; /* next ray to return */
67 static int inp_qend = 0; /* number of rays in this work group */
68
69 typedef void putf_t(RREAL *v, int n);
70 static putf_t puta, putd, putf;
71
72 typedef void oputf_t(RAY *r);
73 static oputf_t oputo, oputd, oputv, oputV, oputl, oputL, oputc, oputp,
74 oputr, oputR, oputx, oputX, oputn, oputN, oputs,
75 oputw, oputW, oputm, oputM, oputtilde;
76
77 extern void tranotify(OBJECT obj);
78 static int is_fifo(FILE *fp);
79 static void bogusray(void);
80 static void raycast(RAY *r);
81 static void rayirrad(RAY *r);
82 static void rtcompute(FVECT org, FVECT dir, double dmax);
83 static int printvals(RAY *r);
84 static int getvec(FVECT vec, int fmt, FILE *fp);
85 static int iszerovec(const FVECT vec);
86 static double nextray(FVECT org, FVECT dir);
87 static void tabin(RAY *r);
88 static void ourtrace(RAY *r);
89
90 static void putscolor(const COLORV *scol);
91
92 static oputf_t *ray_out[32], *every_out[32];
93 static putf_t *putreal;
94
95
96 void
97 quit( /* quit program */
98 int code
99 )
100 {
101 if (ray_pnprocs > 0) /* close children if any */
102 ray_pclose(0);
103 else if (ray_pnprocs < 0)
104 _exit(code); /* avoid flush() in child */
105 #ifndef NON_POSIX
106 else {
107 headclean(); /* delete header file */
108 pfclean(); /* clean up persist files */
109 }
110 #endif
111 exit(code);
112 }
113
114
115 const char *
116 formstr( /* return format identifier */
117 int f
118 )
119 {
120 switch (f) {
121 case 'a': return("ascii");
122 case 'f': return("float");
123 case 'd': return("double");
124 case 'c':
125 if (out_prims == NULL)
126 return(SPECFMT);
127 if (out_prims == xyzprims)
128 return(CIEFMT);
129 return(COLRFMT);
130 }
131 return("unknown");
132 }
133
134
135 static void
136 trace_sources(void) /* trace rays to light sources, also */
137 {
138 int sn;
139
140 for (sn = 0; sn < nsources; sn++)
141 source[sn].sflags |= SFOLLOW;
142 }
143
144
145 void
146 rtrace( /* trace rays from file */
147 char *fname,
148 int nproc
149 )
150 {
151 unsigned long vcount = (hresolu > 1) ? (unsigned long)hresolu*vresolu
152 : (unsigned long)vresolu;
153 long nextflush = (!vresolu | (hresolu <= 1)) * hresolu;
154 int something2flush = 0;
155 FILE *fp;
156 double d;
157 FVECT orig, direc;
158 /* set up input */
159 if (fname == NULL)
160 inpfp = stdin;
161 else if ((inpfp = fopen(fname, "r")) == NULL) {
162 sprintf(errmsg, "cannot open input file \"%s\"", fname);
163 error(SYSTEM, errmsg);
164 }
165 #ifdef getc_unlocked
166 flockfile(inpfp); /* avoid lock/unlock overhead */
167 flockfile(stdout);
168 #endif
169 if (inform != 'a')
170 SET_FILE_BINARY(inpfp);
171 /* set up output */
172 if (castonly || every_out[0] != NULL)
173 nproc = 1; /* don't bother multiprocessing */
174 if (every_out[0] != NULL) {
175 trace = ourtrace; /* enable full tree tracing */
176 if (Tflag) /* light sources, too? */
177 trace_sources();
178 }
179 if ((nextflush > 0) & (nproc > nextflush)) {
180 error(WARNING, "reducing number of processes to match flush interval");
181 nproc = nextflush;
182 }
183 if (nproc > 1) { /* start multiprocessing */
184 ray_popen(nproc);
185 ray_fifo_out = printvals;
186 }
187 if (hresolu > 0) {
188 if (vresolu > 0)
189 fprtresolu(hresolu, vresolu, stdout);
190 else
191 fflush(stdout);
192 }
193 /* process input rays */
194 while ((d = nextray(orig, direc)) >= 0.0) {
195 if (d == 0.0) { /* flush request? */
196 if (something2flush) {
197 if (ray_pnprocs > 1 && ray_fifo_flush() < 0)
198 error(USER, "child(ren) died");
199 bogusray();
200 fflush(stdout);
201 nextflush = (!vresolu | (hresolu <= 1)) * hresolu;
202 something2flush = 0;
203 } else
204 bogusray();
205 } else { /* compute and print */
206 rtcompute(orig, direc, lim_dist ? d : 0.0);
207 if (!--nextflush) { /* flush if time */
208 if (ray_pnprocs > 1 && ray_fifo_flush() < 0)
209 error(USER, "child(ren) died");
210 fflush(stdout);
211 nextflush = hresolu;
212 } else
213 something2flush = 1;
214 }
215 if (ferror(stdout))
216 error(SYSTEM, "write error");
217 if (vcount && !--vcount) /* check for end */
218 break;
219 }
220 if (ray_pnprocs > 1) { /* clean up children */
221 if (ray_fifo_flush() < 0)
222 error(USER, "unable to complete processing");
223 ray_pclose(0);
224 }
225 if (vcount)
226 error(WARNING, "unexpected EOF on input");
227 if (fflush(stdout) < 0)
228 error(SYSTEM, "write error");
229 if (fname != NULL) {
230 fclose(inpfp);
231 inpfp = NULL;
232 }
233 nextray(NULL, NULL);
234 }
235
236
237 int
238 setrtoutput(void) /* set up output tables, return #comp */
239 {
240 char *vs = outvals;
241 oputf_t **table = ray_out;
242 const int nco = (sens_curve != NULL) ? 1 :
243 (out_prims != NULL) ? 3 : NCSAMP;
244 int ncomp = 0;
245
246 if (!*vs)
247 error(USER, "empty output specification");
248
249 switch (outform) { /* make sure (*putreal)() calls someone! */
250 case 'a': putreal = puta; break;
251 case 'f': putreal = putf; break;
252 case 'd': putreal = putd; break;
253 case 'c':
254 if (outvals[1] || !strchr("vrx", outvals[0]))
255 error(USER, "color format only with -ov, -or, -ox");
256 break;
257 default:
258 error(CONSISTENCY, "botched output format");
259 }
260 castonly = 1; /* sets castonly as side-effect */
261 do
262 switch (*vs) {
263 case 'T': /* trace sources */
264 Tflag++;
265 /* fall through */
266 case 't': /* trace */
267 if (!vs[1]) break;
268 *table = NULL;
269 table = every_out;
270 castonly = 0;
271 break;
272 case 'o': /* origin */
273 *table++ = oputo;
274 ncomp += 3;
275 break;
276 case 'd': /* direction */
277 *table++ = oputd;
278 ncomp += 3;
279 break;
280 case 'r': /* reflected contrib. */
281 *table++ = oputr;
282 ncomp += nco;
283 castonly = 0;
284 break;
285 case 'R': /* reflected distance */
286 *table++ = oputR;
287 ncomp++;
288 castonly = 0;
289 break;
290 case 'x': /* xmit contrib. */
291 *table++ = oputx;
292 ncomp += nco;
293 castonly = 0;
294 break;
295 case 'X': /* xmit distance */
296 *table++ = oputX;
297 ncomp++;
298 castonly = 0;
299 break;
300 case 'v': /* value */
301 *table++ = oputv;
302 ncomp += nco;
303 castonly = 0;
304 break;
305 case 'V': /* contribution */
306 *table++ = oputV;
307 ncomp += nco;
308 castonly = 0;
309 if (ambounce > 0 && (ambacc > FTINY || ambssamp > 0))
310 error(WARNING,
311 "-otV accuracy depends on -aa 0 -as 0");
312 break;
313 case 'l': /* effective distance */
314 *table++ = oputl;
315 ncomp++;
316 castonly = 0;
317 break;
318 case 'c': /* local coordinates */
319 *table++ = oputc;
320 ncomp += 2;
321 break;
322 case 'L': /* single ray length */
323 *table++ = oputL;
324 ncomp++;
325 break;
326 case 'p': /* point */
327 *table++ = oputp;
328 ncomp += 3;
329 break;
330 case 'n': /* perturbed normal */
331 *table++ = oputn;
332 ncomp += 3;
333 castonly = 0;
334 break;
335 case 'N': /* unperturbed normal */
336 *table++ = oputN;
337 ncomp += 3;
338 break;
339 case 's': /* surface */
340 *table++ = oputs;
341 ncomp++;
342 break;
343 case 'w': /* weight */
344 *table++ = oputw;
345 ncomp++;
346 break;
347 case 'W': /* coefficient */
348 *table++ = oputW;
349 ncomp += nco;
350 castonly = 0;
351 if (ambounce > 0 && (ambacc > FTINY) | (ambssamp > 0))
352 error(WARNING,
353 "-otW accuracy depends on -aa 0 -as 0");
354 break;
355 case 'm': /* modifier */
356 *table++ = oputm;
357 ncomp++;
358 break;
359 case 'M': /* material */
360 *table++ = oputM;
361 ncomp++;
362 break;
363 case '~': /* tilde */
364 *table++ = oputtilde;
365 break;
366 default:
367 sprintf(errmsg, "unrecognized output option '%c'", *vs);
368 error(USER, errmsg);
369 }
370 while (*++vs);
371
372 *table = NULL;
373 if (*every_out != NULL)
374 ncomp = 0;
375 /* compatibility */
376 if ((do_irrad | imm_irrad) && castonly)
377 error(USER, "-I+ and -i+ options require some value output");
378 for (table = ray_out; *table != NULL; table++) {
379 if ((*table == oputV) | (*table == oputW))
380 error(WARNING, "-oVW options require trace mode");
381 if ((do_irrad | imm_irrad) &&
382 (*table == oputr) | (*table == oputR) |
383 (*table == oputx) | (*table == oputX))
384 error(WARNING, "-orRxX options incompatible with -I+ and -i+");
385 }
386 return(ncomp);
387 }
388
389
390 static void
391 bogusray(void) /* print out empty record */
392 {
393 rayorigin(&thisray, PRIMARY, NULL, NULL);
394 printvals(&thisray);
395 }
396
397
398 static void
399 raycast( /* compute first ray intersection only */
400 RAY *r
401 )
402 {
403 if (!localhit(r, &thescene)) {
404 if (r->ro == &Aftplane) { /* clipped */
405 r->ro = NULL;
406 r->rot = FHUGE;
407 } else
408 sourcehit(r);
409 }
410 }
411
412
413 static void
414 rayirrad( /* compute irradiance rather than radiance */
415 RAY *r
416 )
417 {
418 void (*old_revf)(RAY *) = r->revf;
419 /* pretend we hit surface */
420 r->rxt = r->rot = 1e-5;
421 VSUM(r->rop, r->rorg, r->rdir, r->rot);
422 r->ron[0] = -r->rdir[0];
423 r->ron[1] = -r->rdir[1];
424 r->ron[2] = -r->rdir[2];
425 r->rod = 1.0;
426 /* compute result */
427 r->revf = raytrace;
428 (*ofun[Lamb.otype].funp)(&Lamb, r);
429 r->revf = old_revf;
430 }
431
432
433 static void
434 rtcompute( /* compute and print ray value(s) */
435 FVECT org,
436 FVECT dir,
437 double dmax
438 )
439 {
440 /* set up ray */
441 if (imm_irrad) {
442 VSUM(thisray.rorg, org, dir, 1.1e-4);
443 thisray.rdir[0] = -dir[0];
444 thisray.rdir[1] = -dir[1];
445 thisray.rdir[2] = -dir[2];
446 thisray.rmax = 0.0;
447 } else {
448 VCOPY(thisray.rorg, org);
449 VCOPY(thisray.rdir, dir);
450 thisray.rmax = dmax;
451 }
452 rayorigin(&thisray, PRIMARY, NULL, NULL);
453 if (imm_irrad)
454 thisray.revf = rayirrad;
455 else if (castonly)
456 thisray.revf = raycast;
457 if (ray_pnprocs > 1) { /* multiprocessing FIFO? */
458 if (ray_fifo_in(&thisray) < 0)
459 error(USER, "lost children");
460 return;
461 }
462 samplendx++; /* else do it ourselves */
463 rayvalue(&thisray);
464 printvals(&thisray);
465 }
466
467
468 static int
469 printvals( /* print requested ray values */
470 RAY *r
471 )
472 {
473 oputf_t **tp;
474
475 if (ray_out[0] == NULL)
476 return(0);
477 for (tp = ray_out; *tp != NULL; tp++)
478 (**tp)(r);
479 if (outform == 'a')
480 putchar('\n');
481 return(1);
482 }
483
484
485 static int
486 is_fifo( /* check if file pointer connected to pipe */
487 FILE *fp
488 )
489 {
490 #ifdef S_ISFIFO
491 struct stat sbuf;
492
493 if (fstat(fileno(fp), &sbuf) < 0)
494 error(SYSTEM, "fstat() failed on input stream");
495 return(S_ISFIFO(sbuf.st_mode));
496 #else
497 return (fp == stdin); /* just a guess, really */
498 #endif
499 }
500
501
502 static int
503 getvec( /* get a vector from fp */
504 FVECT vec,
505 int fmt,
506 FILE *fp
507 )
508 {
509 static float vf[3];
510 static double vd[3];
511 char buf[32];
512 int i;
513
514 switch (fmt) {
515 case 'a': /* ascii */
516 for (i = 0; i < 3; i++) {
517 if (fgetword(buf, sizeof(buf), fp) == NULL ||
518 !isflt(buf))
519 return(-1);
520 vec[i] = atof(buf);
521 }
522 break;
523 case 'f': /* binary float */
524 if (getbinary(vf, sizeof(float), 3, fp) != 3)
525 return(-1);
526 VCOPY(vec, vf);
527 break;
528 case 'd': /* binary double */
529 if (getbinary(vd, sizeof(double), 3, fp) != 3)
530 return(-1);
531 VCOPY(vec, vd);
532 break;
533 default:
534 error(CONSISTENCY, "botched input format");
535 }
536 return(0);
537 }
538
539
540 static int
541 iszerovec(const FVECT vec)
542 {
543 return (vec[0] == 0.0) & (vec[1] == 0.0) & (vec[2] == 0.0);
544 }
545
546
547 static double
548 nextray( /* return next ray in work group (-1.0 if EOF) */
549 FVECT org,
550 FVECT dir
551 )
552 {
553 const size_t qlength = !vresolu * hresolu;
554
555 if ((org == NULL) | (dir == NULL)) {
556 if (inp_queue != NULL) /* asking to free queue */
557 free(inp_queue);
558 inp_queue = NULL;
559 inp_qpos = inp_qend = 0;
560 return(-1.);
561 }
562 if (!inp_qend) { /* initialize FIFO queue */
563 int rsiz = 6*20; /* conservative ascii ray size */
564 if (inform == 'f') rsiz = 6*sizeof(float);
565 else if (inform == 'd') rsiz = 6*sizeof(double);
566 /* check against pipe limit */
567 if (qlength*rsiz > 512 && is_fifo(inpfp))
568 inp_queue = (FVECT *)malloc(sizeof(FVECT)*2*qlength);
569 inp_qend = -(inp_queue == NULL); /* flag for no queue */
570 }
571 if (inp_qend < 0) { /* not queuing? */
572 if (getvec(org, inform, inpfp) < 0 ||
573 getvec(dir, inform, inpfp) < 0)
574 return(-1.);
575 return normalize(dir);
576 }
577 if (inp_qpos >= inp_qend) { /* need to refill input queue? */
578 for (inp_qend = 0; inp_qend < qlength; inp_qend++) {
579 if (getvec(inp_queue[2*inp_qend], inform, inpfp) < 0
580 || getvec(inp_queue[2*inp_qend+1],
581 inform, inpfp) < 0)
582 break; /* hit EOF */
583 if (iszerovec(inp_queue[2*inp_qend+1])) {
584 ++inp_qend; /* flush request */
585 break;
586 }
587 }
588 inp_qpos = 0;
589 }
590 if (inp_qpos >= inp_qend) /* unexpected EOF? */
591 return(-1.);
592 VCOPY(org, inp_queue[2*inp_qpos]);
593 VCOPY(dir, inp_queue[2*inp_qpos+1]);
594 ++inp_qpos;
595 return normalize(dir);
596 }
597
598
599 void
600 tranotify( /* record new modifier */
601 OBJECT obj
602 )
603 {
604 static int hitlimit = 0;
605 OBJREC *o = objptr(obj);
606 char **tralp;
607
608 if (obj == OVOID) { /* starting over */
609 traset[0] = 0;
610 hitlimit = 0;
611 return;
612 }
613 if (hitlimit || !ismodifier(o->otype))
614 return;
615 for (tralp = tralist; *tralp != NULL; tralp++)
616 if (!strcmp(o->oname, *tralp)) {
617 if (traset[0] >= MAXTSET) {
618 error(WARNING, "too many modifiers in trace list");
619 hitlimit++;
620 return; /* should this be fatal? */
621 }
622 insertelem(traset, obj);
623 return;
624 }
625 }
626
627
628 static void
629 ourtrace( /* print ray values */
630 RAY *r
631 )
632 {
633 oputf_t **tp;
634
635 if (every_out[0] == NULL)
636 return;
637 if (r->ro == NULL) {
638 if (traincl == 1)
639 return;
640 } else if (traincl != -1 && traincl != inset(traset, r->ro->omod))
641 return;
642 tabin(r);
643 for (tp = every_out; *tp != NULL; tp++)
644 (**tp)(r);
645 if (outform == 'a')
646 putchar('\n');
647 }
648
649
650 static void
651 tabin( /* tab in appropriate amount */
652 RAY *r
653 )
654 {
655 const RAY *rp;
656
657 for (rp = r->parent; rp != NULL; rp = rp->parent)
658 putchar('\t');
659 }
660
661
662 static void
663 oputo( /* print origin */
664 RAY *r
665 )
666 {
667 (*putreal)(r->rorg, 3);
668 }
669
670
671 static void
672 oputd( /* print direction */
673 RAY *r
674 )
675 {
676 (*putreal)(r->rdir, 3);
677 }
678
679
680 static void
681 oputr( /* print mirrored contribution */
682 RAY *r
683 )
684 {
685 RREAL cval[3];
686
687 cval[0] = colval(r->mcol,RED);
688 cval[1] = colval(r->mcol,GRN);
689 cval[2] = colval(r->mcol,BLU);
690 (*putreal)(cval, 3);
691 }
692
693
694
695 static void
696 oputR( /* print mirrored distance */
697 RAY *r
698 )
699 {
700 (*putreal)(&r->rmt, 1);
701 }
702
703
704 static void
705 oputx( /* print unmirrored contribution */
706 RAY *r
707 )
708 {
709 SCOLOR cdiff;
710
711 copyscolor(cdiff, r->rcol);
712 sopscolor(cdiff, -=, r->mcol);
713
714 putscolor(cdiff);
715 }
716
717
718 static void
719 oputX( /* print unmirrored distance */
720 RAY *r
721 )
722 {
723 (*putreal)(&r->rxt, 1);
724 }
725
726
727 static void
728 oputv( /* print value */
729 RAY *r
730 )
731 {
732 putscolor(r->rcol);
733 }
734
735
736 static void
737 oputV( /* print value contribution */
738 RAY *r
739 )
740 {
741 SCOLOR contr;
742
743 raycontrib(contr, r, PRIMARY);
744 smultscolor(contr, r->rcol);
745 putscolor(contr);
746 }
747
748
749 static void
750 oputl( /* print effective distance */
751 RAY *r
752 )
753 {
754 RREAL d = raydistance(r);
755
756 (*putreal)(&d, 1);
757 }
758
759
760 static void
761 oputL( /* print single ray length */
762 RAY *r
763 )
764 {
765 (*putreal)(&r->rot, 1);
766 }
767
768
769 static void
770 oputc( /* print local coordinates */
771 RAY *r
772 )
773 {
774 (*putreal)(r->uv, 2);
775 }
776
777
778 static RREAL vdummy[3] = {0.0, 0.0, 0.0};
779
780
781 static void
782 oputp( /* print intersection point */
783 RAY *r
784 )
785 {
786 (*putreal)(r->rop, 3); /* set to ray origin if distant or no hit */
787 }
788
789
790 static void
791 oputN( /* print unperturbed normal */
792 RAY *r
793 )
794 {
795 if (r->ro == NULL) { /* zero vector if clipped or no hit */
796 (*putreal)(vdummy, 3);
797 return;
798 }
799 if (r->rflips & 1) { /* undo any flippin' flips */
800 FVECT unrm;
801 unrm[0] = -r->ron[0];
802 unrm[1] = -r->ron[1];
803 unrm[2] = -r->ron[2];
804 (*putreal)(unrm, 3);
805 } else
806 (*putreal)(r->ron, 3);
807 }
808
809
810 static void
811 oputn( /* print perturbed normal */
812 RAY *r
813 )
814 {
815 FVECT pnorm;
816
817 if (r->ro == NULL) { /* clipped or no hit */
818 (*putreal)(vdummy, 3);
819 return;
820 }
821 raynormal(pnorm, r);
822 (*putreal)(pnorm, 3);
823 }
824
825
826 static void
827 oputs( /* print name */
828 RAY *r
829 )
830 {
831 if (r->ro != NULL)
832 fputs(r->ro->oname, stdout);
833 else
834 putchar('*');
835 putchar('\t');
836 }
837
838
839 static void
840 oputw( /* print weight */
841 RAY *r
842 )
843 {
844 RREAL rwt = r->rweight;
845
846 (*putreal)(&rwt, 1);
847 }
848
849
850 static void
851 oputW( /* print coefficient */
852 RAY *r
853 )
854 {
855 SCOLOR contr;
856 /* shadow ray not on source? */
857 if (r->rsrc >= 0 && source[r->rsrc].so != r->ro)
858 scolorblack(contr);
859 else
860 raycontrib(contr, r, PRIMARY);
861
862 putscolor(contr);
863 }
864
865
866 static void
867 oputm( /* print modifier */
868 RAY *r
869 )
870 {
871 if (r->ro != NULL)
872 if (r->ro->omod != OVOID)
873 fputs(objptr(r->ro->omod)->oname, stdout);
874 else
875 fputs(VOIDID, stdout);
876 else
877 putchar('*');
878 putchar('\t');
879 }
880
881
882 static void
883 oputM( /* print material */
884 RAY *r
885 )
886 {
887 OBJREC *mat;
888
889 if (r->ro != NULL) {
890 if ((mat = findmaterial(r->ro)) != NULL)
891 fputs(mat->oname, stdout);
892 else
893 fputs(VOIDID, stdout);
894 } else
895 putchar('*');
896 putchar('\t');
897 }
898
899
900 static void
901 oputtilde( /* output tilde (spacer) */
902 RAY *r
903 )
904 {
905 fputs("~\t", stdout);
906 }
907
908
909 static void
910 puta( /* print ascii value(s) */
911 RREAL *v, int n
912 )
913 {
914 if (n == 3) {
915 printf("%e\t%e\t%e\t", v[0], v[1], v[2]);
916 return;
917 }
918 while (n--)
919 printf("%e\t", *v++);
920 }
921
922
923 static void
924 putd(RREAL *v, int n) /* output binary double(s) */
925 {
926 #ifdef SMLFLT
927 double da[3];
928 int i;
929
930 if (n > 3)
931 error(INTERNAL, "code error in putd()");
932 for (i = n; i--; )
933 da[i] = v[i];
934 putbinary(da, sizeof(double), n, stdout);
935 #else
936 putbinary(v, sizeof(RREAL), n, stdout);
937 #endif
938 }
939
940
941 static void
942 putf(RREAL *v, int n) /* output binary float(s) */
943 {
944 #ifndef SMLFLT
945 float fa[3];
946 int i;
947
948 if (n > 3)
949 error(INTERNAL, "code error in putf()");
950 for (i = n; i--; )
951 fa[i] = v[i];
952 putbinary(fa, sizeof(float), n, stdout);
953 #else
954 putbinary(v, sizeof(RREAL), n, stdout);
955 #endif
956 }
957
958
959 static void
960 putscolor(const COLORV *scol) /* output (spectral) color */
961 {
962 static COLORMAT xyz2myrgbmat;
963 SCOLOR my_scol;
964 COLOR col;
965 /* apply scalefactor if any */
966 if (out_scalefactor != 1.) {
967 copyscolor(my_scol, scol);
968 scalescolor(my_scol, out_scalefactor);
969 scol = my_scol;
970 }
971 if (sens_curve != NULL) { /* single channel output */
972 RREAL v = (*sens_curve)(scol);
973 (*putreal)(&v, 1);
974 return;
975 }
976 if (out_prims == NULL) { /* full spectral reporting */
977 if (outform == 'c') {
978 SCOLR sclr;
979 scolor_scolr(sclr, scol);
980 putbinary(sclr, LSCOLR, 1, stdout);
981 } else if (sizeof(RREAL) != sizeof(COLORV)) {
982 RREAL sreal[MAXCSAMP];
983 int i = NCSAMP;
984 while (i--) sreal[i] = scol[i];
985 (*putreal)(sreal, NCSAMP);
986 } else
987 (*putreal)((RREAL *)scol, NCSAMP);
988 return;
989 }
990 if (out_prims == xyzprims) {
991 scolor_cie(col, scol);
992 } else if (out_prims == stdprims) {
993 scolor_rgb(col, scol);
994 } else {
995 COLOR xyz;
996 if (xyz2myrgbmat[0][0] == 0)
997 compxyz2rgbWBmat(xyz2myrgbmat, out_prims);
998 scolor_cie(xyz, scol);
999 colortrans(col, xyz2myrgbmat, xyz);
1000 clipgamut(col, xyz[CIEY], CGAMUT_LOWER, cblack, cwhite);
1001 }
1002 if (outform == 'c') {
1003 COLR clr;
1004 setcolr(clr, colval(col,RED), colval(col,GRN), colval(col,BLU));
1005 putbinary(clr, sizeof(COLR), 1, stdout);
1006 } else if (sizeof(RREAL) != sizeof(COLORV)) {
1007 RREAL creal[3];
1008 copycolor(creal, col);
1009 (*putreal)(creal, 3);
1010 } else
1011 (*putreal)((RREAL *)col, 3);
1012 }