ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rfluxmtx.c
Revision: 2.33
Committed: Tue Feb 2 01:43:24 2016 UTC (8 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.32: +2 -4 lines
Log Message:
Removed redundant error output

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rfluxmtx.c,v 2.32 2015/12/04 22:16:18 greg Exp $";
3 #endif
4 /*
5 * Calculate flux transfer matrix or matrices using rcontrib
6 */
7
8 #include "copyright.h"
9
10 #include <ctype.h>
11 #include <stdlib.h>
12 #include "rtio.h"
13 #include "rtmath.h"
14 #include "rtprocess.h"
15 #include "bsdf.h"
16 #include "bsdf_m.h"
17 #include "random.h"
18 #include "triangulate.h"
19 #include "platform.h"
20
21 #ifdef getc_unlocked /* avoid horrendous overhead of flockfile */
22 #undef getc
23 #define getc getc_unlocked
24 #endif
25
26 #ifdef _WIN32
27 #define SPECIALS " \t\"$*?"
28 #define QUOTCHAR '"'
29 #else
30 #define SPECIALS " \t\n'\"()${}*?[];|&"
31 #define QUOTCHAR '\''
32 #define ALTQUOT '"'
33 #endif
34
35 #define MAXRCARG 512
36
37 char *progname; /* global argv[0] */
38
39 int verbose = 0; /* verbose mode (< 0 no warnings) */
40
41 char *rcarg[MAXRCARG+1] = {"rcontrib", "-fo+"};
42 int nrcargs = 2;
43
44 const char overflowerr[] = "%s: too many arguments!\n";
45
46 #define CHECKARGC(n) if (nrcargs >= MAXRCARG-(n)) \
47 { fprintf(stderr, overflowerr, progname); exit(1); }
48
49 int sampcnt = 0; /* sample count (0==unset) */
50
51 char *reinhfn = "reinhartb.cal";
52 char *shirchiufn = "disk2square.cal";
53 char *kfullfn = "klems_full.cal";
54 char *khalffn = "klems_half.cal";
55 char *kquarterfn = "klems_quarter.cal";
56
57 /* string indicating parameters */
58 const char PARAMSTART[] = "@rfluxmtx";
59
60 /* surface type IDs */
61 #define ST_NONE 0
62 #define ST_POLY 1
63 #define ST_RING 2
64 #define ST_SOURCE 3
65
66 typedef struct surf_s {
67 struct surf_s *next; /* next surface in list */
68 void *priv; /* private data (malloc'ed) */
69 char sname[32]; /* surface name */
70 FVECT snrm; /* surface normal */
71 double area; /* surface area / proj. solid angle */
72 short styp; /* surface type */
73 short nfargs; /* number of real arguments */
74 double farg[1]; /* real values (extends struct) */
75 } SURF; /* surface structure */
76
77 typedef struct {
78 FVECT uva[2]; /* tangent axes */
79 int ntris; /* number of triangles */
80 struct ptri {
81 float afrac; /* fraction of total area */
82 short vndx[3]; /* vertex indices */
83 } tri[1]; /* triangle array (extends struct) */
84 } POLYTRIS; /* triangulated polygon */
85
86 typedef struct param_s {
87 char sign; /* '-' for axis reversal */
88 char hemis[31]; /* hemispherical sampling spec. */
89 int hsiz; /* hemisphere basis size */
90 int nsurfs; /* number of surfaces */
91 SURF *slist; /* list of surfaces */
92 FVECT vup; /* up vector (zero if unset) */
93 FVECT nrm; /* average normal direction */
94 FVECT udir, vdir; /* tangent axes */
95 char *outfn; /* output file name (receiver) */
96 int (*sample_basis)(struct param_s *p, int, FILE *);
97 } PARAMS; /* sender/receiver parameters */
98
99 PARAMS curparams;
100 char curmod[128];
101 char newparams[1024];
102
103 typedef int SURFSAMP(FVECT, SURF *, double);
104
105 static SURFSAMP ssamp_bad, ssamp_poly, ssamp_ring;
106
107 SURFSAMP *orig_in_surf[4] = {
108 ssamp_bad, ssamp_poly, ssamp_ring, ssamp_bad
109 };
110
111 /* Clear parameter set */
112 static void
113 clear_params(PARAMS *p, int reset_only)
114 {
115 while (p->slist != NULL) {
116 SURF *sdel = p->slist;
117 p->slist = sdel->next;
118 if (sdel->priv != NULL)
119 free(sdel->priv);
120 free(sdel);
121 }
122 if (reset_only) {
123 p->nsurfs = 0;
124 memset(p->nrm, 0, sizeof(FVECT));
125 memset(p->vup, 0, sizeof(FVECT));
126 p->outfn = NULL;
127 return;
128 }
129 memset(p, 0, sizeof(PARAMS));
130 }
131
132 /* Get surface type from name */
133 static int
134 surf_type(const char *otype)
135 {
136 if (!strcmp(otype, "polygon"))
137 return(ST_POLY);
138 if (!strcmp(otype, "ring"))
139 return(ST_RING);
140 if (!strcmp(otype, "source"))
141 return(ST_SOURCE);
142 return(ST_NONE);
143 }
144
145 /* Check if any of the characters in str2 are found in str1 */
146 static int
147 matchany(const char *str1, const char *str2)
148 {
149 while (*str1) {
150 const char *cp = str2;
151 while (*cp)
152 if (*cp++ == *str1)
153 return(*str1);
154 ++str1;
155 }
156 return(0);
157 }
158
159 /* Add arguments to oconv command */
160 static char *
161 oconv_command(int ac, char *av[])
162 {
163 static char oconvbuf[2048] = "!oconv -f ";
164 char *cp = oconvbuf + 10;
165 char *recv = *av++;
166
167 if (ac-- <= 0)
168 return(NULL);
169 while (ac-- > 0) {
170 strcpy(cp, *av++);
171 while (*cp) cp++;
172 *cp++ = ' ';
173 if (cp >= oconvbuf+(sizeof(oconvbuf)-32))
174 goto overrun;
175 }
176 /* receiver goes last */
177 if (matchany(recv, SPECIALS)) {
178 *cp++ = QUOTCHAR;
179 while (*recv) {
180 if (cp >= oconvbuf+(sizeof(oconvbuf)-3))
181 goto overrun;
182 *cp++ = *recv++;
183 }
184 *cp++ = QUOTCHAR;
185 *cp = '\0';
186 } else
187 strcpy(cp, recv);
188 return(oconvbuf);
189 overrun:
190 fputs(progname, stderr);
191 fputs(": too many file arguments!\n", stderr);
192 exit(1);
193 }
194
195 /* Convert a set of arguments into a command line for pipe() or system() */
196 static char *
197 convert_commandline(char *cmd, const int len, char *av[])
198 {
199 int match;
200 char *cp;
201
202 for (cp = cmd; *av != NULL; av++) {
203 const int n = strlen(*av);
204 if (cp+n >= cmd+(len-3))
205 return(NULL);
206 if (matchany(*av, SPECIALS)) {
207 const int quote =
208 #ifdef ALTQUOT
209 strchr(*av, QUOTCHAR) ? ALTQUOT :
210 #endif
211 QUOTCHAR;
212 *cp++ = quote;
213 strcpy(cp, *av);
214 cp += n;
215 *cp++ = quote;
216 } else {
217 strcpy(cp, *av);
218 cp += n;
219 }
220 *cp++ = ' ';
221 }
222 if (cp <= cmd)
223 return(NULL);
224 *--cp = '\0';
225 return(cmd);
226 }
227
228 /* Open a pipe to/from a command given as an argument list */
229 static FILE *
230 popen_arglist(char *av[], char *mode)
231 {
232 char cmd[10240];
233
234 if (!convert_commandline(cmd, sizeof(cmd), av)) {
235 fputs(progname, stderr);
236 fputs(": command line too long in popen_arglist()\n", stderr);
237 return(NULL);
238 }
239 if (verbose > 0)
240 fprintf(stderr, "%s: opening pipe %s: %s\n",
241 progname, (*mode=='w') ? "to" : "from", cmd);
242 return(popen(cmd, mode));
243 }
244
245 #ifdef _WIN32
246 /* Execute system command (Windows version) */
247 static int
248 my_exec(char *av[])
249 {
250 char cmd[10240];
251
252 if (!convert_commandline(cmd, sizeof(cmd), av)) {
253 fputs(progname, stderr);
254 fputs(": command line too long in my_exec()\n", stderr);
255 return(1);
256 }
257 if (verbose > 0)
258 fprintf(stderr, "%s: running: %s\n", progname, cmd);
259 return(system(cmd));
260 }
261 #else
262 /* Execute system command in our stead (Unix version) */
263 static int
264 my_exec(char *av[])
265 {
266 char *compath;
267
268 if ((compath = getpath((char *)av[0], getenv("PATH"), X_OK)) == NULL) {
269 fprintf(stderr, "%s: cannot locate %s\n", progname, av[0]);
270 return(1);
271 }
272 if (verbose > 0) {
273 char cmd[4096];
274 if (!convert_commandline(cmd, sizeof(cmd), av))
275 strcpy(cmd, "COMMAND TOO LONG TO SHOW");
276 fprintf(stderr, "%s: running: %s\n", progname, cmd);
277 }
278 execv(compath, av); /* successful call never returns */
279 perror(compath);
280 return(1);
281 }
282 #endif
283
284 /* Get normalized direction vector from string specification */
285 static int
286 get_direction(FVECT dv, const char *s)
287 {
288 int sign = 1;
289 int axis = 0;
290
291 memset(dv, 0, sizeof(FVECT));
292 nextchar:
293 switch (*s) {
294 case '+':
295 ++s;
296 goto nextchar;
297 case '-':
298 sign = -sign;
299 ++s;
300 goto nextchar;
301 case 'z':
302 case 'Z':
303 ++axis;
304 case 'y':
305 case 'Y':
306 ++axis;
307 case 'x':
308 case 'X':
309 dv[axis] = sign;
310 return(!s[1] | isspace(s[1]));
311 default:
312 break;
313 }
314 #ifdef SMLFLT
315 if (sscanf(s, "%f,%f,%f", &dv[0], &dv[1], &dv[2]) != 3)
316 #else
317 if (sscanf(s, "%lf,%lf,%lf", &dv[0], &dv[1], &dv[2]) != 3)
318 #endif
319 return(0);
320 dv[0] *= (RREAL)sign;
321 return(normalize(dv) > 0);
322 }
323
324 /* Parse program parameters (directives) */
325 static int
326 parse_params(PARAMS *p, char *pargs)
327 {
328 char *cp = pargs;
329 int nparams = 0;
330 int quot;
331 int i;
332
333 for ( ; ; ) {
334 switch (*cp++) {
335 case 'h':
336 if (*cp++ != '=')
337 break;
338 if ((*cp == '+') | (*cp == '-'))
339 p->sign = *cp++;
340 else
341 p->sign = '+';
342 p->hsiz = 0;
343 i = 0;
344 while (*cp && !isspace(*cp)) {
345 if (isdigit(*cp))
346 p->hsiz = 10*p->hsiz + *cp - '0';
347 p->hemis[i++] = *cp++;
348 }
349 if (!i)
350 break;
351 p->hemis[i] = '\0';
352 p->hsiz += !p->hsiz;
353 ++nparams;
354 continue;
355 case 'u':
356 if (*cp++ != '=')
357 break;
358 if (!get_direction(p->vup, cp))
359 break;
360 while (*cp && !isspace(*cp++))
361 ;
362 ++nparams;
363 continue;
364 case 'o':
365 if (*cp++ != '=')
366 break;
367 quot = 0;
368 if ((*cp == '"') | (*cp == '\''))
369 quot = *cp++;
370 i = 0;
371 while (*cp && (quot ? (*cp != quot) : !isspace(*cp))) {
372 i++; cp++;
373 }
374 if (!i)
375 break;
376 if (!*cp) {
377 if (quot)
378 break;
379 cp[1] = '\0';
380 }
381 *cp = '\0';
382 p->outfn = savqstr(cp-i);
383 *cp++ = quot ? quot : ' ';
384 ++nparams;
385 continue;
386 case ' ':
387 case '\t':
388 case '\r':
389 case '\n':
390 continue;
391 case '\0':
392 return(nparams);
393 default:
394 break;
395 }
396 break;
397 }
398 fprintf(stderr, "%s: bad parameter string: %s", progname, pargs);
399 exit(1);
400 return(-1); /* pro forma return */
401 }
402
403 /* Add receiver arguments (directives) corresponding to the current modifier */
404 static void
405 finish_receiver(void)
406 {
407 char sbuf[256];
408 int uniform = 0;
409 char *calfn = NULL;
410 char *params = NULL;
411 char *binv = NULL;
412 char *binf = NULL;
413 char *nbins = NULL;
414
415 if (!curmod[0]) {
416 fputs(progname, stderr);
417 fputs(": missing receiver surface!\n", stderr);
418 exit(1);
419 }
420 if (curparams.outfn != NULL) { /* add output file spec. */
421 CHECKARGC(2);
422 rcarg[nrcargs++] = "-o";
423 rcarg[nrcargs++] = curparams.outfn;
424 }
425 /* check arguments */
426 if (!curparams.hemis[0]) {
427 fputs(progname, stderr);
428 fputs(": missing hemisphere sampling type!\n", stderr);
429 exit(1);
430 }
431 if (normalize(curparams.nrm) == 0) {
432 fputs(progname, stderr);
433 fputs(": undefined normal for hemisphere sampling\n", stderr);
434 exit(1);
435 }
436 if (normalize(curparams.vup) == 0) {
437 if (fabs(curparams.nrm[2]) < .7)
438 curparams.vup[2] = 1;
439 else
440 curparams.vup[1] = 1;
441 }
442 /* determine sample type/bin */
443 if (tolower(curparams.hemis[0]) == 'u' | curparams.hemis[0] == '1') {
444 sprintf(sbuf, "if(-Dx*%g-Dy*%g-Dz*%g,0,-1)",
445 curparams.nrm[0], curparams.nrm[1], curparams.nrm[2]);
446 binv = savqstr(sbuf);
447 nbins = "1"; /* uniform sampling -- one bin */
448 uniform = 1;
449 } else if (tolower(curparams.hemis[0]) == 's' &&
450 tolower(curparams.hemis[1]) == 'c') {
451 /* assign parameters */
452 if (curparams.hsiz <= 1) {
453 fputs(progname, stderr);
454 fputs(": missing size for Shirley-Chiu sampling!\n", stderr);
455 exit(1);
456 }
457 calfn = shirchiufn; shirchiufn = NULL;
458 sprintf(sbuf, "SCdim=%d,rNx=%g,rNy=%g,rNz=%g,Ux=%g,Uy=%g,Uz=%g,RHS=%c1",
459 curparams.hsiz,
460 curparams.nrm[0], curparams.nrm[1], curparams.nrm[2],
461 curparams.vup[0], curparams.vup[1], curparams.vup[2],
462 curparams.sign);
463 params = savqstr(sbuf);
464 binv = "scbin";
465 nbins = "SCdim*SCdim";
466 } else if ((tolower(curparams.hemis[0]) == 'r') |
467 (tolower(curparams.hemis[0]) == 't')) {
468 calfn = reinhfn; reinhfn = NULL;
469 sprintf(sbuf, "MF=%d,rNx=%g,rNy=%g,rNz=%g,Ux=%g,Uy=%g,Uz=%g,RHS=%c1",
470 curparams.hsiz,
471 curparams.nrm[0], curparams.nrm[1], curparams.nrm[2],
472 curparams.vup[0], curparams.vup[1], curparams.vup[2],
473 curparams.sign);
474 params = savqstr(sbuf);
475 binv = "rbin";
476 nbins = "Nrbins";
477 } else if (tolower(curparams.hemis[0]) == 'k' &&
478 !curparams.hemis[1] |
479 (tolower(curparams.hemis[1]) == 'f') |
480 (curparams.hemis[1] == '1')) {
481 calfn = kfullfn; kfullfn = NULL;
482 binf = "kbin";
483 nbins = "Nkbins";
484 } else if (tolower(curparams.hemis[0]) == 'k' &&
485 (tolower(curparams.hemis[1]) == 'h') |
486 (curparams.hemis[1] == '2')) {
487 calfn = khalffn; khalffn = NULL;
488 binf = "khbin";
489 nbins = "Nkhbins";
490 } else if (tolower(curparams.hemis[0]) == 'k' &&
491 (tolower(curparams.hemis[1]) == 'q') |
492 (curparams.hemis[1] == '4')) {
493 calfn = kquarterfn; kquarterfn = NULL;
494 binf = "kqbin";
495 nbins = "Nkqbins";
496 } else {
497 fprintf(stderr, "%s: unrecognized hemisphere sampling: h=%s\n",
498 progname, curparams.hemis);
499 exit(1);
500 }
501 if (tolower(curparams.hemis[0]) == 'k') {
502 sprintf(sbuf, "RHS=%c1", curparams.sign);
503 params = savqstr(sbuf);
504 }
505 if (!uniform & (curparams.slist->styp == ST_SOURCE)) {
506 SURF *sp;
507 for (sp = curparams.slist; sp != NULL; sp = sp->next)
508 if (fabs(sp->area - PI) > 1e-3) {
509 fprintf(stderr, "%s: source '%s' must be 180-degrees\n",
510 progname, sp->sname);
511 exit(1);
512 }
513 }
514 if (calfn != NULL) { /* add cal file if needed */
515 CHECKARGC(2);
516 rcarg[nrcargs++] = "-f";
517 rcarg[nrcargs++] = calfn;
518 }
519 if (params != NULL) { /* parameters _after_ cal file */
520 CHECKARGC(2);
521 rcarg[nrcargs++] = "-p";
522 rcarg[nrcargs++] = params;
523 }
524 if (nbins != NULL) { /* add #bins if set */
525 CHECKARGC(2);
526 rcarg[nrcargs++] = "-bn";
527 rcarg[nrcargs++] = nbins;
528 }
529 if (binv != NULL) {
530 CHECKARGC(2); /* assign bin variable */
531 rcarg[nrcargs++] = "-b";
532 rcarg[nrcargs++] = binv;
533 } else if (binf != NULL) {
534 CHECKARGC(2); /* assign bin function */
535 rcarg[nrcargs++] = "-b";
536 sprintf(sbuf, "%s(%g,%g,%g,%g,%g,%g)", binf,
537 curparams.nrm[0], curparams.nrm[1], curparams.nrm[2],
538 curparams.vup[0], curparams.vup[1], curparams.vup[2]);
539 rcarg[nrcargs++] = savqstr(sbuf);
540 }
541 CHECKARGC(2); /* modifier argument goes last */
542 rcarg[nrcargs++] = "-m";
543 rcarg[nrcargs++] = savqstr(curmod);
544 }
545
546 /* Make randomly oriented tangent plane axes for given normal direction */
547 static void
548 make_axes(FVECT uva[2], const FVECT nrm)
549 {
550 int i;
551
552 if (!getperpendicular(uva[0], nrm, 1)) {
553 fputs(progname, stderr);
554 fputs(": bad surface normal in make_axes!\n", stderr);
555 exit(1);
556 }
557 fcross(uva[1], nrm, uva[0]);
558 }
559
560 /* Illegal sender surfaces end up here */
561 static int
562 ssamp_bad(FVECT orig, SURF *sp, double x)
563 {
564 fprintf(stderr, "%s: illegal sender surface '%s'\n",
565 progname, sp->sname);
566 return(0);
567 }
568
569 /* Generate origin on ring surface from uniform random variable */
570 static int
571 ssamp_ring(FVECT orig, SURF *sp, double x)
572 {
573 FVECT *uva = (FVECT *)sp->priv;
574 double samp2[2];
575 double uv[2];
576 int i;
577
578 if (uva == NULL) { /* need tangent axes */
579 uva = (FVECT *)malloc(sizeof(FVECT)*2);
580 if (uva == NULL) {
581 fputs(progname, stderr);
582 fputs(": out of memory in ssamp_ring!\n", stderr);
583 return(0);
584 }
585 make_axes(uva, sp->snrm);
586 sp->priv = (void *)uva;
587 }
588 SDmultiSamp(samp2, 2, x);
589 samp2[0] = sqrt(samp2[0]*sp->area*(1./PI) + sp->farg[6]*sp->farg[6]);
590 samp2[1] *= 2.*PI;
591 uv[0] = samp2[0]*tcos(samp2[1]);
592 uv[1] = samp2[0]*tsin(samp2[1]);
593 for (i = 3; i--; )
594 orig[i] = sp->farg[i] + uv[0]*uva[0][i] + uv[1]*uva[1][i];
595 return(1);
596 }
597
598 /* Add triangle to polygon's list (call-back function) */
599 static int
600 add_triangle(const Vert2_list *tp, int a, int b, int c)
601 {
602 POLYTRIS *ptp = (POLYTRIS *)tp->p;
603 struct ptri *trip = ptp->tri + ptp->ntris++;
604
605 trip->vndx[0] = a;
606 trip->vndx[1] = b;
607 trip->vndx[2] = c;
608 return(1);
609 }
610
611 /* Generate origin on polygon surface from uniform random variable */
612 static int
613 ssamp_poly(FVECT orig, SURF *sp, double x)
614 {
615 POLYTRIS *ptp = (POLYTRIS *)sp->priv;
616 double samp2[2];
617 double *v0, *v1, *v2;
618 int i;
619
620 if (ptp == NULL) { /* need to triangulate */
621 ptp = (POLYTRIS *)malloc(sizeof(POLYTRIS) +
622 sizeof(struct ptri)*(sp->nfargs/3 - 3));
623 if (ptp == NULL)
624 goto memerr;
625 if (sp->nfargs == 3) { /* simple case */
626 ptp->ntris = 1;
627 ptp->tri[0].vndx[0] = 0;
628 ptp->tri[0].vndx[1] = 1;
629 ptp->tri[0].vndx[2] = 2;
630 ptp->tri[0].afrac = 1;
631 } else {
632 Vert2_list *v2l = polyAlloc(sp->nfargs/3);
633 if (v2l == NULL)
634 goto memerr;
635 make_axes(ptp->uva, sp->snrm);
636 for (i = v2l->nv; i--; ) {
637 v2l->v[i].mX = DOT(sp->farg+3*i, ptp->uva[0]);
638 v2l->v[i].mY = DOT(sp->farg+3*i, ptp->uva[1]);
639 }
640 ptp->ntris = 0;
641 v2l->p = (void *)ptp;
642 if (!polyTriangulate(v2l, add_triangle)) {
643 fprintf(stderr,
644 "%s: cannot triangulate polygon '%s'\n",
645 progname, sp->sname);
646 return(0);
647 }
648 for (i = ptp->ntris; i--; ) {
649 int a = ptp->tri[i].vndx[0];
650 int b = ptp->tri[i].vndx[1];
651 int c = ptp->tri[i].vndx[2];
652 ptp->tri[i].afrac =
653 (v2l->v[a].mX*v2l->v[b].mY -
654 v2l->v[b].mX*v2l->v[a].mY +
655 v2l->v[b].mX*v2l->v[c].mY -
656 v2l->v[c].mX*v2l->v[b].mY +
657 v2l->v[c].mX*v2l->v[a].mY -
658 v2l->v[a].mX*v2l->v[c].mY) /
659 (2.*sp->area);
660 }
661 polyFree(v2l);
662 }
663 sp->priv = (void *)ptp;
664 }
665 /* pick triangle by partial area */
666 for (i = 0; i < ptp->ntris && x > ptp->tri[i].afrac; i++)
667 x -= ptp->tri[i].afrac;
668 SDmultiSamp(samp2, 2, x/ptp->tri[i].afrac);
669 samp2[0] *= samp2[1] = sqrt(samp2[1]);
670 samp2[1] = 1. - samp2[1];
671 v0 = sp->farg + 3*ptp->tri[i].vndx[0];
672 v1 = sp->farg + 3*ptp->tri[i].vndx[1];
673 v2 = sp->farg + 3*ptp->tri[i].vndx[2];
674 for (i = 3; i--; )
675 orig[i] = v0[i] + samp2[0]*(v1[i] - v0[i])
676 + samp2[1]*(v2[i] - v0[i]) ;
677 return(1);
678 memerr:
679 fputs(progname, stderr);
680 fputs(": out of memory in ssamp_poly!\n", stderr);
681 return(0);
682 }
683
684 /* Compute sample origin based on projected areas of sender subsurfaces */
685 static int
686 sample_origin(PARAMS *p, FVECT orig, const FVECT rdir, double x)
687 {
688 static double *projsa;
689 static int nall;
690 double tarea = 0;
691 int i;
692 SURF *sp;
693 /* special case for lone surface */
694 if (p->nsurfs == 1) {
695 sp = p->slist;
696 if (DOT(sp->snrm, rdir) >= FTINY) {
697 fprintf(stderr,
698 "%s: internal - sample behind sender '%s'\n",
699 progname, sp->sname);
700 return(0);
701 }
702 return((*orig_in_surf[sp->styp])(orig, sp, x));
703 }
704 if (p->nsurfs > nall) { /* (re)allocate surface area cache */
705 if (projsa) free(projsa);
706 projsa = (double *)malloc(sizeof(double)*p->nsurfs);
707 if (!projsa) return(0);
708 nall = p->nsurfs;
709 }
710 /* compute projected areas */
711 for (i = 0, sp = p->slist; sp != NULL; i++, sp = sp->next) {
712 projsa[i] = -DOT(sp->snrm, rdir) * sp->area;
713 tarea += projsa[i] *= (double)(projsa[i] > FTINY);
714 }
715 if (tarea <= FTINY) { /* wrong side of sender? */
716 fputs(progname, stderr);
717 fputs(": internal - sample behind all sender elements!\n",
718 stderr);
719 return(0);
720 }
721 tarea *= x; /* get surface from list */
722 for (i = 0, sp = p->slist; tarea > projsa[i]; sp = sp->next)
723 tarea -= projsa[i++];
724 return((*orig_in_surf[sp->styp])(orig, sp, tarea/projsa[i]));
725 }
726
727 /* Uniform sample generator */
728 static int
729 sample_uniform(PARAMS *p, int b, FILE *fp)
730 {
731 int n = sampcnt;
732 double samp3[3];
733 double duvw[3];
734 FVECT orig_dir[2];
735 int i;
736
737 if (fp == NULL) /* just requesting number of bins? */
738 return(1);
739
740 while (n--) { /* stratified hemisphere sampling */
741 SDmultiSamp(samp3, 3, (n+frandom())/sampcnt);
742 SDsquare2disk(duvw, samp3[1], samp3[2]);
743 duvw[2] = -sqrt(1. - duvw[0]*duvw[0] - duvw[1]*duvw[1]);
744 for (i = 3; i--; )
745 orig_dir[1][i] = duvw[0]*p->udir[i] +
746 duvw[1]*p->vdir[i] +
747 duvw[2]*p->nrm[i] ;
748 if (!sample_origin(p, orig_dir[0], orig_dir[1], samp3[0]))
749 return(0);
750 if (fwrite(orig_dir, sizeof(FVECT), 2, fp) != 2)
751 return(0);
752 }
753 return(1);
754 }
755
756 /* Shirly-Chiu sample generator */
757 static int
758 sample_shirchiu(PARAMS *p, int b, FILE *fp)
759 {
760 int n = sampcnt;
761 double samp3[3];
762 double duvw[3];
763 FVECT orig_dir[2];
764 int i;
765
766 if (fp == NULL) /* just requesting number of bins? */
767 return(p->hsiz*p->hsiz);
768
769 while (n--) { /* stratified sampling */
770 SDmultiSamp(samp3, 3, (n+frandom())/sampcnt);
771 SDsquare2disk(duvw, (b/p->hsiz + samp3[1])/curparams.hsiz,
772 (b%p->hsiz + samp3[2])/curparams.hsiz);
773 duvw[2] = sqrt(1. - duvw[0]*duvw[0] - duvw[1]*duvw[1]);
774 for (i = 3; i--; )
775 orig_dir[1][i] = -duvw[0]*p->udir[i] -
776 duvw[1]*p->vdir[i] -
777 duvw[2]*p->nrm[i] ;
778 if (!sample_origin(p, orig_dir[0], orig_dir[1], samp3[0]))
779 return(0);
780 if (fwrite(orig_dir, sizeof(FVECT), 2, fp) != 2)
781 return(0);
782 }
783 return(1);
784 }
785
786 /* Reinhart/Tregenza sample generator */
787 static int
788 sample_reinhart(PARAMS *p, int b, FILE *fp)
789 {
790 #define T_NALT 7
791 static const int tnaz[T_NALT] = {30, 30, 24, 24, 18, 12, 6};
792 const int RowMax = T_NALT*p->hsiz + 1;
793 const double RAH = (.5*PI)/(RowMax-.5);
794 #define rnaz(r) (r >= RowMax-1 ? 1 : p->hsiz*tnaz[r/p->hsiz])
795 int n = sampcnt;
796 int row, col;
797 double samp3[3];
798 double alt, azi;
799 double duvw[3];
800 FVECT orig_dir[2];
801 int i;
802
803 if (fp == NULL) { /* just requesting number of bins? */
804 n = 0;
805 for (row = RowMax; row--; ) n += rnaz(row);
806 return(n);
807 }
808 row = 0; /* identify row & column */
809 col = b;
810 while (col >= rnaz(row)) {
811 col -= rnaz(row);
812 ++row;
813 }
814 while (n--) { /* stratified sampling */
815 SDmultiSamp(samp3, 3, (n+frandom())/sampcnt);
816 alt = (row+samp3[1])*RAH;
817 azi = (2.*PI)*(col+samp3[2]-.5)/rnaz(row);
818 duvw[2] = cos(alt); /* measured from horizon */
819 duvw[0] = tsin(azi)*duvw[2];
820 duvw[1] = tcos(azi)*duvw[2];
821 duvw[2] = sqrt(1. - duvw[2]*duvw[2]);
822 for (i = 3; i--; )
823 orig_dir[1][i] = -duvw[0]*p->udir[i] -
824 duvw[1]*p->vdir[i] -
825 duvw[2]*p->nrm[i] ;
826 if (!sample_origin(p, orig_dir[0], orig_dir[1], samp3[0]))
827 return(0);
828 if (fwrite(orig_dir, sizeof(FVECT), 2, fp) != 2)
829 return(0);
830 }
831 return(1);
832 #undef rnaz
833 #undef T_NALT
834 }
835
836 /* Klems sample generator */
837 static int
838 sample_klems(PARAMS *p, int b, FILE *fp)
839 {
840 static const char bname[4][20] = {
841 "LBNL/Klems Full",
842 "LBNL/Klems Half",
843 "INTERNAL ERROR",
844 "LBNL/Klems Quarter"
845 };
846 static ANGLE_BASIS *kbasis[4];
847 const int bi = p->hemis[1] - '1';
848 int n = sampcnt;
849 double samp2[2];
850 double duvw[3];
851 FVECT orig_dir[2];
852 int i;
853
854 if (!kbasis[bi]) { /* need to get basis, first */
855 for (i = 4; i--; )
856 if (!strcasecmp(abase_list[i].name, bname[bi])) {
857 kbasis[bi] = &abase_list[i];
858 break;
859 }
860 if (i < 0) {
861 fprintf(stderr, "%s: unknown hemisphere basis '%s'\n",
862 progname, bname[bi]);
863 return(0);
864 }
865 }
866 if (fp == NULL) /* just requesting number of bins? */
867 return(kbasis[bi]->nangles);
868
869 while (n--) { /* stratified sampling */
870 SDmultiSamp(samp2, 2, (n+frandom())/sampcnt);
871 if (!fo_getvec(duvw, b+samp2[1], kbasis[bi]))
872 return(0);
873 for (i = 3; i--; )
874 orig_dir[1][i] = -duvw[0]*p->udir[i] -
875 duvw[1]*p->vdir[i] -
876 duvw[2]*p->nrm[i] ;
877 if (!sample_origin(p, orig_dir[0], orig_dir[1], samp2[0]))
878 return(0);
879 if (fwrite(orig_dir, sizeof(FVECT), 2, fp) != 2)
880 return(0);
881 }
882 return(1);
883 }
884
885 /* Prepare hemisphere basis sampler that will send rays to rcontrib */
886 static int
887 prepare_sampler(void)
888 {
889 if (curparams.slist == NULL) { /* missing sample surface! */
890 fputs(progname, stderr);
891 fputs(": no sender surface!\n", stderr);
892 return(-1);
893 }
894 /* misplaced output file spec. */
895 if ((curparams.outfn != NULL) & (verbose >= 0))
896 fprintf(stderr, "%s: warning - ignoring output file in sender ('%s')\n",
897 progname, curparams.outfn);
898 /* check/set basis hemisphere */
899 if (!curparams.hemis[0]) {
900 fputs(progname, stderr);
901 fputs(": missing sender sampling type!\n", stderr);
902 return(-1);
903 }
904 if (normalize(curparams.nrm) == 0) {
905 fputs(progname, stderr);
906 fputs(": undefined normal for sender sampling\n", stderr);
907 return(-1);
908 }
909 if (normalize(curparams.vup) == 0) {
910 if (fabs(curparams.nrm[2]) < .7)
911 curparams.vup[2] = 1;
912 else
913 curparams.vup[1] = 1;
914 }
915 fcross(curparams.udir, curparams.vup, curparams.nrm);
916 if (normalize(curparams.udir) == 0) {
917 fputs(progname, stderr);
918 fputs(": up vector coincides with sender normal\n", stderr);
919 return(-1);
920 }
921 fcross(curparams.vdir, curparams.nrm, curparams.udir);
922 if (curparams.sign == '-') { /* left-handed coordinate system? */
923 curparams.udir[0] *= -1.;
924 curparams.udir[1] *= -1.;
925 curparams.udir[2] *= -1.;
926 }
927 if (tolower(curparams.hemis[0]) == 'u' | curparams.hemis[0] == '1')
928 curparams.sample_basis = sample_uniform;
929 else if (tolower(curparams.hemis[0]) == 's' &&
930 tolower(curparams.hemis[1]) == 'c')
931 curparams.sample_basis = sample_shirchiu;
932 else if ((tolower(curparams.hemis[0]) == 'r') |
933 (tolower(curparams.hemis[0]) == 't'))
934 curparams.sample_basis = sample_reinhart;
935 else if (tolower(curparams.hemis[0]) == 'k') {
936 switch (curparams.hemis[1]) {
937 case '1':
938 case '2':
939 case '4':
940 break;
941 case 'f':
942 case 'F':
943 case '\0':
944 curparams.hemis[1] = '1';
945 break;
946 case 'h':
947 case 'H':
948 curparams.hemis[1] = '2';
949 break;
950 case 'q':
951 case 'Q':
952 curparams.hemis[1] = '4';
953 break;
954 default:
955 goto unrecognized;
956 }
957 curparams.hemis[2] = '\0';
958 curparams.sample_basis = sample_klems;
959 } else
960 goto unrecognized;
961 /* return number of bins */
962 return((*curparams.sample_basis)(&curparams,0,NULL));
963 unrecognized:
964 fprintf(stderr, "%s: unrecognized sender sampling: h=%s\n",
965 progname, curparams.hemis);
966 return(-1);
967 }
968
969 /* Compute normal and area for polygon */
970 static int
971 finish_polygon(SURF *p)
972 {
973 const int nv = p->nfargs / 3;
974 FVECT e1, e2, vc;
975 int i;
976
977 memset(p->snrm, 0, sizeof(FVECT));
978 VSUB(e1, p->farg+3, p->farg);
979 for (i = 2; i < nv; i++) {
980 VSUB(e2, p->farg+3*i, p->farg);
981 VCROSS(vc, e1, e2);
982 p->snrm[0] += vc[0];
983 p->snrm[1] += vc[1];
984 p->snrm[2] += vc[2];
985 VCOPY(e1, e2);
986 }
987 p->area = normalize(p->snrm)*0.5;
988 return(p->area > FTINY);
989 }
990
991 /* Add a surface to our current parameters */
992 static void
993 add_surface(int st, const char *oname, FILE *fp)
994 {
995 SURF *snew;
996 int n;
997 /* get floating-point arguments */
998 if (!fscanf(fp, "%d", &n)) return;
999 while (n-- > 0) fscanf(fp, "%*s");
1000 if (!fscanf(fp, "%d", &n)) return;
1001 while (n-- > 0) fscanf(fp, "%*d");
1002 if (!fscanf(fp, "%d", &n) || n <= 0) return;
1003 snew = (SURF *)malloc(sizeof(SURF) + sizeof(double)*(n-1));
1004 if (snew == NULL) {
1005 fputs(progname, stderr);
1006 fputs(": out of memory!\n", stderr);
1007 exit(1);
1008 }
1009 strncpy(snew->sname, oname, sizeof(snew->sname)-1);
1010 snew->sname[sizeof(snew->sname)-1] = '\0';
1011 snew->styp = st;
1012 snew->priv = NULL;
1013 snew->nfargs = n;
1014 for (n = 0; n < snew->nfargs; n++)
1015 if (fscanf(fp, "%lf", &snew->farg[n]) != 1) {
1016 fprintf(stderr, "%s: error reading arguments for '%s'\n",
1017 progname, oname);
1018 exit(1);
1019 }
1020 switch (st) {
1021 case ST_RING:
1022 if (snew->nfargs != 8)
1023 goto badcount;
1024 VCOPY(snew->snrm, snew->farg+3);
1025 if (normalize(snew->snrm) == 0)
1026 goto badnorm;
1027 if (snew->farg[7] < snew->farg[6]) {
1028 double t = snew->farg[7];
1029 snew->farg[7] = snew->farg[6];
1030 snew->farg[6] = t;
1031 }
1032 snew->area = PI*(snew->farg[7]*snew->farg[7] -
1033 snew->farg[6]*snew->farg[6]);
1034 break;
1035 case ST_POLY:
1036 if (snew->nfargs < 9 || snew->nfargs % 3)
1037 goto badcount;
1038 finish_polygon(snew);
1039 break;
1040 case ST_SOURCE:
1041 if (snew->nfargs != 4)
1042 goto badcount;
1043 for (n = 3; n--; ) /* need to reverse "normal" */
1044 snew->snrm[n] = -snew->farg[n];
1045 if (normalize(snew->snrm) == 0)
1046 goto badnorm;
1047 snew->area = sin((PI/180./2.)*snew->farg[3]);
1048 snew->area *= PI*snew->area;
1049 break;
1050 }
1051 if ((snew->area <= FTINY) & (verbose >= 0)) {
1052 fprintf(stderr, "%s: warning - zero area for surface '%s'\n",
1053 progname, oname);
1054 free(snew);
1055 return;
1056 }
1057 VSUM(curparams.nrm, curparams.nrm, snew->snrm, snew->area);
1058 snew->next = curparams.slist;
1059 curparams.slist = snew;
1060 curparams.nsurfs++;
1061 return;
1062 badcount:
1063 fprintf(stderr, "%s: bad argument count for surface element '%s'\n",
1064 progname, oname);
1065 exit(1);
1066 badnorm:
1067 fprintf(stderr, "%s: bad orientation for surface element '%s'\n",
1068 progname, oname);
1069 exit(1);
1070 }
1071
1072 /* Parse a receiver object (look for modifiers to add to rcontrib command) */
1073 static int
1074 add_recv_object(FILE *fp)
1075 {
1076 int st;
1077 char thismod[128], otype[32], oname[128];
1078 int n;
1079
1080 if (fscanf(fp, "%s %s %s", thismod, otype, oname) != 3)
1081 return(0); /* must have hit EOF! */
1082 if (!strcmp(otype, "alias")) {
1083 fscanf(fp, "%*s"); /* skip alias */
1084 return(0);
1085 }
1086 /* is it a new receiver? */
1087 if ((st = surf_type(otype)) != ST_NONE) {
1088 if (curparams.slist != NULL && (st == ST_SOURCE) ^
1089 (curparams.slist->styp == ST_SOURCE)) {
1090 fputs(progname, stderr);
1091 fputs(": cannot mix source/non-source receivers!\n", stderr);
1092 return(-1);
1093 }
1094 if (strcmp(thismod, curmod)) {
1095 if (curmod[0]) { /* output last receiver? */
1096 finish_receiver();
1097 clear_params(&curparams, 1);
1098 }
1099 parse_params(&curparams, newparams);
1100 newparams[0] = '\0';
1101 strcpy(curmod, thismod);
1102 }
1103 add_surface(st, oname, fp); /* read & store surface */
1104 return(1);
1105 }
1106 /* else skip arguments */
1107 if (!fscanf(fp, "%d", &n)) return(0);
1108 while (n-- > 0) fscanf(fp, "%*s");
1109 if (!fscanf(fp, "%d", &n)) return(0);
1110 while (n-- > 0) fscanf(fp, "%*d");
1111 if (!fscanf(fp, "%d", &n)) return(0);
1112 while (n-- > 0) fscanf(fp, "%*f");
1113 return(0);
1114 }
1115
1116 /* Parse a sender object */
1117 static int
1118 add_send_object(FILE *fp)
1119 {
1120 int st;
1121 char thismod[128], otype[32], oname[128];
1122 int n;
1123
1124 if (fscanf(fp, "%s %s %s", thismod, otype, oname) != 3)
1125 return(0); /* must have hit EOF! */
1126 if (!strcmp(otype, "alias")) {
1127 fscanf(fp, "%*s"); /* skip alias */
1128 return(0);
1129 }
1130 /* is it a new surface? */
1131 if ((st = surf_type(otype)) != ST_NONE) {
1132 if (st == ST_SOURCE) {
1133 fputs(progname, stderr);
1134 fputs(": cannot use source as a sender!\n", stderr);
1135 return(-1);
1136 }
1137 if (strcmp(thismod, curmod)) {
1138 if (curmod[0]) {
1139 fputs(progname, stderr);
1140 fputs(": warning - multiple modifiers in sender\n",
1141 stderr);
1142 }
1143 strcpy(curmod, thismod);
1144 }
1145 parse_params(&curparams, newparams);
1146 newparams[0] = '\0';
1147 add_surface(st, oname, fp); /* read & store surface */
1148 return(0);
1149 }
1150 /* else skip arguments */
1151 if (!fscanf(fp, "%d", &n)) return(0);
1152 while (n-- > 0) fscanf(fp, "%*s");
1153 if (!fscanf(fp, "%d", &n)) return(0);
1154 while (n-- > 0) fscanf(fp, "%*d");
1155 if (!fscanf(fp, "%d", &n)) return(0);
1156 while (n-- > 0) fscanf(fp, "%*f");
1157 return(0);
1158 }
1159
1160 /* Load a Radiance scene using the given callback function for objects */
1161 static int
1162 load_scene(const char *inspec, int (*ocb)(FILE *))
1163 {
1164 int rv = 0;
1165 char inpbuf[1024];
1166 FILE *fp;
1167 int c;
1168
1169 if (*inspec == '!')
1170 fp = popen(inspec+1, "r");
1171 else
1172 fp = fopen(inspec, "r");
1173 if (fp == NULL) {
1174 fprintf(stderr, "%s: cannot load '%s'\n", progname, inspec);
1175 return(-1);
1176 }
1177 while ((c = getc(fp)) != EOF) { /* load receiver data */
1178 if (isspace(c)) /* skip leading white space */
1179 continue;
1180 if (c == '!') { /* read from a new command */
1181 inpbuf[0] = c;
1182 if (fgetline(inpbuf+1, sizeof(inpbuf)-1, fp) != NULL) {
1183 if ((c = load_scene(inpbuf, ocb)) < 0)
1184 return(c);
1185 rv += c;
1186 }
1187 continue;
1188 }
1189 if (c == '#') { /* parameters/comment */
1190 if ((c = getc(fp)) == EOF || ungetc(c,fp) == EOF)
1191 break;
1192 if (!isspace(c) && fscanf(fp, "%s", inpbuf) == 1 &&
1193 !strcmp(inpbuf, PARAMSTART)) {
1194 if (fgets(inpbuf, sizeof(inpbuf), fp) != NULL)
1195 strcat(newparams, inpbuf);
1196 continue;
1197 }
1198 while ((c = getc(fp)) != EOF && c != '\n')
1199 ; /* else skipping comment */
1200 continue;
1201 }
1202 ungetc(c, fp); /* else check object for receiver */
1203 c = (*ocb)(fp);
1204 if (c < 0)
1205 return(c);
1206 rv += c;
1207 }
1208 /* close our input stream */
1209 c = (*inspec == '!') ? pclose(fp) : fclose(fp);
1210 if (c != 0) {
1211 fprintf(stderr, "%s: error loading '%s'\n", progname, inspec);
1212 return(-1);
1213 }
1214 return(rv);
1215 }
1216
1217 /* Get command arguments and run program */
1218 int
1219 main(int argc, char *argv[])
1220 {
1221 char fmtopt[6] = "-faa"; /* default output is ASCII */
1222 char *xrs=NULL, *yrs=NULL, *ldopt=NULL;
1223 char *iropt = NULL;
1224 char *sendfn;
1225 char sampcntbuf[32], nsbinbuf[32];
1226 FILE *rcfp;
1227 int nsbins;
1228 int a, i;
1229 /* screen rcontrib options */
1230 progname = argv[0];
1231 for (a = 1; a < argc-2; a++) {
1232 int na;
1233 /* check for argument expansion */
1234 while ((na = expandarg(&argc, &argv, a)) > 0)
1235 ;
1236 if (na < 0) {
1237 fprintf(stderr, "%s: cannot expand '%s'\n",
1238 progname, argv[a]);
1239 return(1);
1240 }
1241 if (argv[a][0] != '-' || !argv[a][1])
1242 break;
1243 na = 1;
1244 switch (argv[a][1]) { /* !! Keep consistent !! */
1245 case 'v': /* verbose mode */
1246 verbose = 1;
1247 na = 0;
1248 continue;
1249 case 'f': /* special case for -fo, -ff, etc. */
1250 switch (argv[a][2]) {
1251 case '\0': /* cal file */
1252 goto userr;
1253 case 'o': /* force output */
1254 goto userr;
1255 case 'a': /* output format */
1256 case 'f':
1257 case 'd':
1258 case 'c':
1259 if (!(fmtopt[3] = argv[a][3]))
1260 fmtopt[3] = argv[a][2];
1261 fmtopt[2] = argv[a][2];
1262 na = 0;
1263 continue; /* will pass later */
1264 default:
1265 goto userr;
1266 }
1267 break;
1268 case 'x': /* x-resolution */
1269 xrs = argv[++a];
1270 na = 0;
1271 continue;
1272 case 'y': /* y-resolution */
1273 yrs = argv[++a];
1274 na = 0;
1275 continue;
1276 case 'c': /* number of samples */
1277 sampcnt = atoi(argv[++a]);
1278 if (sampcnt <= 0)
1279 goto userr;
1280 na = 0; /* we re-add this later */
1281 continue;
1282 case 'I': /* only for pass-through mode */
1283 case 'i':
1284 iropt = argv[a];
1285 na = 0;
1286 continue;
1287 case 'w': /* options without arguments */
1288 if (argv[a][2] != '+') verbose = -1;
1289 case 'V':
1290 case 'u':
1291 case 'h':
1292 case 'r':
1293 break;
1294 case 'n': /* options with 1 argument */
1295 case 's':
1296 case 'o':
1297 na = 2;
1298 break;
1299 case 'b': /* special case */
1300 if (argv[a][2] != 'v') goto userr;
1301 break;
1302 case 'l': /* special case */
1303 if (argv[a][2] == 'd') {
1304 ldopt = argv[a];
1305 na = 0;
1306 continue;
1307 }
1308 na = 2;
1309 break;
1310 case 'd': /* special case */
1311 if (argv[a][2] != 'v') na = 2;
1312 break;
1313 case 'a': /* special case */
1314 if (argv[a][2] == 'p') {
1315 na = 2; /* photon map [+ bandwidth(s)] */
1316 if (a < argc-3 && atoi(argv[a+1]) > 0)
1317 na += 1 + (a < argc-4 && atoi(argv[a+2]) > 0);
1318 } else
1319 na = (argv[a][2] == 'v') ? 4 : 2;
1320 break;
1321 case 'm': /* special case */
1322 if (!argv[a][2]) goto userr;
1323 na = (argv[a][2] == 'e') | (argv[a][2] == 'a') ? 4 : 2;
1324 break;
1325 default: /* anything else is verbotten */
1326 goto userr;
1327 }
1328 if (na <= 0) continue;
1329 CHECKARGC(na); /* pass on option */
1330 rcarg[nrcargs++] = argv[a];
1331 while (--na) /* + arguments if any */
1332 rcarg[nrcargs++] = argv[++a];
1333 }
1334 if (a > argc-2)
1335 goto userr; /* check at end of options */
1336 sendfn = argv[a++]; /* assign sender & receiver inputs */
1337 if (sendfn[0] == '-') { /* user wants pass-through mode? */
1338 if (sendfn[1]) goto userr;
1339 sendfn = NULL;
1340 if (iropt) {
1341 CHECKARGC(1);
1342 rcarg[nrcargs++] = iropt;
1343 }
1344 if (xrs) {
1345 CHECKARGC(2);
1346 rcarg[nrcargs++] = "-x";
1347 rcarg[nrcargs++] = xrs;
1348 }
1349 if (yrs) {
1350 CHECKARGC(2);
1351 rcarg[nrcargs++] = "-y";
1352 rcarg[nrcargs++] = yrs;
1353 }
1354 if (ldopt) {
1355 CHECKARGC(1);
1356 rcarg[nrcargs++] = ldopt;
1357 }
1358 if (sampcnt <= 0) sampcnt = 1;
1359 } else { /* else in sampling mode */
1360 if (iropt) {
1361 fputs(progname, stderr);
1362 fputs(": -i, -I supported for pass-through only\n", stderr);
1363 return(1);
1364 }
1365 fmtopt[2] = (sizeof(RREAL)==sizeof(double)) ? 'd' : 'f';
1366 if (sampcnt <= 0) sampcnt = 10000;
1367 }
1368 sprintf(sampcntbuf, "%d", sampcnt);
1369 CHECKARGC(3); /* add our format & sample count */
1370 rcarg[nrcargs++] = fmtopt;
1371 rcarg[nrcargs++] = "-c";
1372 rcarg[nrcargs++] = sampcntbuf;
1373 /* add receiver arguments to rcontrib */
1374 if (load_scene(argv[a], add_recv_object) < 0)
1375 return(1);
1376 finish_receiver();
1377 if (sendfn == NULL) { /* pass-through mode? */
1378 CHECKARGC(1); /* add octree */
1379 rcarg[nrcargs++] = oconv_command(argc-a, argv+a);
1380 rcarg[nrcargs] = NULL;
1381 return(my_exec(rcarg)); /* rcontrib does everything */
1382 }
1383 clear_params(&curparams, 0); /* else load sender surface & params */
1384 curmod[0] = '\0';
1385 if (load_scene(sendfn, add_send_object) < 0)
1386 return(1);
1387 if ((nsbins = prepare_sampler()) <= 0)
1388 return(1);
1389 CHECKARGC(3); /* add row count and octree */
1390 rcarg[nrcargs++] = "-y";
1391 sprintf(nsbinbuf, "%d", nsbins);
1392 rcarg[nrcargs++] = nsbinbuf;
1393 rcarg[nrcargs++] = oconv_command(argc-a, argv+a);
1394 rcarg[nrcargs] = NULL;
1395 /* open pipe to rcontrib process */
1396 if ((rcfp = popen_arglist(rcarg, "w")) == NULL)
1397 return(1);
1398 SET_FILE_BINARY(rcfp);
1399 #ifdef getc_unlocked
1400 flockfile(rcfp);
1401 #endif
1402 if (verbose > 0) {
1403 fprintf(stderr, "%s: sampling %d directions", progname, nsbins);
1404 if (curparams.nsurfs > 1)
1405 fprintf(stderr, " (%d elements)\n", curparams.nsurfs);
1406 else
1407 fputc('\n', stderr);
1408 }
1409 for (i = 0; i < nsbins; i++) /* send rcontrib ray samples */
1410 if (!(*curparams.sample_basis)(&curparams, i, rcfp))
1411 return(1);
1412 return(pclose(rcfp) < 0); /* all finished! */
1413 userr:
1414 if (a < argc-2)
1415 fprintf(stderr, "%s: unsupported option '%s'", progname, argv[a]);
1416 fprintf(stderr, "Usage: %s [-v][rcontrib options] sender.rad receiver.rad [-i system.oct] [system.rad ..]\n",
1417 progname);
1418 return(1);
1419 }