ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/bsdfrep.c
Revision: 2.33
Committed: Sat Dec 28 18:05:14 2019 UTC (4 years, 4 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R3
Changes since 2.32: +1 -2 lines
Log Message:
Removed redundant include files

File Contents

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