ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rfluxmtx.c
Revision: 2.44
Committed: Thu Apr 13 22:47:17 2017 UTC (6 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.43: +3 -3 lines
Log Message:
Added parens to eliminate compiler warnings

File Contents

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