ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rtrace.c
Revision: 2.96
Committed: Mon Jun 15 15:44:04 2020 UTC (3 years, 10 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.95: +2 -1 lines
Log Message:
Optimize rtrace for throughput, especially with ambient cache

File Contents

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