ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrep.c
Revision: 2.27
Committed: Fri Aug 22 05:38:44 2014 UTC (9 years, 8 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad4R2P2, rad4R2P1
Changes since 2.26: +2 -2 lines
Log Message:
Set minimum cosine to 0.02 to avoid blowing-up values near grazing

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: bsdfrep.c,v 2.26 2014/08/21 13:44:05 greg Exp $";
3 #endif
4 /*
5 * Support BSDF representation as radial basis functions.
6 *
7 * G. Ward
8 */
9
10 #define _USE_MATH_DEFINES
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 #include "rtio.h"
15 #include "resolu.h"
16 #include "bsdfrep.h"
17 /* name and manufacturer if known */
18 char bsdf_name[256];
19 char bsdf_manuf[256];
20 /* active grid resolution */
21 int grid_res = GRIDRES;
22
23 /* coverage/symmetry using INP_QUAD? flags */
24 int inp_coverage = 0;
25 /* all incident angles in-plane so far? */
26 int single_plane_incident = -1;
27
28 /* input/output orientations */
29 int input_orient = 0;
30 int output_orient = 0;
31
32 /* BSDF histogram */
33 unsigned long bsdf_hist[HISTLEN];
34
35 /* BSDF value for boundary regions */
36 double bsdf_min = 0;
37 double bsdf_spec_peak = 0;
38 double bsdf_spec_rad = 0;
39
40 /* processed incident DSF measurements */
41 RBFNODE *dsf_list = NULL;
42
43 /* RBF-linking matrices (edges) */
44 MIGRATION *mig_list = NULL;
45
46 /* current input direction */
47 double theta_in_deg, phi_in_deg;
48
49 /* Register new input direction */
50 int
51 new_input_direction(double new_theta, double new_phi)
52 {
53 if (!input_orient) /* check input orientation */
54 input_orient = 1 - 2*(new_theta > 90.);
55 else if (input_orient > 0 ^ new_theta < 90.) {
56 fprintf(stderr,
57 "%s: Cannot handle input angles on both sides of surface\n",
58 progname);
59 return(0);
60 }
61 /* normalize angle ranges */
62 while (new_theta < -180.)
63 new_theta += 360.;
64 while (new_theta > 180.)
65 new_theta -= 360.;
66 if (new_theta < 0) {
67 new_theta = -new_theta;
68 new_phi += 180.;
69 }
70 if ((theta_in_deg = new_theta) < 1.0)
71 return(1); /* don't rely on phi near normal */
72 while (new_phi < 0)
73 new_phi += 360.;
74 while (new_phi >= 360.)
75 new_phi -= 360.;
76 if (single_plane_incident > 0) /* check input coverage */
77 single_plane_incident = (round(new_phi) == round(phi_in_deg));
78 else if (single_plane_incident < 0)
79 single_plane_incident = 1;
80 phi_in_deg = new_phi;
81 if ((1. < new_phi) & (new_phi < 89.))
82 inp_coverage |= INP_QUAD1;
83 else if ((91. < new_phi) & (new_phi < 179.))
84 inp_coverage |= INP_QUAD2;
85 else if ((181. < new_phi) & (new_phi < 269.))
86 inp_coverage |= INP_QUAD3;
87 else if ((271. < new_phi) & (new_phi < 359.))
88 inp_coverage |= INP_QUAD4;
89 return(1);
90 }
91
92 /* Apply symmetry to the given vector based on distribution */
93 int
94 use_symmetry(FVECT vec)
95 {
96 const double phi = get_phi360(vec);
97
98 switch (inp_coverage) {
99 case INP_QUAD1|INP_QUAD2|INP_QUAD3|INP_QUAD4:
100 break;
101 case INP_QUAD1|INP_QUAD2:
102 if ((-FTINY > phi) | (phi > 180.+FTINY))
103 goto mir_y;
104 break;
105 case INP_QUAD2|INP_QUAD3:
106 if ((90.-FTINY > phi) | (phi > 270.+FTINY))
107 goto mir_x;
108 break;
109 case INP_QUAD3|INP_QUAD4:
110 if ((180.-FTINY > phi) | (phi > 360.+FTINY))
111 goto mir_y;
112 break;
113 case INP_QUAD4|INP_QUAD1:
114 if ((270.-FTINY > phi) & (phi > 90.+FTINY))
115 goto mir_x;
116 break;
117 case INP_QUAD1:
118 if ((-FTINY > phi) | (phi > 90.+FTINY))
119 switch ((int)(phi*(1./90.))) {
120 case 1: goto mir_x;
121 case 2: goto mir_xy;
122 case 3: goto mir_y;
123 }
124 break;
125 case INP_QUAD2:
126 if ((90.-FTINY > phi) | (phi > 180.+FTINY))
127 switch ((int)(phi*(1./90.))) {
128 case 0: goto mir_x;
129 case 2: goto mir_y;
130 case 3: goto mir_xy;
131 }
132 break;
133 case INP_QUAD3:
134 if ((180.-FTINY > phi) | (phi > 270.+FTINY))
135 switch ((int)(phi*(1./90.))) {
136 case 0: goto mir_xy;
137 case 1: goto mir_y;
138 case 3: goto mir_x;
139 }
140 break;
141 case INP_QUAD4:
142 if ((270.-FTINY > phi) | (phi > 360.+FTINY))
143 switch ((int)(phi*(1./90.))) {
144 case 0: goto mir_y;
145 case 1: goto mir_xy;
146 case 2: goto mir_x;
147 }
148 break;
149 default:
150 fprintf(stderr, "%s: Illegal input coverage (%d)\n",
151 progname, inp_coverage);
152 exit(1);
153 }
154 return(0); /* in range */
155 mir_x:
156 vec[0] = -vec[0];
157 return(MIRROR_X);
158 mir_y:
159 vec[1] = -vec[1];
160 return(MIRROR_Y);
161 mir_xy:
162 vec[0] = -vec[0];
163 vec[1] = -vec[1];
164 return(MIRROR_X|MIRROR_Y);
165 }
166
167 /* Reverse symmetry based on what was done before */
168 void
169 rev_symmetry(FVECT vec, int sym)
170 {
171 if (sym & MIRROR_X)
172 vec[0] = -vec[0];
173 if (sym & MIRROR_Y)
174 vec[1] = -vec[1];
175 }
176
177 /* Reverse symmetry for an RBF distribution */
178 void
179 rev_rbf_symmetry(RBFNODE *rbf, int sym)
180 {
181 int n;
182
183 rev_symmetry(rbf->invec, sym);
184 if (sym & MIRROR_X)
185 for (n = rbf->nrbf; n-- > 0; )
186 rbf->rbfa[n].gx = grid_res-1 - rbf->rbfa[n].gx;
187 if (sym & MIRROR_Y)
188 for (n = rbf->nrbf; n-- > 0; )
189 rbf->rbfa[n].gy = grid_res-1 - rbf->rbfa[n].gy;
190 }
191
192 /* Rotate RBF to correspond to given incident vector */
193 void
194 rotate_rbf(RBFNODE *rbf, const FVECT invec)
195 {
196 static const FVECT vnorm = {.0, .0, 1.};
197 const double phi = atan2(invec[1],invec[0]) -
198 atan2(rbf->invec[1],rbf->invec[0]);
199 FVECT outvec;
200 int pos[2];
201 int n;
202
203 for (n = (cos(phi) < 1.-FTINY)*rbf->nrbf; n-- > 0; ) {
204 ovec_from_pos(outvec, rbf->rbfa[n].gx, rbf->rbfa[n].gy);
205 spinvector(outvec, outvec, vnorm, phi);
206 pos_from_vec(pos, outvec);
207 rbf->rbfa[n].gx = pos[0];
208 rbf->rbfa[n].gy = pos[1];
209 }
210 VCOPY(rbf->invec, invec);
211 }
212
213 /* Compute outgoing vector from grid position */
214 void
215 ovec_from_pos(FVECT vec, int xpos, int ypos)
216 {
217 double uv[2];
218 double r2;
219
220 SDsquare2disk(uv, (xpos+.5)/grid_res, (ypos+.5)/grid_res);
221 /* uniform hemispherical projection */
222 r2 = uv[0]*uv[0] + uv[1]*uv[1];
223 vec[0] = vec[1] = sqrt(2. - r2);
224 vec[0] *= uv[0];
225 vec[1] *= uv[1];
226 vec[2] = output_orient*(1. - r2);
227 }
228
229 /* Compute grid position from normalized input/output vector */
230 void
231 pos_from_vec(int pos[2], const FVECT vec)
232 {
233 double sq[2]; /* uniform hemispherical projection */
234 double norm = 1./sqrt(1. + fabs(vec[2]));
235
236 SDdisk2square(sq, vec[0]*norm, vec[1]*norm);
237
238 pos[0] = (int)(sq[0]*grid_res);
239 pos[1] = (int)(sq[1]*grid_res);
240 }
241
242 /* Compute volume associated with Gaussian lobe */
243 double
244 rbf_volume(const RBFVAL *rbfp)
245 {
246 double rad = R2ANG(rbfp->crad);
247 FVECT odir;
248 double elev, integ;
249 /* infinite integral approximation */
250 integ = (2.*M_PI) * rbfp->peak * rad*rad;
251 /* check if we're near horizon */
252 ovec_from_pos(odir, rbfp->gx, rbfp->gy);
253 elev = output_orient*odir[2];
254 /* apply cut-off correction if > 1% */
255 if (elev < 2.8*rad) {
256 /* elev = asin(elev); /* this is so crude, anyway... */
257 integ *= 1. - .5*exp(-.5*elev*elev/(rad*rad));
258 }
259 return(integ);
260 }
261
262 /* Evaluate BSDF at the given normalized outgoing direction */
263 double
264 eval_rbfrep(const RBFNODE *rp, const FVECT outvec)
265 {
266 const double rfact2 = (38./M_PI/M_PI)*(grid_res*grid_res);
267 int pos[2];
268 double res = 0;
269 const RBFVAL *rbfp;
270 FVECT odir;
271 double rad2;
272 int n;
273 /* check for wrong side */
274 if (outvec[2] > 0 ^ output_orient > 0)
275 return(.0);
276 /* use minimum if no information avail. */
277 if (rp == NULL)
278 return(bsdf_min);
279 /* optimization for fast lobe culling */
280 pos_from_vec(pos, outvec);
281 /* sum radial basis function */
282 rbfp = rp->rbfa;
283 for (n = rp->nrbf; n--; rbfp++) {
284 int d2 = (pos[0]-rbfp->gx)*(pos[0]-rbfp->gx) +
285 (pos[1]-rbfp->gy)*(pos[1]-rbfp->gy);
286 rad2 = R2ANG(rbfp->crad);
287 rad2 *= rad2;
288 if (d2 > rad2*rfact2)
289 continue;
290 ovec_from_pos(odir, rbfp->gx, rbfp->gy);
291 res += rbfp->peak * exp((DOT(odir,outvec) - 1.) / rad2);
292 }
293 res /= COSF(outvec[2]);
294 if (res < bsdf_min) /* never return less than bsdf_min */
295 return(bsdf_min);
296 return(res);
297 }
298
299 /* Insert a new directional scattering function in our global list */
300 int
301 insert_dsf(RBFNODE *newrbf)
302 {
303 RBFNODE *rbf, *rbf_last;
304 int pos;
305 /* check for redundant meas. */
306 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next)
307 if (DOT(rbf->invec, newrbf->invec) >= 1.-FTINY) {
308 fprintf(stderr,
309 "%s: Duplicate incident measurement ignored at (%.1f,%.1f)\n",
310 progname, get_theta180(newrbf->invec),
311 get_phi360(newrbf->invec));
312 free(newrbf);
313 return(-1);
314 }
315 /* keep in ascending theta order */
316 for (rbf_last = NULL, rbf = dsf_list; rbf != NULL;
317 rbf_last = rbf, rbf = rbf->next)
318 if (single_plane_incident && input_orient*rbf->invec[2] <
319 input_orient*newrbf->invec[2])
320 break;
321 if (rbf_last == NULL) { /* insert new node in list */
322 newrbf->ord = 0;
323 newrbf->next = dsf_list;
324 dsf_list = newrbf;
325 } else {
326 newrbf->ord = rbf_last->ord + 1;
327 newrbf->next = rbf;
328 rbf_last->next = newrbf;
329 }
330 rbf_last = newrbf;
331 while (rbf != NULL) { /* update ordinal positions */
332 rbf->ord = rbf_last->ord + 1;
333 rbf_last = rbf;
334 rbf = rbf->next;
335 }
336 return(newrbf->ord);
337 }
338
339 /* Get the DSF indicated by its ordinal position */
340 RBFNODE *
341 get_dsf(int ord)
342 {
343 RBFNODE *rbf;
344
345 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next)
346 if (rbf->ord == ord)
347 return(rbf);
348 return(NULL);
349 }
350
351 /* Get triangle surface orientation (unnormalized) */
352 void
353 tri_orient(FVECT vres, const FVECT v1, const FVECT v2, const FVECT v3)
354 {
355 FVECT v2minus1, v3minus2;
356
357 VSUB(v2minus1, v2, v1);
358 VSUB(v3minus2, v3, v2);
359 VCROSS(vres, v2minus1, v3minus2);
360 }
361
362 /* Determine if vertex order is reversed (inward normal) */
363 int
364 is_rev_tri(const FVECT v1, const FVECT v2, const FVECT v3)
365 {
366 FVECT tor;
367
368 tri_orient(tor, v1, v2, v3);
369
370 return(DOT(tor, v2) < 0.);
371 }
372
373 /* Find vertices completing triangles on either side of the given edge */
374 int
375 get_triangles(RBFNODE *rbfv[2], const MIGRATION *mig)
376 {
377 const MIGRATION *ej1, *ej2;
378 RBFNODE *tv;
379
380 rbfv[0] = rbfv[1] = NULL;
381 if (mig == NULL)
382 return(0);
383 for (ej1 = mig->rbfv[0]->ejl; ej1 != NULL;
384 ej1 = nextedge(mig->rbfv[0],ej1)) {
385 if (ej1 == mig)
386 continue;
387 tv = opp_rbf(mig->rbfv[0],ej1);
388 for (ej2 = tv->ejl; ej2 != NULL; ej2 = nextedge(tv,ej2))
389 if (opp_rbf(tv,ej2) == mig->rbfv[1]) {
390 rbfv[is_rev_tri(mig->rbfv[0]->invec,
391 mig->rbfv[1]->invec,
392 tv->invec)] = tv;
393 break;
394 }
395 }
396 return((rbfv[0] != NULL) + (rbfv[1] != NULL));
397 }
398
399 /* Return single-lobe specular RBF for the given incident direction */
400 RBFNODE *
401 def_rbf_spec(const FVECT invec)
402 {
403 RBFNODE *rbf;
404 FVECT ovec;
405 int pos[2];
406
407 if (input_orient > 0 ^ invec[2] > 0) /* wrong side? */
408 return(NULL);
409 if ((bsdf_spec_peak <= bsdf_min) | (bsdf_spec_rad <= 0))
410 return(NULL); /* nothing set */
411 rbf = (RBFNODE *)malloc(sizeof(RBFNODE));
412 if (rbf == NULL)
413 return(NULL);
414 ovec[0] = -invec[0];
415 ovec[1] = -invec[1];
416 ovec[2] = invec[2]*(2*(input_orient==output_orient) - 1);
417 pos_from_vec(pos, ovec);
418 rbf->ord = 0;
419 rbf->next = NULL;
420 rbf->ejl = NULL;
421 VCOPY(rbf->invec, invec);
422 rbf->nrbf = 1;
423 rbf->rbfa[0].peak = bsdf_spec_peak * output_orient*ovec[2];
424 rbf->rbfa[0].crad = ANG2R(bsdf_spec_rad);
425 rbf->rbfa[0].gx = pos[0];
426 rbf->rbfa[0].gy = pos[1];
427 rbf->vtotal = rbf_volume(rbf->rbfa);
428 return(rbf);
429 }
430
431 /* Advect and allocate new RBF along edge (internal call) */
432 RBFNODE *
433 e_advect_rbf(const MIGRATION *mig, const FVECT invec, int lobe_lim)
434 {
435 double cthresh = FTINY;
436 RBFNODE *rbf;
437 int n, i, j;
438 double t, full_dist;
439 /* get relative position */
440 t = Acos(DOT(invec, mig->rbfv[0]->invec));
441 if (t < M_PI/grid_res) { /* near first DSF */
442 n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[0]->nrbf-1);
443 rbf = (RBFNODE *)malloc(n);
444 if (rbf == NULL)
445 goto memerr;
446 memcpy(rbf, mig->rbfv[0], n); /* just duplicate */
447 rbf->next = NULL; rbf->ejl = NULL;
448 return(rbf);
449 }
450 full_dist = acos(DOT(mig->rbfv[0]->invec, mig->rbfv[1]->invec));
451 if (t > full_dist-M_PI/grid_res) { /* near second DSF */
452 n = sizeof(RBFNODE) + sizeof(RBFVAL)*(mig->rbfv[1]->nrbf-1);
453 rbf = (RBFNODE *)malloc(n);
454 if (rbf == NULL)
455 goto memerr;
456 memcpy(rbf, mig->rbfv[1], n); /* just duplicate */
457 rbf->next = NULL; rbf->ejl = NULL;
458 return(rbf);
459 }
460 t /= full_dist;
461 tryagain:
462 n = 0; /* count migrating particles */
463 for (i = 0; i < mtx_nrows(mig); i++)
464 for (j = 0; j < mtx_ncols(mig); j++)
465 n += (mtx_coef(mig,i,j) > cthresh);
466 /* are we over our limit? */
467 if ((lobe_lim > 0) & (n > lobe_lim)) {
468 cthresh = cthresh*2. + 10.*FTINY;
469 goto tryagain;
470 }
471 #ifdef DEBUG
472 fprintf(stderr, "Input RBFs have %d, %d nodes -> output has %d\n",
473 mig->rbfv[0]->nrbf, mig->rbfv[1]->nrbf, n);
474 #endif
475 rbf = (RBFNODE *)malloc(sizeof(RBFNODE) + sizeof(RBFVAL)*(n-1));
476 if (rbf == NULL)
477 goto memerr;
478 rbf->next = NULL; rbf->ejl = NULL;
479 VCOPY(rbf->invec, invec);
480 rbf->nrbf = n;
481 rbf->vtotal = 1.-t + t*mig->rbfv[1]->vtotal/mig->rbfv[0]->vtotal;
482 n = 0; /* advect RBF lobes */
483 for (i = 0; i < mtx_nrows(mig); i++) {
484 const RBFVAL *rbf0i = &mig->rbfv[0]->rbfa[i];
485 const float peak0 = rbf0i->peak;
486 const double rad0 = R2ANG(rbf0i->crad);
487 FVECT v0;
488 float mv;
489 ovec_from_pos(v0, rbf0i->gx, rbf0i->gy);
490 for (j = 0; j < mtx_ncols(mig); j++)
491 if ((mv = mtx_coef(mig,i,j)) > cthresh) {
492 const RBFVAL *rbf1j = &mig->rbfv[1]->rbfa[j];
493 double rad2;
494 FVECT v;
495 int pos[2];
496 rad2 = R2ANG(rbf1j->crad);
497 rad2 = rad0*rad0*(1.-t) + rad2*rad2*t;
498 rbf->rbfa[n].peak = peak0 * mv * rbf->vtotal *
499 rad0*rad0/rad2;
500 rbf->rbfa[n].crad = ANG2R(sqrt(rad2));
501 ovec_from_pos(v, rbf1j->gx, rbf1j->gy);
502 geodesic(v, v0, v, t, GEOD_REL);
503 pos_from_vec(pos, v);
504 rbf->rbfa[n].gx = pos[0];
505 rbf->rbfa[n].gy = pos[1];
506 ++n;
507 }
508 }
509 rbf->vtotal *= mig->rbfv[0]->vtotal; /* turn ratio into actual */
510 return(rbf);
511 memerr:
512 fprintf(stderr, "%s: Out of memory in e_advect_rbf()\n", progname);
513 exit(1);
514 return(NULL); /* pro forma return */
515 }
516
517 /* Clear our BSDF representation and free memory */
518 void
519 clear_bsdf_rep(void)
520 {
521 while (mig_list != NULL) {
522 MIGRATION *mig = mig_list;
523 mig_list = mig->next;
524 free(mig);
525 }
526 while (dsf_list != NULL) {
527 RBFNODE *rbf = dsf_list;
528 dsf_list = rbf->next;
529 free(rbf);
530 }
531 bsdf_name[0] = '\0';
532 bsdf_manuf[0] = '\0';
533 inp_coverage = 0;
534 single_plane_incident = -1;
535 input_orient = output_orient = 0;
536 grid_res = GRIDRES;
537 bsdf_min = 0;
538 bsdf_spec_peak = 0;
539 bsdf_spec_rad = 0;
540 }
541
542 /* Write our BSDF mesh interpolant out to the given binary stream */
543 void
544 save_bsdf_rep(FILE *ofp)
545 {
546 RBFNODE *rbf;
547 MIGRATION *mig;
548 int i, n;
549 /* finish header */
550 if (bsdf_name[0])
551 fprintf(ofp, "NAME=%s\n", bsdf_name);
552 if (bsdf_manuf[0])
553 fprintf(ofp, "MANUFACT=%s\n", bsdf_manuf);
554 fprintf(ofp, "SYMMETRY=%d\n", !single_plane_incident * inp_coverage);
555 fprintf(ofp, "IO_SIDES= %d %d\n", input_orient, output_orient);
556 fprintf(ofp, "GRIDRES=%d\n", grid_res);
557 fprintf(ofp, "BSDFMIN=%g\n", bsdf_min);
558 if ((bsdf_spec_peak > bsdf_min) & (bsdf_spec_rad > 0))
559 fprintf(ofp, "BSDFSPEC= %f %f\n", bsdf_spec_peak, bsdf_spec_rad);
560 fputformat(BSDFREP_FMT, ofp);
561 fputc('\n', ofp);
562 /* write each DSF */
563 for (rbf = dsf_list; rbf != NULL; rbf = rbf->next) {
564 putint(rbf->ord, 4, ofp);
565 putflt(rbf->invec[0], ofp);
566 putflt(rbf->invec[1], ofp);
567 putflt(rbf->invec[2], ofp);
568 putflt(rbf->vtotal, ofp);
569 putint(rbf->nrbf, 4, ofp);
570 for (i = 0; i < rbf->nrbf; i++) {
571 putflt(rbf->rbfa[i].peak, ofp);
572 putint(rbf->rbfa[i].crad, 2, ofp);
573 putint(rbf->rbfa[i].gx, 1, ofp);
574 putint(rbf->rbfa[i].gy, 1, ofp);
575 }
576 }
577 putint(-1, 4, ofp); /* terminator */
578 /* write each migration matrix */
579 for (mig = mig_list; mig != NULL; mig = mig->next) {
580 int zerocnt = 0;
581 putint(mig->rbfv[0]->ord, 4, ofp);
582 putint(mig->rbfv[1]->ord, 4, ofp);
583 /* write out as sparse data */
584 n = mtx_nrows(mig) * mtx_ncols(mig);
585 for (i = 0; i < n; i++) {
586 if (zerocnt == 0xff) {
587 putint(0xff, 1, ofp); zerocnt = 0;
588 }
589 if (mig->mtx[i] != 0) {
590 putint(zerocnt, 1, ofp); zerocnt = 0;
591 putflt(mig->mtx[i], ofp);
592 } else
593 ++zerocnt;
594 }
595 putint(zerocnt, 1, ofp);
596 }
597 putint(-1, 4, ofp); /* terminator */
598 putint(-1, 4, ofp);
599 if (fflush(ofp) == EOF) {
600 fprintf(stderr, "%s: error writing BSDF interpolant\n",
601 progname);
602 exit(1);
603 }
604 }
605
606 /* Check header line for critical information */
607 static int
608 headline(char *s, void *p)
609 {
610 char fmt[32];
611
612 if (!strncmp(s, "NAME=", 5)) {
613 strcpy(bsdf_name, s+5);
614 bsdf_name[strlen(bsdf_name)-1] = '\0';
615 }
616 if (!strncmp(s, "MANUFACT=", 9)) {
617 strcpy(bsdf_manuf, s+9);
618 bsdf_manuf[strlen(bsdf_manuf)-1] = '\0';
619 }
620 if (!strncmp(s, "SYMMETRY=", 9)) {
621 inp_coverage = atoi(s+9);
622 single_plane_incident = !inp_coverage;
623 return(0);
624 }
625 if (!strncmp(s, "IO_SIDES=", 9)) {
626 sscanf(s+9, "%d %d", &input_orient, &output_orient);
627 return(0);
628 }
629 if (!strncmp(s, "GRIDRES=", 8)) {
630 sscanf(s+8, "%d", &grid_res);
631 return(0);
632 }
633 if (!strncmp(s, "BSDFMIN=", 8)) {
634 sscanf(s+8, "%lf", &bsdf_min);
635 return(0);
636 }
637 if (!strncmp(s, "BSDFSPEC=", 9)) {
638 sscanf(s+9, "%lf %lf", &bsdf_spec_peak, &bsdf_spec_rad);
639 return(0);
640 }
641 if (formatval(fmt, s) && strcmp(fmt, BSDFREP_FMT))
642 return(-1);
643 return(0);
644 }
645
646 /* Read a BSDF mesh interpolant from the given binary stream */
647 int
648 load_bsdf_rep(FILE *ifp)
649 {
650 RBFNODE rbfh;
651 int from_ord, to_ord;
652 int i;
653
654 clear_bsdf_rep();
655 if (ifp == NULL)
656 return(0);
657 if (getheader(ifp, headline, NULL) < 0 || (single_plane_incident < 0) |
658 !input_orient | !output_orient |
659 (grid_res < 16) | (grid_res > 256)) {
660 fprintf(stderr, "%s: missing/bad format for BSDF interpolant\n",
661 progname);
662 return(0);
663 }
664 memset(&rbfh, 0, sizeof(rbfh)); /* read each DSF */
665 while ((rbfh.ord = getint(4, ifp)) >= 0) {
666 RBFNODE *newrbf;
667
668 rbfh.invec[0] = getflt(ifp);
669 rbfh.invec[1] = getflt(ifp);
670 rbfh.invec[2] = getflt(ifp);
671 if (normalize(rbfh.invec) == 0) {
672 fprintf(stderr, "%s: zero incident vector\n", progname);
673 return(0);
674 }
675 rbfh.vtotal = getflt(ifp);
676 rbfh.nrbf = getint(4, ifp);
677 newrbf = (RBFNODE *)malloc(sizeof(RBFNODE) +
678 sizeof(RBFVAL)*(rbfh.nrbf-1));
679 if (newrbf == NULL)
680 goto memerr;
681 *newrbf = rbfh;
682 for (i = 0; i < rbfh.nrbf; i++) {
683 newrbf->rbfa[i].peak = getflt(ifp);
684 newrbf->rbfa[i].crad = getint(2, ifp) & 0xffff;
685 newrbf->rbfa[i].gx = getint(1, ifp) & 0xff;
686 newrbf->rbfa[i].gy = getint(1, ifp) & 0xff;
687 }
688 if (feof(ifp))
689 goto badEOF;
690 /* insert in global list */
691 if (insert_dsf(newrbf) != rbfh.ord) {
692 fprintf(stderr, "%s: error adding DSF\n", progname);
693 return(0);
694 }
695 }
696 /* read each migration matrix */
697 while ((from_ord = getint(4, ifp)) >= 0 &&
698 (to_ord = getint(4, ifp)) >= 0) {
699 RBFNODE *from_rbf = get_dsf(from_ord);
700 RBFNODE *to_rbf = get_dsf(to_ord);
701 MIGRATION *newmig;
702 int n;
703
704 if ((from_rbf == NULL) | (to_rbf == NULL)) {
705 fprintf(stderr,
706 "%s: bad DSF reference in migration edge\n",
707 progname);
708 return(0);
709 }
710 n = from_rbf->nrbf * to_rbf->nrbf;
711 newmig = (MIGRATION *)malloc(sizeof(MIGRATION) +
712 sizeof(float)*(n-1));
713 if (newmig == NULL)
714 goto memerr;
715 newmig->rbfv[0] = from_rbf;
716 newmig->rbfv[1] = to_rbf;
717 memset(newmig->mtx, 0, sizeof(float)*n);
718 for (i = 0; ; ) { /* read sparse data */
719 int zc = getint(1, ifp) & 0xff;
720 if ((i += zc) >= n)
721 break;
722 if (zc == 0xff)
723 continue;
724 newmig->mtx[i++] = getflt(ifp);
725 }
726 if (feof(ifp))
727 goto badEOF;
728 /* insert in edge lists */
729 newmig->enxt[0] = from_rbf->ejl;
730 from_rbf->ejl = newmig;
731 newmig->enxt[1] = to_rbf->ejl;
732 to_rbf->ejl = newmig;
733 /* push onto global list */
734 newmig->next = mig_list;
735 mig_list = newmig;
736 }
737 return(1); /* success! */
738 memerr:
739 fprintf(stderr, "%s: Out of memory in load_bsdf_rep()\n", progname);
740 exit(1);
741 badEOF:
742 fprintf(stderr, "%s: Unexpected EOF in load_bsdf_rep()\n", progname);
743 return(0);
744 }