ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/Development/ray/src/util/rfluxmtx.c
Revision: 2.67
Committed: Fri Nov 14 23:51:42 2025 UTC (3 days, 4 hours ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.66: +52 -30 lines
Log Message:
feat(rfluxmtx,rxfluxmtx): Added bin sample jittering for smoother visual results

File Contents

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