ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.17
Committed: Tue Oct 16 22:00:51 2012 UTC (11 years, 7 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.16: +19 -12 lines
Log Message:
Fixed memory leak and termination issue

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pabopto2xml.c,v 2.16 2012/10/14 22:31:20 greg Exp $";
3 #endif
4 /*
5 * Convert PAB-Opto measurements to XML format using tensor tree representation
6 * Employs Bonneel et al. Earth Mover's Distance interpolant.
7 *
8 * G.Ward
9 */
10
11 #ifndef _WIN32
12 #include <unistd.h>
13 #include <sys/wait.h>
14 #include <sys/mman.h>
15 #endif
16 #define _USE_MATH_DEFINES
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <math.h>
22 #include "bsdf.h"
23
24 #define DEBUG 1
25
26 #ifndef GRIDRES
27 #define GRIDRES 200 /* grid resolution per side */
28 #endif
29
30 #define RSCA 2.7 /* radius scaling factor (empirical) */
31
32 /* convert to/from coded radians */
33 #define ANG2R(r) (int)((r)*((1<<16)/M_PI))
34 #define R2ANG(c) (((c)+.5)*(M_PI/(1<<16)))
35
36 typedef struct {
37 float vsum; /* DSF sum */
38 unsigned short nval; /* number of values in sum */
39 unsigned short crad; /* radius (coded angle) */
40 } GRIDVAL; /* grid value */
41
42 typedef struct {
43 float peak; /* lobe value at peak */
44 unsigned short crad; /* radius (coded angle) */
45 unsigned char gx, gy; /* grid position */
46 } RBFVAL; /* radial basis function value */
47
48 struct s_rbfnode; /* forward declaration of RBF struct */
49
50 typedef struct s_migration {
51 struct s_migration *next; /* next in global edge list */
52 struct s_rbfnode *rbfv[2]; /* from,to vertex */
53 struct s_migration *enxt[2]; /* next from,to sibling */
54 float mtx[1]; /* matrix (extends struct) */
55 } MIGRATION; /* migration link (winged edge structure) */
56
57 typedef struct s_rbfnode {
58 struct s_rbfnode *next; /* next in global RBF list */
59 MIGRATION *ejl; /* edge list for this vertex */
60 FVECT invec; /* incident vector direction */
61 double vtotal; /* volume for normalization */
62 int nrbf; /* number of RBFs */
63 RBFVAL rbfa[1]; /* RBF array (extends struct) */
64 } RBFNODE; /* RBF representation of DSF @ 1 incidence */
65
66 /* our loaded grid for this incident angle */
67 static double theta_in_deg, phi_in_deg;
68 static GRIDVAL dsf_grid[GRIDRES][GRIDRES];
69
70 /* all incident angles in-plane so far? */
71 static int single_plane_incident = -1;
72
73 /* input/output orientations */
74 static int input_orient = 0;
75 static int output_orient = 0;
76
77 /* processed incident DSF measurements */
78 static RBFNODE *dsf_list = NULL;
79
80 /* RBF-linking matrices (edges) */
81 static MIGRATION *mig_list = NULL;
82
83 /* migration edges drawn in raster fashion */
84 static MIGRATION *mig_grid[GRIDRES][GRIDRES];
85
86 #define mtx_nrows(m) ((m)->rbfv[0]->nrbf)
87 #define mtx_ncols(m) ((m)->rbfv[1]->nrbf)
88 #define mtx_ndx(m,i,j) ((i)*mtx_ncols(m) + (j))
89 #define is_src(rbf,m) ((rbf) == (m)->rbfv[0])
90 #define is_dest(rbf,m) ((rbf) == (m)->rbfv[1])
91 #define nextedge(rbf,m) (m)->enxt[is_dest(rbf,m)]
92 #define opp_rbf(rbf,m) (m)->rbfv[is_src(rbf,m)]
93
94 #define round(v) (int)((v) + .5 - ((v) < -.5))
95
96 char *progname;
97
98 #ifdef DEBUG /* percentage to cull (<0 to turn off) */
99 int pctcull = -1;
100 #else
101 int pctcull = 90;
102 #endif
103 /* number of processes to run */
104 int nprocs = 1;
105 /* number of children (-1 in child) */
106 int nchild = 0;
107
108 /* sampling order (set by data density) */
109 int samp_order = 0;
110
111 /* Compute volume associated with Gaussian lobe */
112 static double
113 rbf_volume(const RBFVAL *rbfp)
114 {
115 double rad = R2ANG(rbfp->crad);
116
117 return((2.*M_PI) * rbfp->peak * rad*rad);
118 }
119
120 /* Compute outgoing vector from grid position */
121 static void
122 ovec_from_pos(FVECT vec, int xpos, int ypos)
123 {
124 double uv[2];
125 double r2;
126
127 SDsquare2disk(uv, (1./GRIDRES)*(xpos+.5), (1./GRIDRES)*(ypos+.5));
128 /* uniform hemispherical projection */
129 r2 = uv[0]*uv[0] + uv[1]*uv[1];
130 vec[0] = vec[1] = sqrt(2. - r2);
131 vec[0] *= uv[0];
132 vec[1] *= uv[1];
133 vec[2] = output_orient*(1. - r2);
134 }
135
136 /* Compute grid position from normalized input/output vector */
137 static void
138 pos_from_vec(int pos[2], const FVECT vec)
139 {
140 double sq[2]; /* uniform hemispherical projection */
141 double norm = 1./sqrt(1. + fabs(vec[2]));
142
143 SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
144
145 pos[0] = (int)(sq[0]*GRIDRES);
146 pos[1] = (int)(sq[1]*GRIDRES);
147 }
148
149 /* Evaluate RBF for DSF at the given normalized outgoing direction */
150 static double
151 eval_rbfrep(const RBFNODE *rp, const FVECT outvec)
152 {
153 double res = .0;
154 const RBFVAL *rbfp;
155 FVECT odir;
156 double sig2;
157 int n;
158
159 if (rp == NULL)
160 return(.0);
161 rbfp = rp->rbfa;
162 for (n = rp->nrbf; n--; rbfp++) {
163 ovec_from_pos(odir, rbfp->gx, rbfp->gy);
164 sig2 = R2ANG(rbfp->crad);
165 sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
166 if (sig2 > -19.)
167 res += rbfp->peak * exp(sig2);
168 }
169 return(res);
170 }
171
172 /* Insert a new directional scattering function in our global list */
173 static void
174 insert_dsf(RBFNODE *newrbf)
175 {
176 RBFNODE *rbf, *rbf_last;
177 /* check for redundant meas. */
178 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next)
179 if (DOT(rbf->invec, newrbf->invec) >= 1.-FTINY) {
180 fputs("Duplicate incident measurement (ignored)\n", stderr);
181 free(newrbf);
182 return;
183 }
184 /* keep in ascending theta order */
185 for (rbf_last = NULL, rbf = dsf_list;
186 single_plane_incident & (rbf != NULL);
187 rbf_last = rbf, rbf = rbf->next)
188 if (input_orient*rbf->invec[2] < input_orient*newrbf->invec[2])
189 break;
190 if (rbf_last == NULL) {
191 newrbf->next = dsf_list;
192 dsf_list = newrbf;
193 return;
194 }
195 newrbf->next = rbf;
196 rbf_last->next = newrbf;
197 }
198
199 /* Count up filled nodes and build RBF representation from current grid */
200 static RBFNODE *
201 make_rbfrep(void)
202 {
203 int niter = 16;
204 int minrad = ANG2R(pow(2., 1.-samp_order));
205 double lastVar, thisVar = 100.;
206 int nn;
207 RBFNODE *newnode;
208 int i, j;
209
210 nn = 0; /* count selected bins */
211 for (i = 0; i < GRIDRES; i++)
212 for (j = 0; j < GRIDRES; j++)
213 nn += dsf_grid[i][j].nval;
214 /* allocate RBF array */
215 newnode = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(nn-1));
216 if (newnode == NULL) {
217 fputs("Out of memory in make_rbfrep()\n", stderr);
218 exit(1);
219 }
220 newnode->next = NULL;
221 newnode->ejl = NULL;
222 newnode->invec[2] = sin(M_PI/180.*theta_in_deg);
223 newnode->invec[0] = cos(M_PI/180.*phi_in_deg)*newnode->invec[2];
224 newnode->invec[1] = sin(M_PI/180.*phi_in_deg)*newnode->invec[2];
225 newnode->invec[2] = input_orient*sqrt(1. - newnode->invec[2]*newnode->invec[2]);
226 newnode->vtotal = 0;
227 newnode->nrbf = nn;
228 nn = 0; /* fill RBF array */
229 for (i = 0; i < GRIDRES; i++)
230 for (j = 0; j < GRIDRES; j++)
231 if (dsf_grid[i][j].nval) {
232 newnode->rbfa[nn].peak = dsf_grid[i][j].vsum;
233 newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
234 newnode->rbfa[nn].gx = i;
235 newnode->rbfa[nn].gy = j;
236 if (newnode->rbfa[nn].crad < minrad)
237 minrad = newnode->rbfa[nn].crad;
238 ++nn;
239 }
240 /* iterate to improve interpolation accuracy */
241 do {
242 double dsum = 0, dsum2 = 0;
243 nn = 0;
244 for (i = 0; i < GRIDRES; i++)
245 for (j = 0; j < GRIDRES; j++)
246 if (dsf_grid[i][j].nval) {
247 FVECT odir;
248 double corr;
249 ovec_from_pos(odir, i, j);
250 newnode->rbfa[nn++].peak *= corr =
251 dsf_grid[i][j].vsum /
252 eval_rbfrep(newnode, odir);
253 dsum += corr - 1.;
254 dsum2 += (corr-1.)*(corr-1.);
255 }
256 lastVar = thisVar;
257 thisVar = dsum2/(double)nn;
258 #ifdef DEBUG
259 fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
260 100.*dsum/(double)nn,
261 100.*sqrt(thisVar));
262 #endif
263 } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
264
265 nn = 0; /* compute sum for normalization */
266 while (nn < newnode->nrbf)
267 newnode->vtotal += rbf_volume(&newnode->rbfa[nn++]);
268
269 insert_dsf(newnode);
270 /* adjust sampling resolution */
271 samp_order = log(2./R2ANG(minrad))/M_LN2 + .5;
272
273 return(newnode);
274 }
275
276 /* Load a set of measurements corresponding to a particular incident angle */
277 static int
278 load_pabopto_meas(const char *fname)
279 {
280 FILE *fp = fopen(fname, "r");
281 int inp_is_DSF = -1;
282 double new_phi, theta_out, phi_out, val;
283 char buf[2048];
284 int n, c;
285
286 if (fp == NULL) {
287 fputs(fname, stderr);
288 fputs(": cannot open\n", stderr);
289 return(0);
290 }
291 memset(dsf_grid, 0, sizeof(dsf_grid));
292 #ifdef DEBUG
293 fprintf(stderr, "Loading measurement file '%s'...\n", fname);
294 #endif
295 /* read header information */
296 while ((c = getc(fp)) == '#' || c == EOF) {
297 if (fgets(buf, sizeof(buf), fp) == NULL) {
298 fputs(fname, stderr);
299 fputs(": unexpected EOF\n", stderr);
300 fclose(fp);
301 return(0);
302 }
303 if (!strcmp(buf, "format: theta phi DSF\n")) {
304 inp_is_DSF = 1;
305 continue;
306 }
307 if (!strcmp(buf, "format: theta phi BSDF\n")) {
308 inp_is_DSF = 0;
309 continue;
310 }
311 if (sscanf(buf, "intheta %lf", &theta_in_deg) == 1)
312 continue;
313 if (sscanf(buf, "inphi %lf", &new_phi) == 1)
314 continue;
315 if (sscanf(buf, "incident_angle %lf %lf",
316 &theta_in_deg, &new_phi) == 2)
317 continue;
318 }
319 if (inp_is_DSF < 0) {
320 fputs(fname, stderr);
321 fputs(": unknown format\n", stderr);
322 fclose(fp);
323 return(0);
324 }
325 if (!input_orient) /* check input orientation */
326 input_orient = 1 - 2*(theta_in_deg > 90.);
327 else if (input_orient > 0 ^ theta_in_deg < 90.) {
328 fputs("Cannot handle input angles on both sides of surface\n",
329 stderr);
330 exit(1);
331 }
332 if (single_plane_incident > 0) /* check if still in plane */
333 single_plane_incident = (round(new_phi) == round(phi_in_deg));
334 else if (single_plane_incident < 0)
335 single_plane_incident = 1;
336 phi_in_deg = new_phi;
337 ungetc(c, fp); /* read actual data */
338 while (fscanf(fp, "%lf %lf %lf\n", &theta_out, &phi_out, &val) == 3) {
339 FVECT ovec;
340 int pos[2];
341
342 if (!output_orient) /* check output orientation */
343 output_orient = 1 - 2*(theta_out > 90.);
344 else if (output_orient > 0 ^ theta_out < 90.) {
345 fputs("Cannot handle output angles on both sides of surface\n",
346 stderr);
347 exit(1);
348 }
349 ovec[2] = sin(M_PI/180.*theta_out);
350 ovec[0] = cos(M_PI/180.*phi_out) * ovec[2];
351 ovec[1] = sin(M_PI/180.*phi_out) * ovec[2];
352 ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
353
354 if (!inp_is_DSF)
355 val *= ovec[2]; /* convert from BSDF to DSF */
356
357 pos_from_vec(pos, ovec);
358
359 dsf_grid[pos[0]][pos[1]].vsum += val;
360 dsf_grid[pos[0]][pos[1]].nval++;
361 }
362 n = 0;
363 while ((c = getc(fp)) != EOF)
364 n += !isspace(c);
365 if (n)
366 fprintf(stderr,
367 "%s: warning: %d unexpected characters past EOD\n",
368 fname, n);
369 fclose(fp);
370 return(1);
371 }
372
373 /* Compute radii for non-empty bins */
374 /* (distance to furthest empty bin for which non-empty bin is the closest) */
375 static void
376 compute_radii(void)
377 {
378 unsigned int fill_grid[GRIDRES][GRIDRES];
379 unsigned short fill_cnt[GRIDRES][GRIDRES];
380 FVECT ovec0, ovec1;
381 double ang2, lastang2;
382 int r, i, j, jn, ii, jj, inear, jnear;
383
384 r = GRIDRES/2; /* proceed in zig-zag */
385 for (i = 0; i < GRIDRES; i++)
386 for (jn = 0; jn < GRIDRES; jn++) {
387 j = (i&1) ? jn : GRIDRES-1-jn;
388 if (dsf_grid[i][j].nval) /* find empty grid pos. */
389 continue;
390 ovec_from_pos(ovec0, i, j);
391 inear = jnear = -1; /* find nearest non-empty */
392 lastang2 = M_PI*M_PI;
393 for (ii = i-r; ii <= i+r; ii++) {
394 if (ii < 0) continue;
395 if (ii >= GRIDRES) break;
396 for (jj = j-r; jj <= j+r; jj++) {
397 if (jj < 0) continue;
398 if (jj >= GRIDRES) break;
399 if (!dsf_grid[ii][jj].nval)
400 continue;
401 ovec_from_pos(ovec1, ii, jj);
402 ang2 = 2. - 2.*DOT(ovec0,ovec1);
403 if (ang2 >= lastang2)
404 continue;
405 lastang2 = ang2;
406 inear = ii; jnear = jj;
407 }
408 }
409 if (inear < 0) {
410 fputs("Could not find non-empty neighbor!\n", stderr);
411 exit(1);
412 }
413 ang2 = sqrt(lastang2);
414 r = ANG2R(ang2); /* record if > previous */
415 if (r > dsf_grid[inear][jnear].crad)
416 dsf_grid[inear][jnear].crad = r;
417 /* next search radius */
418 r = ang2*(2.*GRIDRES/M_PI) + 3;
419 }
420 /* blur radii over hemisphere */
421 memset(fill_grid, 0, sizeof(fill_grid));
422 memset(fill_cnt, 0, sizeof(fill_cnt));
423 for (i = 0; i < GRIDRES; i++)
424 for (j = 0; j < GRIDRES; j++) {
425 if (!dsf_grid[i][j].crad)
426 continue; /* missing distance */
427 r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI);
428 for (ii = i-r; ii <= i+r; ii++) {
429 if (ii < 0) continue;
430 if (ii >= GRIDRES) break;
431 for (jj = j-r; jj <= j+r; jj++) {
432 if (jj < 0) continue;
433 if (jj >= GRIDRES) break;
434 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
435 continue;
436 fill_grid[ii][jj] += dsf_grid[i][j].crad;
437 fill_cnt[ii][jj]++;
438 }
439 }
440 }
441 /* copy back blurred radii */
442 for (i = 0; i < GRIDRES; i++)
443 for (j = 0; j < GRIDRES; j++)
444 if (fill_cnt[i][j])
445 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
446 }
447
448 /* Cull points for more uniform distribution, leave all nval 0 or 1 */
449 static void
450 cull_values(void)
451 {
452 FVECT ovec0, ovec1;
453 double maxang, maxang2;
454 int i, j, ii, jj, r;
455 /* simple greedy algorithm */
456 for (i = 0; i < GRIDRES; i++)
457 for (j = 0; j < GRIDRES; j++) {
458 if (!dsf_grid[i][j].nval)
459 continue;
460 if (!dsf_grid[i][j].crad)
461 continue; /* shouldn't happen */
462 ovec_from_pos(ovec0, i, j);
463 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
464 if (maxang > ovec0[2]) /* clamp near horizon */
465 maxang = ovec0[2];
466 r = maxang*(2.*GRIDRES/M_PI) + 1;
467 maxang2 = maxang*maxang;
468 for (ii = i-r; ii <= i+r; ii++) {
469 if (ii < 0) continue;
470 if (ii >= GRIDRES) break;
471 for (jj = j-r; jj <= j+r; jj++) {
472 if (jj < 0) continue;
473 if (jj >= GRIDRES) break;
474 if (!dsf_grid[ii][jj].nval)
475 continue;
476 if ((ii == i) & (jj == j))
477 continue; /* don't get self-absorbed */
478 ovec_from_pos(ovec1, ii, jj);
479 if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
480 continue;
481 /* absorb sum */
482 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
483 dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
484 /* keep value, though */
485 dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval;
486 dsf_grid[ii][jj].nval = 0;
487 }
488 }
489 }
490 /* final averaging pass */
491 for (i = 0; i < GRIDRES; i++)
492 for (j = 0; j < GRIDRES; j++)
493 if (dsf_grid[i][j].nval > 1) {
494 dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval;
495 dsf_grid[i][j].nval = 1;
496 }
497 }
498
499 /* Compute (and allocate) migration price matrix for optimization */
500 static float *
501 price_routes(const RBFNODE *from_rbf, const RBFNODE *to_rbf)
502 {
503 float *pmtx = (float *)malloc(sizeof(float) *
504 from_rbf->nrbf * to_rbf->nrbf);
505 FVECT *vto = (FVECT *)malloc(sizeof(FVECT) * to_rbf->nrbf);
506 int i, j;
507
508 if ((pmtx == NULL) | (vto == NULL)) {
509 fputs("Out of memory in migration_costs()\n", stderr);
510 exit(1);
511 }
512 for (j = to_rbf->nrbf; j--; ) /* save repetitive ops. */
513 ovec_from_pos(vto[j], to_rbf->rbfa[j].gx, to_rbf->rbfa[j].gy);
514
515 for (i = from_rbf->nrbf; i--; ) {
516 const double from_ang = R2ANG(from_rbf->rbfa[i].crad);
517 FVECT vfrom;
518 ovec_from_pos(vfrom, from_rbf->rbfa[i].gx, from_rbf->rbfa[i].gy);
519 for (j = to_rbf->nrbf; j--; )
520 pmtx[i*to_rbf->nrbf + j] = acos(DOT(vfrom, vto[j])) +
521 fabs(R2ANG(to_rbf->rbfa[j].crad) - from_ang);
522 }
523 free(vto);
524 return(pmtx);
525 }
526
527 /* Comparison routine needed for sorting price row */
528 static const float *price_arr;
529 static int
530 msrt_cmp(const void *p1, const void *p2)
531 {
532 float c1 = price_arr[*(const int *)p1];
533 float c2 = price_arr[*(const int *)p2];
534
535 if (c1 > c2) return(1);
536 if (c1 < c2) return(-1);
537 return(0);
538 }
539
540 /* Compute minimum (optimistic) cost for moving the given source material */
541 static double
542 min_cost(double amt2move, const double *avail, const float *price, int n)
543 {
544 static int *price_sort = NULL;
545 static int n_alloc = 0;
546 double total_cost = 0;
547 int i;
548
549 if (amt2move <= FTINY) /* pre-emptive check */
550 return(0.);
551 if (n > n_alloc) { /* (re)allocate sort array */
552 if (n_alloc) free(price_sort);
553 price_sort = (int *)malloc(sizeof(int)*n);
554 if (price_sort == NULL) {
555 fputs("Out of memory in min_cost()\n", stderr);
556 exit(1);
557 }
558 n_alloc = n;
559 }
560 for (i = n; i--; )
561 price_sort[i] = i;
562 price_arr = price;
563 qsort(price_sort, n, sizeof(int), &msrt_cmp);
564 /* move cheapest first */
565 for (i = 0; i < n && amt2move > FTINY; i++) {
566 int d = price_sort[i];
567 double amt = (amt2move < avail[d]) ? amt2move : avail[d];
568
569 total_cost += amt * price[d];
570 amt2move -= amt;
571 }
572 return(total_cost);
573 }
574
575 /* Take a step in migration by choosing optimal bucket to transfer */
576 static double
577 migration_step(MIGRATION *mig, double *src_rem, double *dst_rem, const float *pmtx)
578 {
579 static double *src_cost = NULL;
580 static int n_alloc = 0;
581 const double maxamt = .1; /* 2./(mtx_nrows(mig)*mtx_ncols(mig)); */
582 double amt = 0;
583 struct {
584 int s, d; /* source and destination */
585 double price; /* price estimate per amount moved */
586 double amt; /* amount we can move */
587 } cur, best;
588 int i;
589
590 if (mtx_nrows(mig) > n_alloc) { /* allocate cost array */
591 if (n_alloc)
592 free(src_cost);
593 src_cost = (double *)malloc(sizeof(double)*mtx_nrows(mig));
594 if (src_cost == NULL) {
595 fputs("Out of memory in migration_step()\n", stderr);
596 exit(1);
597 }
598 n_alloc = mtx_nrows(mig);
599 }
600 for (i = mtx_nrows(mig); i--; ) /* starting costs for diff. */
601 src_cost[i] = min_cost(src_rem[i], dst_rem,
602 pmtx+i*mtx_ncols(mig), mtx_ncols(mig));
603
604 /* find best source & dest. */
605 best.s = best.d = -1; best.price = FHUGE; best.amt = 0;
606 for (cur.s = mtx_nrows(mig); cur.s--; ) {
607 const float *price = pmtx + cur.s*mtx_ncols(mig);
608 double cost_others = 0;
609 if (src_rem[cur.s] <= FTINY)
610 continue;
611 cur.d = -1; /* examine cheapest dest. */
612 for (i = mtx_ncols(mig); i--; )
613 if (dst_rem[i] > FTINY &&
614 (cur.d < 0 || price[i] < price[cur.d]))
615 cur.d = i;
616 if (cur.d < 0)
617 return(.0);
618 if ((cur.price = price[cur.d]) >= best.price)
619 continue; /* no point checking further */
620 cur.amt = (src_rem[cur.s] < dst_rem[cur.d]) ?
621 src_rem[cur.s] : dst_rem[cur.d];
622 if (cur.amt > maxamt) cur.amt = maxamt;
623 dst_rem[cur.d] -= cur.amt; /* add up differential costs */
624 for (i = mtx_nrows(mig); i--; ) {
625 if (i == cur.s) continue;
626 cost_others += min_cost(src_rem[i], dst_rem, price, mtx_ncols(mig))
627 - src_cost[i];
628 }
629 dst_rem[cur.d] += cur.amt; /* undo trial move */
630 cur.price += cost_others/cur.amt; /* adjust effective price */
631 if (cur.price < best.price) /* are we better than best? */
632 best = cur;
633 }
634 if ((best.s < 0) | (best.d < 0))
635 return(.0);
636 /* make the actual move */
637 mig->mtx[mtx_ndx(mig,best.s,best.d)] += best.amt;
638 src_rem[best.s] -= best.amt;
639 dst_rem[best.d] -= best.amt;
640 return(best.amt);
641 }
642
643 #ifdef DEBUG
644 static char *
645 thetaphi(const FVECT v)
646 {
647 static char buf[128];
648 double theta, phi;
649
650 theta = 180./M_PI*acos(v[2]);
651 phi = 180./M_PI*atan2(v[1],v[0]);
652 sprintf(buf, "(%.0f,%.0f)", theta, phi);
653
654 return(buf);
655 }
656 #endif
657
658 /* Create a new migration holder (sharing memory for multiprocessing) */
659 static MIGRATION *
660 new_migration(RBFNODE *from_rbf, RBFNODE *to_rbf)
661 {
662 size_t memlen = sizeof(MIGRATION) +
663 sizeof(float)*(from_rbf->nrbf*to_rbf->nrbf - 1);
664 MIGRATION *newmig;
665 #ifdef _WIN32
666 newmig = (MIGRATION *)malloc(memlen);
667 #else
668 if (nprocs <= 1) { /* single process? */
669 newmig = (MIGRATION *)malloc(memlen);
670 } else { /* else need to share memory */
671 newmig = (MIGRATION *)mmap(NULL, memlen, PROT_READ|PROT_WRITE,
672 MAP_ANON|MAP_SHARED, -1, 0);
673 if ((void *)newmig == MAP_FAILED)
674 newmig = NULL;
675 }
676 #endif
677 if (newmig == NULL) {
678 fprintf(stderr, "%s: cannot allocate new migration\n", progname);
679 exit(1);
680 }
681 newmig->rbfv[0] = from_rbf;
682 newmig->rbfv[1] = to_rbf;
683 /* insert in edge lists */
684 newmig->enxt[0] = from_rbf->ejl;
685 from_rbf->ejl = newmig;
686 newmig->enxt[1] = to_rbf->ejl;
687 to_rbf->ejl = newmig;
688 newmig->next = mig_list; /* push onto global list */
689 return(mig_list = newmig);
690 }
691
692 #ifdef _WIN32
693 #define await_children(n) (void)(n)
694 #define run_subprocess() 0
695 #define end_subprocess() (void)0
696 #else
697
698 /* Wait for the specified number of child processes to complete */
699 static void
700 await_children(int n)
701 {
702 int exit_status = 0;
703
704 if (n > nchild)
705 n = nchild;
706 while (n-- > 0) {
707 int status;
708 if (wait(&status) < 0) {
709 fprintf(stderr, "%s: missing child(ren)!\n", progname);
710 nchild = 0;
711 break;
712 }
713 --nchild;
714 if (status) { /* something wrong */
715 if ((status = WEXITSTATUS(status)))
716 exit_status = status;
717 else
718 exit_status += !exit_status;
719 fprintf(stderr, "%s: subprocess died\n", progname);
720 n = nchild; /* wait for the rest */
721 }
722 }
723 if (exit_status)
724 exit(exit_status);
725 }
726
727 /* Start child process if multiprocessing selected */
728 static pid_t
729 run_subprocess(void)
730 {
731 int status;
732 pid_t pid;
733
734 if (nprocs <= 1) /* any children requested? */
735 return(0);
736 await_children(nchild + 1 - nprocs); /* free up child process */
737 if ((pid = fork())) {
738 if (pid < 0) {
739 fprintf(stderr, "%s: cannot fork subprocess\n",
740 progname);
741 exit(1);
742 }
743 ++nchild; /* subprocess started */
744 return(pid);
745 }
746 nchild = -1;
747 return(0); /* put child to work */
748 }
749
750 /* If we are in subprocess, call exit */
751 #define end_subprocess() if (nchild < 0) _exit(0); else
752
753 #endif /* ! _WIN32 */
754
755 /* Compute and insert migration along directed edge (may fork child) */
756 static MIGRATION *
757 create_migration(RBFNODE *from_rbf, RBFNODE *to_rbf)
758 {
759 const double end_thresh = 0.1/(from_rbf->nrbf*to_rbf->nrbf);
760 const double rel_thresh = 0.0001;
761 float *pmtx;
762 MIGRATION *newmig;
763 double *src_rem, *dst_rem;
764 double total_rem = 1., move_amt;
765 int i;
766 /* check if exists already */
767 for (newmig = from_rbf->ejl; newmig != NULL;
768 newmig = nextedge(from_rbf,newmig))
769 if (newmig->rbfv[1] == to_rbf)
770 return(NULL);
771 /* else allocate */
772 newmig = new_migration(from_rbf, to_rbf);
773 if (run_subprocess())
774 return(newmig); /* child continues */
775 pmtx = price_routes(from_rbf, to_rbf);
776 src_rem = (double *)malloc(sizeof(double)*from_rbf->nrbf);
777 dst_rem = (double *)malloc(sizeof(double)*to_rbf->nrbf);
778 if ((src_rem == NULL) | (dst_rem == NULL)) {
779 fputs("Out of memory in create_migration()\n", stderr);
780 exit(1);
781 }
782 #ifdef DEBUG
783 fprintf(stderr, "Building path from (theta,phi) %s ",
784 thetaphi(from_rbf->invec));
785 fprintf(stderr, "to %s", thetaphi(to_rbf->invec));
786 /* if (nchild) */ fputc('\n', stderr);
787 #endif
788 /* starting quantities */
789 memset(newmig->mtx, 0, sizeof(float)*from_rbf->nrbf*to_rbf->nrbf);
790 for (i = from_rbf->nrbf; i--; )
791 src_rem[i] = rbf_volume(&from_rbf->rbfa[i]) / from_rbf->vtotal;
792 for (i = to_rbf->nrbf; i--; )
793 dst_rem[i] = rbf_volume(&to_rbf->rbfa[i]) / to_rbf->vtotal;
794 do { /* move a bit at a time */
795 move_amt = migration_step(newmig, src_rem, dst_rem, pmtx);
796 total_rem -= move_amt;
797 #ifdef DEBUG
798 if (!nchild)
799 /* fputc('.', stderr); */
800 fprintf(stderr, "%.9f remaining...\r", total_rem);
801 #endif
802 } while ((total_rem > end_thresh) & (move_amt > rel_thresh*total_rem));
803 #ifdef DEBUG
804 if (!nchild) fputs("\ndone.\n", stderr);
805 #endif
806
807 free(pmtx); /* free working arrays */
808 free(src_rem);
809 free(dst_rem);
810 for (i = from_rbf->nrbf; i--; ) { /* normalize final matrix */
811 float nf = rbf_volume(&from_rbf->rbfa[i]);
812 int j;
813 if (nf <= FTINY) continue;
814 nf = from_rbf->vtotal / nf;
815 for (j = to_rbf->nrbf; j--; )
816 newmig->mtx[mtx_ndx(newmig,i,j)] *= nf;
817 }
818 end_subprocess(); /* exit here if subprocess */
819 return(newmig);
820 }
821
822 /* Get triangle surface orientation (unnormalized) */
823 static void
824 tri_orient(FVECT vres, const FVECT v1, const FVECT v2, const FVECT v3)
825 {
826 FVECT v2minus1, v3minus2;
827
828 VSUB(v2minus1, v2, v1);
829 VSUB(v3minus2, v3, v2);
830 VCROSS(vres, v2minus1, v3minus2);
831 }
832
833 /* Determine if vertex order is reversed (inward normal) */
834 static int
835 is_rev_tri(const FVECT v1, const FVECT v2, const FVECT v3)
836 {
837 FVECT tor;
838
839 tri_orient(tor, v1, v2, v3);
840
841 return(DOT(tor, v2) < 0.);
842 }
843
844 /* Find vertices completing triangles on either side of the given edge */
845 static int
846 get_triangles(RBFNODE *rbfv[2], const MIGRATION *mig)
847 {
848 const MIGRATION *ej, *ej2;
849 RBFNODE *tv;
850
851 rbfv[0] = rbfv[1] = NULL;
852 if (mig == NULL)
853 return(0);
854 for (ej = mig->rbfv[0]->ejl; ej != NULL;
855 ej = nextedge(mig->rbfv[0],ej)) {
856 if (ej == mig)
857 continue;
858 tv = opp_rbf(mig->rbfv[0],ej);
859 for (ej2 = tv->ejl; ej2 != NULL; ej2 = nextedge(tv,ej2))
860 if (opp_rbf(tv,ej2) == mig->rbfv[1]) {
861 rbfv[is_rev_tri(mig->rbfv[0]->invec,
862 mig->rbfv[1]->invec,
863 tv->invec)] = tv;
864 break;
865 }
866 }
867 return((rbfv[0] != NULL) + (rbfv[1] != NULL));
868 }
869
870 /* Check if prospective vertex would create overlapping triangle */
871 static int
872 overlaps_tri(const RBFNODE *bv0, const RBFNODE *bv1, const RBFNODE *pv)
873 {
874 const MIGRATION *ej;
875 RBFNODE *vother[2];
876 int im_rev;
877 /* find shared edge in mesh */
878 for (ej = pv->ejl; ej != NULL; ej = nextedge(pv,ej)) {
879 const RBFNODE *tv = opp_rbf(pv,ej);
880 if (tv == bv0) {
881 im_rev = is_rev_tri(ej->rbfv[0]->invec,
882 ej->rbfv[1]->invec, bv1->invec);
883 break;
884 }
885 if (tv == bv1) {
886 im_rev = is_rev_tri(ej->rbfv[0]->invec,
887 ej->rbfv[1]->invec, bv0->invec);
888 break;
889 }
890 }
891 if (!get_triangles(vother, ej)) /* triangle on same side? */
892 return(0);
893 return(vother[im_rev] != NULL);
894 }
895
896 /* Find context hull vertex to complete triangle (oriented call) */
897 static RBFNODE *
898 find_chull_vert(const RBFNODE *rbf0, const RBFNODE *rbf1)
899 {
900 FVECT vmid, vejn, vp;
901 RBFNODE *rbf, *rbfbest = NULL;
902 double dprod, area2, bestarea2 = FHUGE, bestdprod = -.5;
903
904 VSUB(vejn, rbf1->invec, rbf0->invec);
905 VADD(vmid, rbf0->invec, rbf1->invec);
906 if (normalize(vejn) == 0 || normalize(vmid) == 0)
907 return(NULL);
908 /* XXX exhaustive search */
909 /* Find triangle with minimum rotation from perpendicular */
910 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
911 if ((rbf == rbf0) | (rbf == rbf1))
912 continue;
913 tri_orient(vp, rbf0->invec, rbf1->invec, rbf->invec);
914 if (DOT(vp, vmid) <= FTINY)
915 continue; /* wrong orientation */
916 area2 = .25*DOT(vp,vp);
917 VSUB(vp, rbf->invec, rbf0->invec);
918 dprod = -DOT(vp, vejn);
919 VSUM(vp, vp, vejn, dprod); /* above guarantees non-zero */
920 dprod = DOT(vp, vmid) / VLEN(vp);
921 if (dprod <= bestdprod + FTINY*(1 - 2*(area2 < bestarea2)))
922 continue; /* found better already */
923 if (overlaps_tri(rbf0, rbf1, rbf))
924 continue; /* overlaps another triangle */
925 rbfbest = rbf;
926 bestdprod = dprod; /* new one to beat */
927 bestarea2 = area2;
928 }
929 return(rbfbest);
930 }
931
932 /* Create new migration edge and grow mesh recursively around it */
933 static void
934 mesh_from_edge(MIGRATION *edge)
935 {
936 MIGRATION *ej0, *ej1;
937 RBFNODE *tvert[2];
938
939 if (edge == NULL)
940 return;
941 /* triangle on either side? */
942 get_triangles(tvert, edge);
943 if (tvert[0] == NULL) { /* grow mesh on right */
944 tvert[0] = find_chull_vert(edge->rbfv[0], edge->rbfv[1]);
945 if (tvert[0] != NULL) {
946 if (tvert[0] > edge->rbfv[0])
947 ej0 = create_migration(edge->rbfv[0], tvert[0]);
948 else
949 ej0 = create_migration(tvert[0], edge->rbfv[0]);
950 if (tvert[0] > edge->rbfv[1])
951 ej1 = create_migration(edge->rbfv[1], tvert[0]);
952 else
953 ej1 = create_migration(tvert[0], edge->rbfv[1]);
954 mesh_from_edge(ej0);
955 mesh_from_edge(ej1);
956 }
957 } else if (tvert[1] == NULL) { /* grow mesh on left */
958 tvert[1] = find_chull_vert(edge->rbfv[1], edge->rbfv[0]);
959 if (tvert[1] != NULL) {
960 if (tvert[1] > edge->rbfv[0])
961 ej0 = create_migration(edge->rbfv[0], tvert[1]);
962 else
963 ej0 = create_migration(tvert[1], edge->rbfv[0]);
964 if (tvert[1] > edge->rbfv[1])
965 ej1 = create_migration(edge->rbfv[1], tvert[1]);
966 else
967 ej1 = create_migration(tvert[1], edge->rbfv[1]);
968 mesh_from_edge(ej0);
969 mesh_from_edge(ej1);
970 }
971 }
972 }
973
974 #ifdef DEBUG
975 #include "random.h"
976 #include "bmpfile.h"
977 /* Hash pointer to byte value (must return 0 for NULL) */
978 static int
979 byte_hash(const void *p)
980 {
981 size_t h = (size_t)p;
982 h ^= (size_t)p >> 8;
983 h ^= (size_t)p >> 16;
984 h ^= (size_t)p >> 24;
985 return(h & 0xff);
986 }
987 /* Write out BMP image showing edges */
988 static void
989 write_edge_image(const char *fname)
990 {
991 BMPHeader *hdr = BMPmappedHeader(GRIDRES, GRIDRES, 0, 256);
992 BMPWriter *wtr;
993 int i, j;
994
995 fprintf(stderr, "Writing incident mesh drawing to '%s'\n", fname);
996 hdr->compr = BI_RLE8;
997 for (i = 256; --i; ) { /* assign random color map */
998 hdr->palette[i].r = random() & 0xff;
999 hdr->palette[i].g = random() & 0xff;
1000 hdr->palette[i].b = random() & 0xff;
1001 /* reject dark colors */
1002 i += (hdr->palette[i].r + hdr->palette[i].g +
1003 hdr->palette[i].b < 128);
1004 }
1005 hdr->palette[0].r = hdr->palette[0].g = hdr->palette[0].b = 0;
1006 /* open output */
1007 wtr = BMPopenOutputFile(fname, hdr);
1008 if (wtr == NULL) {
1009 free(hdr);
1010 return;
1011 }
1012 for (i = 0; i < GRIDRES; i++) { /* write scanlines */
1013 for (j = 0; j < GRIDRES; j++)
1014 wtr->scanline[j] = byte_hash(mig_grid[i][j]);
1015 if (BMPwriteScanline(wtr) != BIR_OK)
1016 break;
1017 }
1018 BMPcloseOutput(wtr); /* close & clean up */
1019 }
1020 #endif
1021
1022 /* Draw edge list into mig_grid array */
1023 static void
1024 draw_edges()
1025 {
1026 int nnull = 0, ntot = 0;
1027 MIGRATION *ej;
1028 int p0[2], p1[2];
1029
1030 /* memset(mig_grid, 0, sizeof(mig_grid)); */
1031 for (ej = mig_list; ej != NULL; ej = ej->next) {
1032 ++ntot;
1033 pos_from_vec(p0, ej->rbfv[0]->invec);
1034 pos_from_vec(p1, ej->rbfv[1]->invec);
1035 if ((p0[0] == p1[0]) & (p0[1] == p1[1])) {
1036 ++nnull;
1037 mig_grid[p0[0]][p0[1]] = ej;
1038 continue;
1039 }
1040 if (abs(p1[0]-p0[0]) > abs(p1[1]-p0[1])) {
1041 const int xstep = 2*(p1[0] > p0[0]) - 1;
1042 const double ystep = (double)((p1[1]-p0[1])*xstep) /
1043 (double)(p1[0]-p0[0]);
1044 int x;
1045 double y;
1046 for (x = p0[0], y = p0[1]+.5; x != p1[0];
1047 x += xstep, y += ystep)
1048 mig_grid[x][(int)y] = ej;
1049 mig_grid[x][(int)y] = ej;
1050 } else {
1051 const int ystep = 2*(p1[1] > p0[1]) - 1;
1052 const double xstep = (double)((p1[0]-p0[0])*ystep) /
1053 (double)(p1[1]-p0[1]);
1054 int y;
1055 double x;
1056 for (y = p0[1], x = p0[0]+.5; y != p1[1];
1057 y += ystep, x += xstep)
1058 mig_grid[(int)x][y] = ej;
1059 mig_grid[(int)x][y] = ej;
1060 }
1061 }
1062 if (nnull)
1063 fprintf(stderr, "Warning: %d of %d edges are null\n",
1064 nnull, ntot);
1065 #ifdef DEBUG
1066 write_edge_image("bsdf_edges.bmp");
1067 #endif
1068 }
1069
1070 /* Build our triangle mesh from recorded RBFs */
1071 static void
1072 build_mesh()
1073 {
1074 double best2 = M_PI*M_PI;
1075 RBFNODE *shrt_edj[2];
1076 RBFNODE *rbf0, *rbf1;
1077 /* check if isotropic */
1078 if (single_plane_incident) {
1079 for (rbf0 = dsf_list; rbf0 != NULL; rbf0 = rbf0->next)
1080 if (rbf0->next != NULL)
1081 create_migration(rbf0, rbf0->next);
1082 await_children(nchild);
1083 return;
1084 }
1085 /* start w/ shortest edge */
1086 shrt_edj[0] = shrt_edj[1] = NULL;
1087 for (rbf0 = dsf_list; rbf0 != NULL; rbf0 = rbf0->next)
1088 for (rbf1 = rbf0->next; rbf1 != NULL; rbf1 = rbf1->next) {
1089 double dist2 = 2. - 2.*DOT(rbf0->invec,rbf1->invec);
1090 if (dist2 < best2) {
1091 shrt_edj[0] = rbf0;
1092 shrt_edj[1] = rbf1;
1093 best2 = dist2;
1094 }
1095 }
1096 if (shrt_edj[0] == NULL) {
1097 fputs("Cannot find shortest edge\n", stderr);
1098 exit(1);
1099 }
1100 /* build mesh from this edge */
1101 if (shrt_edj[0] < shrt_edj[1])
1102 mesh_from_edge(create_migration(shrt_edj[0], shrt_edj[1]));
1103 else
1104 mesh_from_edge(create_migration(shrt_edj[1], shrt_edj[0]));
1105 /* complete migrations */
1106 await_children(nchild);
1107 /* draw edge list into grid */
1108 draw_edges();
1109 }
1110
1111 /* Identify enclosing triangle for this position (flood fill raster check) */
1112 static int
1113 identify_tri(MIGRATION *miga[3], unsigned char vmap[GRIDRES][(GRIDRES+7)/8],
1114 int px, int py)
1115 {
1116 const int btest = 1<<(py&07);
1117
1118 if (vmap[px][py>>3] & btest) /* already visited here? */
1119 return(1);
1120 /* else mark it */
1121 vmap[px][py>>3] |= btest;
1122
1123 if (mig_grid[px][py] != NULL) { /* are we on an edge? */
1124 int i;
1125 for (i = 0; i < 3; i++) {
1126 if (miga[i] == mig_grid[px][py])
1127 return(1);
1128 if (miga[i] != NULL)
1129 continue;
1130 miga[i] = mig_grid[px][py];
1131 return(1);
1132 }
1133 return(0); /* outside triangle! */
1134 }
1135 /* check neighbors (flood) */
1136 if (px > 0 && !identify_tri(miga, vmap, px-1, py))
1137 return(0);
1138 if (px < GRIDRES-1 && !identify_tri(miga, vmap, px+1, py))
1139 return(0);
1140 if (py > 0 && !identify_tri(miga, vmap, px, py-1))
1141 return(0);
1142 if (py < GRIDRES-1 && !identify_tri(miga, vmap, px, py+1))
1143 return(0);
1144 return(1); /* this neighborhood done */
1145 }
1146
1147 /* Insert vertex in ordered list */
1148 static void
1149 insert_vert(RBFNODE **vlist, RBFNODE *v)
1150 {
1151 int i, j;
1152
1153 for (i = 0; vlist[i] != NULL; i++) {
1154 if (v == vlist[i])
1155 return;
1156 if (v < vlist[i])
1157 break;
1158 }
1159 for (j = i; vlist[j] != NULL; j++)
1160 ;
1161 while (j > i) {
1162 vlist[j] = vlist[j-1];
1163 --j;
1164 }
1165 vlist[i] = v;
1166 }
1167
1168 /* Sort triangle edges in standard order */
1169 static int
1170 order_triangle(MIGRATION *miga[3])
1171 {
1172 RBFNODE *vert[7];
1173 MIGRATION *ord[3];
1174 int i;
1175 /* order vertices, first */
1176 memset(vert, 0, sizeof(vert));
1177 for (i = 3; i--; ) {
1178 if (miga[i] == NULL)
1179 return(0);
1180 insert_vert(vert, miga[i]->rbfv[0]);
1181 insert_vert(vert, miga[i]->rbfv[1]);
1182 }
1183 /* should be just 3 vertices */
1184 if ((vert[3] == NULL) | (vert[4] != NULL))
1185 return(0);
1186 /* identify edge 0 */
1187 for (i = 3; i--; )
1188 if (miga[i]->rbfv[0] == vert[0] &&
1189 miga[i]->rbfv[1] == vert[1]) {
1190 ord[0] = miga[i];
1191 break;
1192 }
1193 if (i < 0)
1194 return(0);
1195 /* identify edge 1 */
1196 for (i = 3; i--; )
1197 if (miga[i]->rbfv[0] == vert[1] &&
1198 miga[i]->rbfv[1] == vert[2]) {
1199 ord[1] = miga[i];
1200 break;
1201 }
1202 if (i < 0)
1203 return(0);
1204 /* identify edge 2 */
1205 for (i = 3; i--; )
1206 if (miga[i]->rbfv[0] == vert[0] &&
1207 miga[i]->rbfv[1] == vert[2]) {
1208 ord[2] = miga[i];
1209 break;
1210 }
1211 if (i < 0)
1212 return(0);
1213 /* reassign order */
1214 miga[0] = ord[0]; miga[1] = ord[1]; miga[2] = ord[2];
1215 return(1);
1216 }
1217
1218 /* Find edge(s) for interpolating the given incident vector */
1219 static int
1220 get_interp(MIGRATION *miga[3], const FVECT invec)
1221 {
1222 miga[0] = miga[1] = miga[2] = NULL;
1223 if (single_plane_incident) { /* isotropic BSDF? */
1224 RBFNODE *rbf; /* find edge we're on */
1225 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
1226 if (input_orient*rbf->invec[2] < input_orient*invec[2])
1227 break;
1228 if (rbf->next != NULL &&
1229 input_orient*rbf->next->invec[2] <
1230 input_orient*invec[2]) {
1231 for (miga[0] = rbf->ejl; miga[0] != NULL;
1232 miga[0] = nextedge(rbf,miga[0]))
1233 if (opp_rbf(rbf,miga[0]) == rbf->next)
1234 return(1);
1235 break;
1236 }
1237 }
1238 return(0); /* outside range! */
1239 }
1240 { /* else use triangle mesh */
1241 unsigned char floodmap[GRIDRES][(GRIDRES+7)/8];
1242 int pstart[2];
1243 RBFNODE *vother;
1244 MIGRATION *ej;
1245 int i;
1246
1247 pos_from_vec(pstart, invec);
1248 memset(floodmap, 0, sizeof(floodmap));
1249 /* call flooding function */
1250 if (!identify_tri(miga, floodmap, pstart[0], pstart[1]))
1251 return(0); /* outside mesh */
1252 if ((miga[0] == NULL) | (miga[2] == NULL))
1253 return(0); /* should never happen */
1254 if (miga[1] == NULL)
1255 return(1); /* on edge */
1256 /* verify triangle */
1257 if (!order_triangle(miga)) {
1258 #ifdef DEBUG
1259 fputs("Munged triangle in get_interp()\n", stderr);
1260 #endif
1261 vother = NULL; /* find triangle from edge */
1262 for (i = 3; i--; ) {
1263 RBFNODE *tpair[2];
1264 if (get_triangles(tpair, miga[i]) &&
1265 (vother = tpair[ is_rev_tri(
1266 miga[i]->rbfv[0]->invec,
1267 miga[i]->rbfv[1]->invec,
1268 invec) ]) != NULL)
1269 break;
1270 }
1271 if (vother == NULL) { /* couldn't find 3rd vertex */
1272 #ifdef DEBUG
1273 fputs("No triangle in get_interp()\n", stderr);
1274 #endif
1275 return(0);
1276 }
1277 /* reassign other two edges */
1278 for (ej = vother->ejl; ej != NULL;
1279 ej = nextedge(vother,ej)) {
1280 RBFNODE *vorig = opp_rbf(vother,ej);
1281 if (vorig == miga[i]->rbfv[0])
1282 miga[(i+1)%3] = ej;
1283 else if (vorig == miga[i]->rbfv[1])
1284 miga[(i+2)%3] = ej;
1285 }
1286 if (!order_triangle(miga)) {
1287 #ifdef DEBUG
1288 fputs("Bad triangle in get_interp()\n", stderr);
1289 #endif
1290 return(0);
1291 }
1292 }
1293 return(3); /* return in standard order */
1294 }
1295 }
1296
1297 /* Advect and allocate new RBF along edge */
1298 static RBFNODE *
1299 e_advect_rbf(const MIGRATION *mig, const FVECT invec)
1300 {
1301 RBFNODE *rbf;
1302 int n, i, j;
1303 double t, full_dist;
1304 /* get relative position */
1305 t = acos(DOT(invec, mig->rbfv[0]->invec));
1306 if (t < M_PI/GRIDRES) { /* near first DSF */
1307 n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[0]->nrbf-1);
1308 rbf = (RBFNODE *)malloc(n);
1309 if (rbf == NULL)
1310 goto memerr;
1311 memcpy(rbf, mig->rbfv[0], n); /* just duplicate */
1312 return(rbf);
1313 }
1314 full_dist = acos(DOT(mig->rbfv[0]->invec, mig->rbfv[1]->invec));
1315 if (t > full_dist-M_PI/GRIDRES) { /* near second DSF */
1316 n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[1]->nrbf-1);
1317 rbf = (RBFNODE *)malloc(n);
1318 if (rbf == NULL)
1319 goto memerr;
1320 memcpy(rbf, mig->rbfv[1], n); /* just duplicate */
1321 return(rbf);
1322 }
1323 t /= full_dist;
1324 n = 0; /* count migrating particles */
1325 for (i = 0; i < mtx_nrows(mig); i++)
1326 for (j = 0; j < mtx_ncols(mig); j++)
1327 n += (mig->mtx[mtx_ndx(mig,i,j)] > FTINY);
1328 #ifdef DEBUG
1329 fprintf(stderr, "Input RBFs have %d, %d nodes -> output has %d\n",
1330 mig->rbfv[0]->nrbf, mig->rbfv[1]->nrbf, n);
1331 #endif
1332 rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
1333 if (rbf == NULL)
1334 goto memerr;
1335 rbf->next = NULL; rbf->ejl = NULL;
1336 VCOPY(rbf->invec, invec);
1337 rbf->nrbf = n;
1338 rbf->vtotal = 1.-t + t*mig->rbfv[1]->vtotal/mig->rbfv[0]->vtotal;
1339 n = 0; /* advect RBF lobes */
1340 for (i = 0; i < mtx_nrows(mig); i++) {
1341 const RBFVAL *rbf0i = &mig->rbfv[0]->rbfa[i];
1342 const float peak0 = rbf0i->peak;
1343 const double rad0 = R2ANG(rbf0i->crad);
1344 FVECT v0;
1345 float mv;
1346 ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
1347 for (j = 0; j < mtx_ncols(mig); j++)
1348 if ((mv = mig->mtx[mtx_ndx(mig,i,j)]) > FTINY) {
1349 const RBFVAL *rbf1j = &mig->rbfv[1]->rbfa[j];
1350 double rad1 = R2ANG(rbf1j->crad);
1351 FVECT v;
1352 int pos[2];
1353 rbf->rbfa[n].peak = peak0 * mv * rbf->vtotal;
1354 rbf->rbfa[n].crad = ANG2R(sqrt(rad0*rad0*(1.-t) +
1355 rad1*rad1*t));
1356 ovec_from_pos(v, rbf1j->gx, rbf1j->gy);
1357 geodesic(v, v0, v, t, GEOD_REL);
1358 pos_from_vec(pos, v);
1359 rbf->rbfa[n].gx = pos[0];
1360 rbf->rbfa[n].gy = pos[1];
1361 ++n;
1362 }
1363 }
1364 rbf->vtotal *= mig->rbfv[0]->vtotal; /* turn ratio into actual */
1365 return(rbf);
1366 memerr:
1367 fputs("Out of memory in e_advect_rbf()\n", stderr);
1368 exit(1);
1369 return(NULL); /* pro forma return */
1370 }
1371
1372 /* Partially advect between recorded incident angles and allocate new RBF */
1373 static RBFNODE *
1374 advect_rbf(const FVECT invec)
1375 {
1376 MIGRATION *miga[3];
1377 RBFNODE *rbf;
1378 float mbfact, mcfact;
1379 int n, i, j, k;
1380 FVECT v0, v1, v2;
1381 double s, t;
1382
1383 if (!get_interp(miga, invec)) /* can't interpolate? */
1384 return(NULL);
1385 if (miga[1] == NULL) /* advect along edge? */
1386 return(e_advect_rbf(miga[0], invec));
1387 #ifdef DEBUG
1388 if (miga[0]->rbfv[0] != miga[2]->rbfv[0] |
1389 miga[0]->rbfv[1] != miga[1]->rbfv[0] |
1390 miga[1]->rbfv[1] != miga[2]->rbfv[1]) {
1391 fputs("Triangle vertex screw-up!\n", stderr);
1392 exit(1);
1393 }
1394 #endif
1395 /* figure out position */
1396 fcross(v0, miga[2]->rbfv[0]->invec, miga[2]->rbfv[1]->invec);
1397 normalize(v0);
1398 fcross(v2, miga[1]->rbfv[0]->invec, miga[1]->rbfv[1]->invec);
1399 normalize(v2);
1400 fcross(v1, invec, miga[1]->rbfv[1]->invec);
1401 normalize(v1);
1402 s = acos(DOT(v0,v1)) / acos(DOT(v0,v2));
1403 geodesic(v1, miga[0]->rbfv[0]->invec, miga[0]->rbfv[1]->invec,
1404 s, GEOD_REL);
1405 t = acos(DOT(v1,invec)) / acos(DOT(v1,miga[1]->rbfv[1]->invec));
1406 n = 0; /* count migrating particles */
1407 for (i = 0; i < mtx_nrows(miga[0]); i++)
1408 for (j = 0; j < mtx_ncols(miga[0]); j++)
1409 for (k = (miga[0]->mtx[mtx_ndx(miga[0],i,j)] > FTINY) *
1410 mtx_ncols(miga[2]); k--; )
1411 n += (miga[2]->mtx[mtx_ndx(miga[2],i,k)] > FTINY &&
1412 miga[1]->mtx[mtx_ndx(miga[1],j,k)] > FTINY);
1413 #ifdef DEBUG
1414 fprintf(stderr, "Input RBFs have %d, %d, %d nodes -> output has %d\n",
1415 miga[0]->rbfv[0]->nrbf, miga[0]->rbfv[1]->nrbf,
1416 miga[2]->rbfv[1]->nrbf, n);
1417 #endif
1418 rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
1419 if (rbf == NULL) {
1420 fputs("Out of memory in advect_rbf()\n", stderr);
1421 exit(1);
1422 }
1423 rbf->next = NULL; rbf->ejl = NULL;
1424 VCOPY(rbf->invec, invec);
1425 rbf->nrbf = n;
1426 n = 0; /* compute RBF lobes */
1427 mbfact = s * miga[0]->rbfv[1]->vtotal/miga[0]->rbfv[0]->vtotal *
1428 (1.-t + t*miga[1]->rbfv[1]->vtotal/miga[1]->rbfv[0]->vtotal);
1429 mcfact = (1.-s) *
1430 (1.-t + t*miga[2]->rbfv[1]->vtotal/miga[2]->rbfv[0]->vtotal);
1431 for (i = 0; i < mtx_nrows(miga[0]); i++) {
1432 const RBFVAL *rbf0i = &miga[0]->rbfv[0]->rbfa[i];
1433 const float w0i = rbf0i->peak;
1434 const double rad0i = R2ANG(rbf0i->crad);
1435 ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
1436 for (j = 0; j < mtx_ncols(miga[0]); j++) {
1437 const float ma = miga[0]->mtx[mtx_ndx(miga[0],i,j)];
1438 const RBFVAL *rbf1j;
1439 double rad1j, srad2;
1440 if (ma <= FTINY)
1441 continue;
1442 rbf1j = &miga[0]->rbfv[1]->rbfa[j];
1443 rad1j = R2ANG(rbf1j->crad);
1444 srad2 = (1.-s)*(1.-t)*rad0i*rad0i + s*(1.-t)*rad1j*rad1j;
1445 ovec_from_pos(v1, rbf1j->gx, rbf1j->gy);
1446 geodesic(v1, v0, v1, s, GEOD_REL);
1447 for (k = 0; k < mtx_ncols(miga[2]); k++) {
1448 float mb = miga[1]->mtx[mtx_ndx(miga[1],j,k)];
1449 float mc = miga[2]->mtx[mtx_ndx(miga[2],i,k)];
1450 const RBFVAL *rbf2k;
1451 double rad2k;
1452 FVECT vout;
1453 int pos[2];
1454 if ((mb <= FTINY) | (mc <= FTINY))
1455 continue;
1456 rbf2k = &miga[2]->rbfv[1]->rbfa[k];
1457 rbf->rbfa[n].peak = w0i * ma * (mb*mbfact + mc*mcfact);
1458 rad2k = R2ANG(rbf2k->crad);
1459 rbf->rbfa[n].crad = ANG2R(sqrt(srad2 + t*rad2k*rad2k));
1460 ovec_from_pos(v2, rbf2k->gx, rbf2k->gy);
1461 geodesic(vout, v1, v2, t, GEOD_REL);
1462 pos_from_vec(pos, vout);
1463 rbf->rbfa[n].gx = pos[0];
1464 rbf->rbfa[n].gy = pos[1];
1465 ++n;
1466 }
1467 }
1468 }
1469 rbf->vtotal = miga[0]->rbfv[0]->vtotal * (mbfact + mcfact);
1470 return(rbf);
1471 }
1472
1473 /* Interpolate and output isotropic BSDF data */
1474 static void
1475 interp_isotropic()
1476 {
1477 const int sqres = 1<<samp_order;
1478 FILE *ofp = NULL;
1479 char cmd[128];
1480 int ix, ox, oy;
1481 FVECT ivec, ovec;
1482 double bsdf;
1483 #if DEBUG
1484 fprintf(stderr, "Writing isotropic order %d ", samp_order);
1485 if (pctcull >= 0) fprintf(stderr, "data with %d%% culling\n", pctcull);
1486 else fputs("raw data\n", stderr);
1487 #endif
1488 if (pctcull >= 0) { /* begin output */
1489 sprintf(cmd, "rttree_reduce -h -a -fd -r 3 -t %d -g %d",
1490 pctcull, samp_order);
1491 fflush(stdout);
1492 ofp = popen(cmd, "w");
1493 if (ofp == NULL) {
1494 fprintf(stderr, "%s: cannot create pipe to rttree_reduce\n",
1495 progname);
1496 exit(1);
1497 }
1498 } else
1499 fputs("{\n", stdout);
1500 /* run through directions */
1501 for (ix = 0; ix < sqres/2; ix++) {
1502 RBFNODE *rbf;
1503 SDsquare2disk(ivec, (ix+.5)/sqres, .5);
1504 ivec[2] = input_orient *
1505 sqrt(1. - ivec[0]*ivec[0] - ivec[1]*ivec[1]);
1506 rbf = advect_rbf(ivec);
1507 for (ox = 0; ox < sqres; ox++)
1508 for (oy = 0; oy < sqres; oy++) {
1509 SDsquare2disk(ovec, (ox+.5)/sqres, (oy+.5)/sqres);
1510 ovec[2] = output_orient *
1511 sqrt(1. - ovec[0]*ovec[0] - ovec[1]*ovec[1]);
1512 bsdf = eval_rbfrep(rbf, ovec) / fabs(ovec[2]);
1513 if (pctcull >= 0)
1514 fwrite(&bsdf, sizeof(bsdf), 1, ofp);
1515 else
1516 printf("\t%.3e\n", bsdf);
1517 }
1518 free(rbf);
1519 }
1520 if (pctcull >= 0) { /* finish output */
1521 if (pclose(ofp)) {
1522 fprintf(stderr, "%s: error running '%s'\n",
1523 progname, cmd);
1524 exit(1);
1525 }
1526 } else {
1527 for (ix = sqres*sqres*sqres/2; ix--; )
1528 fputs("\t0\n", stdout);
1529 fputs("}\n", stdout);
1530 }
1531 }
1532
1533 /* Interpolate and output anisotropic BSDF data */
1534 static void
1535 interp_anisotropic()
1536 {
1537 const int sqres = 1<<samp_order;
1538 FILE *ofp = NULL;
1539 char cmd[128];
1540 int ix, iy, ox, oy;
1541 FVECT ivec, ovec;
1542 double bsdf;
1543 #if DEBUG
1544 fprintf(stderr, "Writing anisotropic order %d ", samp_order);
1545 if (pctcull >= 0) fprintf(stderr, "data with %d%% culling\n", pctcull);
1546 else fputs("raw data\n", stderr);
1547 #endif
1548 if (pctcull >= 0) { /* begin output */
1549 sprintf(cmd, "rttree_reduce -h -a -fd -r 4 -t %d -g %d",
1550 pctcull, samp_order);
1551 fflush(stdout);
1552 ofp = popen(cmd, "w");
1553 if (ofp == NULL) {
1554 fprintf(stderr, "%s: cannot create pipe to rttree_reduce\n",
1555 progname);
1556 exit(1);
1557 }
1558 } else
1559 fputs("{\n", stdout);
1560 /* run through directions */
1561 for (ix = 0; ix < sqres; ix++)
1562 for (iy = 0; iy < sqres; iy++) {
1563 RBFNODE *rbf;
1564 SDsquare2disk(ivec, (ix+.5)/sqres, (iy+.5)/sqres);
1565 ivec[2] = input_orient *
1566 sqrt(1. - ivec[0]*ivec[0] - ivec[1]*ivec[1]);
1567 rbf = advect_rbf(ivec);
1568 for (ox = 0; ox < sqres; ox++)
1569 for (oy = 0; oy < sqres; oy++) {
1570 SDsquare2disk(ovec, (ox+.5)/sqres, (oy+.5)/sqres);
1571 ovec[2] = output_orient *
1572 sqrt(1. - ovec[0]*ovec[0] - ovec[1]*ovec[1]);
1573 bsdf = eval_rbfrep(rbf, ovec) / fabs(ovec[2]);
1574 if (pctcull >= 0)
1575 fwrite(&bsdf, sizeof(bsdf), 1, ofp);
1576 else
1577 printf("\t%.3e\n", bsdf);
1578 }
1579 free(rbf);
1580 }
1581 if (pctcull >= 0) { /* finish output */
1582 if (pclose(ofp)) {
1583 fprintf(stderr, "%s: error running '%s'\n",
1584 progname, cmd);
1585 exit(1);
1586 }
1587 } else
1588 fputs("}\n", stdout);
1589 }
1590
1591 #if 1
1592 /* Read in BSDF files and interpolate as tensor tree representation */
1593 int
1594 main(int argc, char *argv[])
1595 {
1596 RBFNODE *rbf;
1597 double bsdf;
1598 int i;
1599
1600 progname = argv[0]; /* get options */
1601 while (argc > 2 && argv[1][0] == '-') {
1602 switch (argv[1][1]) {
1603 case 'n':
1604 nprocs = atoi(argv[2]);
1605 break;
1606 case 't':
1607 pctcull = atoi(argv[2]);
1608 break;
1609 default:
1610 goto userr;
1611 }
1612 argv += 2; argc -= 2;
1613 }
1614 if (argc < 3)
1615 goto userr;
1616 #ifdef _WIN32
1617 if (nprocs > 1) {
1618 fprintf(stderr, "%s: multiprocessing not supported\n",
1619 progname);
1620 return(1);
1621 }
1622 #endif
1623 for (i = 1; i < argc; i++) { /* compile measurements */
1624 if (!load_pabopto_meas(argv[i]))
1625 return(1);
1626 compute_radii();
1627 cull_values();
1628 make_rbfrep();
1629 }
1630 build_mesh(); /* create interpolation */
1631 /* xml_prologue(); /* start XML output */
1632 if (single_plane_incident) /* resample dist. */
1633 interp_isotropic();
1634 else
1635 interp_anisotropic();
1636 /* xml_epilogue(); /* finish XML output */
1637 return(0);
1638 userr:
1639 fprintf(stderr,
1640 "Usage: %s [-n nprocs][-t pctcull] meas1.dat meas2.dat .. > bsdf.xml\n",
1641 progname);
1642 return(1);
1643 }
1644 #else
1645 /* Test main produces a Radiance model from the given input file */
1646 int
1647 main(int argc, char *argv[])
1648 {
1649 char buf[128];
1650 FILE *pfp;
1651 double bsdf;
1652 FVECT dir;
1653 int i, j, n;
1654
1655 if (argc != 2) {
1656 fprintf(stderr, "Usage: %s input.dat > output.rad\n", argv[0]);
1657 return(1);
1658 }
1659 if (!load_pabopto_meas(argv[1]))
1660 return(1);
1661
1662 compute_radii();
1663 cull_values();
1664 make_rbfrep();
1665 /* produce spheres at meas. */
1666 puts("void plastic yellow\n0\n0\n5 .6 .4 .01 .04 .08\n");
1667 puts("void plastic pink\n0\n0\n5 .5 .05 .9 .04 .08\n");
1668 n = 0;
1669 for (i = 0; i < GRIDRES; i++)
1670 for (j = 0; j < GRIDRES; j++)
1671 if (dsf_grid[i][j].vsum > .0f) {
1672 ovec_from_pos(dir, i, j);
1673 bsdf = dsf_grid[i][j].vsum / dir[2];
1674 if (dsf_grid[i][j].nval) {
1675 printf("pink cone c%04d\n0\n0\n8\n", ++n);
1676 printf("\t%.6g %.6g %.6g\n",
1677 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1678 printf("\t%.6g %.6g %.6g\n",
1679 dir[0]*(bsdf+.005), dir[1]*(bsdf+.005),
1680 dir[2]*(bsdf+.005));
1681 puts("\t.003\t0\n");
1682 } else {
1683 ovec_from_pos(dir, i, j);
1684 printf("yellow sphere s%04d\n0\n0\n", ++n);
1685 printf("4 %.6g %.6g %.6g .0015\n\n",
1686 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1687 }
1688 }
1689 /* output continuous surface */
1690 puts("void trans tgreen\n0\n0\n7 .7 1 .7 .04 .04 .9 .9\n");
1691 fflush(stdout);
1692 sprintf(buf, "gensurf tgreen bsdf - - - %d %d", GRIDRES-1, GRIDRES-1);
1693 pfp = popen(buf, "w");
1694 if (pfp == NULL) {
1695 fputs(buf, stderr);
1696 fputs(": cannot start command\n", stderr);
1697 return(1);
1698 }
1699 for (i = 0; i < GRIDRES; i++)
1700 for (j = 0; j < GRIDRES; j++) {
1701 ovec_from_pos(dir, i, j);
1702 bsdf = eval_rbfrep(dsf_list, dir) / dir[2];
1703 fprintf(pfp, "%.8e %.8e %.8e\n",
1704 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
1705 }
1706 return(pclose(pfp)==0 ? 0 : 1);
1707 }
1708 #endif