ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/pabopto2xml.c
Revision: 2.7
Committed: Sun Sep 2 15:33:15 2012 UTC (11 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.6: +21 -4 lines
Log Message:
Fixes to reciprocity for tensor tree representation

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.7 static const char RCSid[] = "$Id: pabopto2xml.c,v 2.6 2012/08/26 19:40:02 greg Exp $";
3 greg 2.1 #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     #define _USE_MATH_DEFINES
12     #include <stdio.h>
13     #include <stdlib.h>
14     #include <string.h>
15     #include <ctype.h>
16     #include <math.h>
17     #include "bsdf.h"
18    
19     #ifndef GRIDRES
20     #define GRIDRES 200 /* max. grid resolution per side */
21     #endif
22    
23 greg 2.3 #define RSCA 2.7 /* radius scaling factor (empirical) */
24 greg 2.2
25 greg 2.6 /* convert to/from coded radians */
26     #define ANG2R(r) (int)((r)*((1<<16)/M_PI))
27 greg 2.2 #define R2ANG(c) (((c)+.5)*(M_PI/(1<<16)))
28 greg 2.1
29     typedef struct {
30 greg 2.5 float vsum; /* DSF sum */
31 greg 2.1 unsigned short nval; /* number of values in sum */
32 greg 2.2 unsigned short crad; /* radius (coded angle) */
33 greg 2.1 } GRIDVAL; /* grid value */
34    
35     typedef struct {
36 greg 2.5 float peak; /* lobe value at peak */
37 greg 2.2 unsigned short crad; /* radius (coded angle) */
38 greg 2.1 unsigned char gx, gy; /* grid position */
39     } RBFVAL; /* radial basis function value */
40    
41 greg 2.7 struct s_rbfnode; /* forward declaration of RBF struct */
42    
43     typedef struct s_migration {
44     struct s_migration *next; /* next in global edge list */
45     struct s_rbfnode *rbfv[2]; /* from,to vertex */
46     struct s_migration *enxt[2]; /* next from,to sibling */
47     float mtx[1]; /* matrix (extends struct) */
48     } MIGRATION; /* migration link (winged edge structure) */
49    
50     typedef struct s_rbfnode {
51     struct s_rbfnode *next; /* next in global RBF list */
52     MIGRATION *ejl; /* edge list for this vertex */
53 greg 2.1 FVECT invec; /* incident vector direction */
54     int nrbf; /* number of RBFs */
55     RBFVAL rbfa[1]; /* RBF array (extends struct) */
56 greg 2.5 } RBFLIST; /* RBF representation of DSF @ 1 incidence */
57 greg 2.1
58     /* our loaded grid for this incident angle */
59     static double theta_in_deg, phi_in_deg;
60 greg 2.5 static GRIDVAL dsf_grid[GRIDRES][GRIDRES];
61 greg 2.1
62 greg 2.5 /* processed incident DSF measurements */
63 greg 2.7 static RBFLIST *dsf_list = NULL;
64    
65     /* edge (linking) matrices */
66     static MIGRATION *mig_list = NULL;
67    
68     #define mtxval(m,i,j) (m)->mtx[(i)*(m)->rbfv[1]->nrbf+(j)]
69     #define nextedge(rbf,m) (m)->enxt[(rbf)==(m)->rbfv[1]]
70 greg 2.1
71 greg 2.3 /* Compute outgoing vector from grid position */
72     static void
73     vec_from_pos(FVECT vec, int xpos, int ypos)
74 greg 2.1 {
75 greg 2.3 double uv[2];
76     double r2;
77    
78     SDsquare2disk(uv, (1./GRIDRES)*(xpos+.5), (1./GRIDRES)*(ypos+.5));
79     /* uniform hemispherical projection */
80     r2 = uv[0]*uv[0] + uv[1]*uv[1];
81     vec[0] = vec[1] = sqrt(2. - r2);
82     vec[0] *= uv[0];
83     vec[1] *= uv[1];
84     vec[2] = 1. - r2;
85 greg 2.1 }
86    
87     /* Compute grid position from normalized outgoing vector */
88     static void
89     pos_from_vec(int pos[2], const FVECT vec)
90     {
91     double sq[2]; /* uniform hemispherical projection */
92     double norm = 1./sqrt(1. + vec[2]);
93    
94     SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
95    
96     pos[0] = (int)(sq[0]*GRIDRES);
97     pos[1] = (int)(sq[1]*GRIDRES);
98     }
99    
100 greg 2.5 /* Evaluate RBF for DSF at the given normalized outgoing direction */
101 greg 2.1 static double
102     eval_rbfrep(const RBFLIST *rp, const FVECT outvec)
103     {
104     double res = .0;
105     const RBFVAL *rbfp;
106     FVECT odir;
107     double sig2;
108     int n;
109    
110     rbfp = rp->rbfa;
111     for (n = rp->nrbf; n--; rbfp++) {
112     vec_from_pos(odir, rbfp->gx, rbfp->gy);
113 greg 2.2 sig2 = R2ANG(rbfp->crad);
114     sig2 = (DOT(odir,outvec) - 1.) / (sig2*sig2);
115 greg 2.1 if (sig2 > -19.)
116 greg 2.5 res += rbfp->peak * exp(sig2);
117 greg 2.1 }
118     return(res);
119     }
120    
121 greg 2.3 /* Count up filled nodes and build RBF representation from current grid */
122     static RBFLIST *
123     make_rbfrep(void)
124     {
125 greg 2.6 int niter = 16;
126     double lastVar, thisVar = 100.;
127 greg 2.3 int nn;
128     RBFLIST *newnode;
129     int i, j;
130    
131     nn = 0; /* count selected bins */
132     for (i = 0; i < GRIDRES; i++)
133     for (j = 0; j < GRIDRES; j++)
134 greg 2.6 nn += dsf_grid[i][j].nval;
135 greg 2.3 /* allocate RBF array */
136     newnode = (RBFLIST *)malloc(sizeof(RBFLIST) + sizeof(RBFVAL)*(nn-1));
137     if (newnode == NULL) {
138     fputs("Out of memory in make_rbfrep\n", stderr);
139     exit(1);
140     }
141     newnode->next = NULL;
142 greg 2.7 newnode->ejl = NULL;
143 greg 2.3 newnode->invec[2] = sin(M_PI/180.*theta_in_deg);
144     newnode->invec[0] = cos(M_PI/180.*phi_in_deg)*newnode->invec[2];
145     newnode->invec[1] = sin(M_PI/180.*phi_in_deg)*newnode->invec[2];
146     newnode->invec[2] = sqrt(1. - newnode->invec[2]*newnode->invec[2]);
147     newnode->nrbf = nn;
148     nn = 0; /* fill RBF array */
149     for (i = 0; i < GRIDRES; i++)
150     for (j = 0; j < GRIDRES; j++)
151 greg 2.5 if (dsf_grid[i][j].nval) {
152 greg 2.6 newnode->rbfa[nn].peak = dsf_grid[i][j].vsum;
153 greg 2.5 newnode->rbfa[nn].crad = RSCA*dsf_grid[i][j].crad + .5;
154 greg 2.3 newnode->rbfa[nn].gx = i;
155     newnode->rbfa[nn].gy = j;
156     ++nn;
157     }
158 greg 2.6 /* iterate to improve interpolation accuracy */
159     do {
160 greg 2.4 double dsum = .0, dsum2 = .0;
161 greg 2.3 nn = 0;
162     for (i = 0; i < GRIDRES; i++)
163     for (j = 0; j < GRIDRES; j++)
164 greg 2.5 if (dsf_grid[i][j].nval) {
165 greg 2.3 FVECT odir;
166 greg 2.6 double corr;
167 greg 2.3 vec_from_pos(odir, i, j);
168 greg 2.6 newnode->rbfa[nn++].peak *= corr =
169 greg 2.5 dsf_grid[i][j].vsum /
170 greg 2.3 eval_rbfrep(newnode, odir);
171 greg 2.4 dsum += corr - 1.;
172     dsum2 += (corr-1.)*(corr-1.);
173 greg 2.3 }
174 greg 2.6 lastVar = thisVar;
175     thisVar = dsum2/(double)nn;
176 greg 2.4 /*
177     fprintf(stderr, "Avg., RMS error: %.1f%% %.1f%%\n",
178     100.*dsum/(double)nn,
179 greg 2.6 100.*sqrt(thisVar));
180 greg 2.4 */
181 greg 2.6 } while (--niter > 0 && lastVar-thisVar > 0.02*lastVar);
182    
183 greg 2.5 newnode->next = dsf_list;
184     return(dsf_list = newnode);
185 greg 2.3 }
186    
187 greg 2.1 /* Load a set of measurements corresponding to a particular incident angle */
188     static int
189     load_bsdf_meas(const char *fname)
190     {
191     FILE *fp = fopen(fname, "r");
192     int inp_is_DSF = -1;
193     double theta_out, phi_out, val;
194     char buf[2048];
195     int n, c;
196    
197     if (fp == NULL) {
198     fputs(fname, stderr);
199     fputs(": cannot open\n", stderr);
200     return(0);
201     }
202 greg 2.5 memset(dsf_grid, 0, sizeof(dsf_grid));
203 greg 2.1 /* read header information */
204     while ((c = getc(fp)) == '#' || c == EOF) {
205     if (fgets(buf, sizeof(buf), fp) == NULL) {
206     fputs(fname, stderr);
207     fputs(": unexpected EOF\n", stderr);
208     fclose(fp);
209     return(0);
210     }
211     if (!strcmp(buf, "format: theta phi DSF\n")) {
212     inp_is_DSF = 1;
213     continue;
214     }
215     if (!strcmp(buf, "format: theta phi BSDF\n")) {
216     inp_is_DSF = 0;
217     continue;
218     }
219     if (sscanf(buf, "intheta %lf", &theta_in_deg) == 1)
220     continue;
221     if (sscanf(buf, "inphi %lf", &phi_in_deg) == 1)
222     continue;
223     if (sscanf(buf, "incident_angle %lf %lf",
224     &theta_in_deg, &phi_in_deg) == 2)
225     continue;
226     }
227     if (inp_is_DSF < 0) {
228     fputs(fname, stderr);
229     fputs(": unknown format\n", stderr);
230     fclose(fp);
231     return(0);
232     }
233     ungetc(c, fp); /* read actual data */
234     while (fscanf(fp, "%lf %lf %lf\n", &theta_out, &phi_out, &val) == 3) {
235     FVECT ovec;
236     int pos[2];
237    
238     ovec[2] = sin(M_PI/180.*theta_out);
239     ovec[0] = cos(M_PI/180.*phi_out) * ovec[2];
240     ovec[1] = sin(M_PI/180.*phi_out) * ovec[2];
241     ovec[2] = sqrt(1. - ovec[2]*ovec[2]);
242    
243 greg 2.5 if (!inp_is_DSF)
244     val *= ovec[2]; /* convert from BSDF to DSF */
245 greg 2.1
246     pos_from_vec(pos, ovec);
247    
248 greg 2.5 dsf_grid[pos[0]][pos[1]].vsum += val;
249     dsf_grid[pos[0]][pos[1]].nval++;
250 greg 2.1 }
251     n = 0;
252     while ((c = getc(fp)) != EOF)
253     n += !isspace(c);
254     if (n)
255     fprintf(stderr,
256     "%s: warning: %d unexpected characters past EOD\n",
257     fname, n);
258     fclose(fp);
259     return(1);
260     }
261    
262     /* Compute radii for non-empty bins */
263     /* (distance to furthest empty bin for which non-empty bin is the closest) */
264     static void
265     compute_radii(void)
266     {
267 greg 2.4 unsigned int fill_grid[GRIDRES][GRIDRES];
268     unsigned short fill_cnt[GRIDRES][GRIDRES];
269 greg 2.2 FVECT ovec0, ovec1;
270     double ang2, lastang2;
271     int r, i, j, jn, ii, jj, inear, jnear;
272    
273     r = GRIDRES/2; /* proceed in zig-zag */
274 greg 2.1 for (i = 0; i < GRIDRES; i++)
275     for (jn = 0; jn < GRIDRES; jn++) {
276     j = (i&1) ? jn : GRIDRES-1-jn;
277 greg 2.5 if (dsf_grid[i][j].nval) /* find empty grid pos. */
278 greg 2.1 continue;
279 greg 2.2 vec_from_pos(ovec0, i, j);
280 greg 2.1 inear = jnear = -1; /* find nearest non-empty */
281 greg 2.2 lastang2 = M_PI*M_PI;
282 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
283     if (ii < 0) continue;
284     if (ii >= GRIDRES) break;
285     for (jj = j-r; jj <= j+r; jj++) {
286     if (jj < 0) continue;
287     if (jj >= GRIDRES) break;
288 greg 2.5 if (!dsf_grid[ii][jj].nval)
289 greg 2.1 continue;
290 greg 2.2 vec_from_pos(ovec1, ii, jj);
291     ang2 = 2. - 2.*DOT(ovec0,ovec1);
292     if (ang2 >= lastang2)
293 greg 2.1 continue;
294 greg 2.2 lastang2 = ang2;
295 greg 2.1 inear = ii; jnear = jj;
296     }
297     }
298 greg 2.2 if (inear < 0) {
299     fputs("Could not find non-empty neighbor!\n", stderr);
300     exit(1);
301     }
302     ang2 = sqrt(lastang2);
303     r = ANG2R(ang2); /* record if > previous */
304 greg 2.5 if (r > dsf_grid[inear][jnear].crad)
305     dsf_grid[inear][jnear].crad = r;
306 greg 2.2 /* next search radius */
307     r = ang2*(2.*GRIDRES/M_PI) + 1;
308 greg 2.1 }
309 greg 2.4 /* blur radii over hemisphere */
310 greg 2.1 memset(fill_grid, 0, sizeof(fill_grid));
311 greg 2.4 memset(fill_cnt, 0, sizeof(fill_cnt));
312 greg 2.1 for (i = 0; i < GRIDRES; i++)
313     for (j = 0; j < GRIDRES; j++) {
314 greg 2.5 if (!dsf_grid[i][j].crad)
315 greg 2.4 continue; /* missing distance */
316 greg 2.5 r = R2ANG(dsf_grid[i][j].crad)*(2.*RSCA*GRIDRES/M_PI);
317 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
318     if (ii < 0) continue;
319     if (ii >= GRIDRES) break;
320     for (jj = j-r; jj <= j+r; jj++) {
321     if (jj < 0) continue;
322     if (jj >= GRIDRES) break;
323 greg 2.4 if ((ii-i)*(ii-i) + (jj-j)*(jj-j) > r*r)
324 greg 2.1 continue;
325 greg 2.5 fill_grid[ii][jj] += dsf_grid[i][j].crad;
326 greg 2.4 fill_cnt[ii][jj]++;
327 greg 2.1 }
328     }
329     }
330 greg 2.6 /* copy back blurred radii */
331 greg 2.1 for (i = 0; i < GRIDRES; i++)
332     for (j = 0; j < GRIDRES; j++)
333 greg 2.4 if (fill_cnt[i][j])
334 greg 2.5 dsf_grid[i][j].crad = fill_grid[i][j]/fill_cnt[i][j];
335 greg 2.1 }
336    
337 greg 2.6 /* Cull points for more uniform distribution, leave all nval 0 or 1 */
338 greg 2.1 static void
339     cull_values(void)
340     {
341 greg 2.2 FVECT ovec0, ovec1;
342     double maxang, maxang2;
343     int i, j, ii, jj, r;
344 greg 2.1 /* simple greedy algorithm */
345     for (i = 0; i < GRIDRES; i++)
346     for (j = 0; j < GRIDRES; j++) {
347 greg 2.5 if (!dsf_grid[i][j].nval)
348 greg 2.1 continue;
349 greg 2.5 if (!dsf_grid[i][j].crad)
350 greg 2.2 continue; /* shouldn't happen */
351     vec_from_pos(ovec0, i, j);
352 greg 2.5 maxang = 2.*R2ANG(dsf_grid[i][j].crad);
353 greg 2.2 if (maxang > ovec0[2]) /* clamp near horizon */
354     maxang = ovec0[2];
355     r = maxang*(2.*GRIDRES/M_PI) + 1;
356     maxang2 = maxang*maxang;
357 greg 2.1 for (ii = i-r; ii <= i+r; ii++) {
358     if (ii < 0) continue;
359     if (ii >= GRIDRES) break;
360     for (jj = j-r; jj <= j+r; jj++) {
361     if (jj < 0) continue;
362     if (jj >= GRIDRES) break;
363 greg 2.5 if (!dsf_grid[ii][jj].nval)
364 greg 2.1 continue;
365 greg 2.2 if ((ii == i) & (jj == j))
366     continue; /* don't get self-absorbed */
367     vec_from_pos(ovec1, ii, jj);
368     if (2. - 2.*DOT(ovec0,ovec1) >= maxang2)
369 greg 2.1 continue;
370 greg 2.2 /* absorb sum */
371 greg 2.5 dsf_grid[i][j].vsum += dsf_grid[ii][jj].vsum;
372     dsf_grid[i][j].nval += dsf_grid[ii][jj].nval;
373 greg 2.2 /* keep value, though */
374 greg 2.6 dsf_grid[ii][jj].vsum /= (float)dsf_grid[ii][jj].nval;
375 greg 2.5 dsf_grid[ii][jj].nval = 0;
376 greg 2.1 }
377     }
378     }
379 greg 2.6 /* final averaging pass */
380     for (i = 0; i < GRIDRES; i++)
381     for (j = 0; j < GRIDRES; j++)
382     if (dsf_grid[i][j].nval > 1) {
383     dsf_grid[i][j].vsum /= (float)dsf_grid[i][j].nval;
384     dsf_grid[i][j].nval = 1;
385     }
386 greg 2.1 }
387    
388    
389     #if 1
390     /* Test main produces a Radiance model from the given input file */
391     int
392     main(int argc, char *argv[])
393     {
394     char buf[128];
395     FILE *pfp;
396     double bsdf;
397     FVECT dir;
398     int i, j, n;
399    
400     if (argc != 2) {
401     fprintf(stderr, "Usage: %s input.dat > output.rad\n", argv[0]);
402     return(1);
403     }
404     if (!load_bsdf_meas(argv[1]))
405     return(1);
406    
407     compute_radii();
408     cull_values();
409 greg 2.3 make_rbfrep();
410     /* produce spheres at meas. */
411     puts("void plastic yellow\n0\n0\n5 .6 .4 .01 .04 .08\n");
412 greg 2.1 puts("void plastic pink\n0\n0\n5 .5 .05 .9 .04 .08\n");
413     n = 0;
414     for (i = 0; i < GRIDRES; i++)
415     for (j = 0; j < GRIDRES; j++)
416 greg 2.5 if (dsf_grid[i][j].vsum > .0f) {
417 greg 2.1 vec_from_pos(dir, i, j);
418 greg 2.5 bsdf = dsf_grid[i][j].vsum / dir[2];
419     if (dsf_grid[i][j].nval) {
420 greg 2.3 printf("pink cone c%04d\n0\n0\n8\n", ++n);
421     printf("\t%.6g %.6g %.6g\n",
422 greg 2.1 dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
423 greg 2.3 printf("\t%.6g %.6g %.6g\n",
424 greg 2.1 dir[0]*(bsdf+.005), dir[1]*(bsdf+.005),
425     dir[2]*(bsdf+.005));
426 greg 2.3 puts("\t.003\t0\n");
427     } else {
428     vec_from_pos(dir, i, j);
429     printf("yellow sphere s%04d\n0\n0\n", ++n);
430     printf("4 %.6g %.6g %.6g .0015\n\n",
431     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
432     }
433 greg 2.1 }
434     /* output continuous surface */
435     puts("void trans tgreen\n0\n0\n7 .7 1 .7 .04 .04 .9 .9\n");
436     fflush(stdout);
437 greg 2.5 sprintf(buf, "gensurf tgreen bsdf - - - %d %d", GRIDRES-1, GRIDRES-1);
438 greg 2.1 pfp = popen(buf, "w");
439     if (pfp == NULL) {
440     fputs(buf, stderr);
441     fputs(": cannot start command\n", stderr);
442     return(1);
443     }
444     for (i = 0; i < GRIDRES; i++)
445     for (j = 0; j < GRIDRES; j++) {
446     vec_from_pos(dir, i, j);
447 greg 2.5 bsdf = eval_rbfrep(dsf_list, dir) / dir[2];
448 greg 2.1 fprintf(pfp, "%.8e %.8e %.8e\n",
449     dir[0]*bsdf, dir[1]*bsdf, dir[2]*bsdf);
450     }
451     return(pclose(pfp)==0 ? 0 : 1);
452     }
453     #endif