ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rfluxmtx.c
Revision: 2.57
Committed: Tue Mar 19 10:48:05 2024 UTC (5 weeks, 3 days ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.56: +3 -3 lines
Log Message:
fix(rfluxmtx,genBSDF): Fixed issue with -c{s,w} argument parsing

File Contents

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