ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rtrace.c
Revision: 2.90
Committed: Sat Apr 4 15:38:52 2020 UTC (4 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.89: +2 -2 lines
Log Message:
Turn off multiprocessing with -oT* and -ot* options

File Contents

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