ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/ambient.c
Revision: 2.113
Committed: Mon Mar 7 19:30:53 2022 UTC (2 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.112: +1 -3 lines
Log Message:
style: removed unused reference

File Contents

# User Rev Content
1 greg 2.113 static const char RCSid[] = "$Id: ambient.c,v 2.112 2022/03/07 17:52:14 greg Exp $";
2 greg 1.1 /*
3     * ambient.c - routines dealing with ambient (inter-reflected) component.
4 greg 2.47 *
5     * Declarations of external symbols in ambient.h
6     */
7    
8 greg 2.48 #include "copyright.h"
9 greg 1.1
10 schorsch 2.52 #include <string.h>
11    
12 schorsch 2.50 #include "platform.h"
13 greg 1.1 #include "ray.h"
14 greg 1.8 #include "otypes.h"
15 greg 2.105 #include "otspecial.h"
16 schorsch 2.56 #include "resolu.h"
17 greg 1.14 #include "ambient.h"
18 greg 1.1 #include "random.h"
19 greg 2.94 #include "pmapamb.h"
20 greg 1.1
21 greg 2.32 #ifndef OCTSCALE
22 greg 2.29 #define OCTSCALE 1.0 /* ceil((valid rad.)/(cube size)) */
23 greg 2.32 #endif
24 greg 1.1
25 greg 2.57 #ifndef MAXASET
26 greg 2.67 #define MAXASET 4095 /* maximum number of elements in ambient set */
27 greg 2.57 #endif
28 greg 2.12 OBJECT ambset[MAXASET+1]={0}; /* ambient include/exclude set */
29 greg 1.1
30 greg 2.12 double maxarad; /* maximum ambient radius */
31     double minarad; /* minimum ambient radius */
32 greg 1.1
33 greg 2.12 static AMBTREE atrunk; /* our ambient trunk node */
34 greg 1.1
35     static FILE *ambfp = NULL; /* ambient file pointer */
36 greg 2.16 static int nunflshed = 0; /* number of unflushed ambient values */
37 greg 1.1
38 gregl 2.43 static double avsum = 0.; /* computed ambient value sum (log) */
39     static unsigned int navsum = 0; /* number of values in avsum */
40 greg 2.35 static unsigned int nambvals = 0; /* total number of indirect values */
41     static unsigned int nambshare = 0; /* number of values from file */
42 greg 2.47 static FILE *ambinp = NULL; /* auxiliary file for input */
43     static long lastpos = -1; /* last flush position */
44 greg 2.26
45 greg 2.12 #define AMBFLUSH (BUFSIZ/AMBVALSIZ)
46 greg 2.6
47 greg 2.47 #define newambval() (AMBVAL *)malloc(sizeof(AMBVAL))
48 greg 1.1
49 greg 2.108 #define tfunc(lwr, x, upr) (((x)-(lwr))/((upr)-(lwr)))
50    
51 schorsch 2.56 static void initambfile(int creat);
52     static void avsave(AMBVAL *av);
53     static AMBVAL *avstore(AMBVAL *aval);
54     static AMBTREE *newambtree(void);
55     static void freeambtree(AMBTREE *atp);
56    
57 greg 2.71 typedef void unloadtf_t(AMBVAL *);
58 schorsch 2.56 static unloadtf_t avinsert;
59 greg 2.71 static unloadtf_t avfree;
60 schorsch 2.56 static void unloadatree(AMBTREE *at, unloadtf_t *f);
61    
62 greg 2.112 static void sortambvals(void);
63 schorsch 2.56
64 greg 2.108 static int plugaleak(RAY *r, AMBVAL *ap, FVECT anorm, double ang);
65     static double sumambient(COLOR acol, RAY *r, FVECT rn, int al,
66     AMBTREE *at, FVECT c0, double s);
67     static int makeambient(COLOR acol, RAY *r, FVECT rn, int al);
68     static int extambient(COLOR cr, AMBVAL *ap, FVECT pv, FVECT nv,
69     FVECT uvw[3]);
70    
71 greg 2.19 #ifdef F_SETLKW
72 schorsch 2.56 static void aflock(int typ);
73 greg 2.19 #endif
74 greg 1.1
75 greg 2.7
76 greg 2.71 void
77 schorsch 2.56 setambres( /* set ambient resolution */
78     int ar
79     )
80 greg 2.3 {
81 greg 2.21 ambres = ar < 0 ? 0 : ar; /* may be done already */
82 greg 2.3 /* set min & max radii */
83     if (ar <= 0) {
84 greg 2.25 minarad = 0;
85 greg 2.89 maxarad = thescene.cusize*0.2;
86 greg 2.3 } else {
87     minarad = thescene.cusize / ar;
88 greg 2.83 maxarad = 64.0 * minarad; /* heuristic */
89 greg 2.89 if (maxarad > thescene.cusize*0.2)
90     maxarad = thescene.cusize*0.2;
91 greg 2.3 }
92 greg 2.25 if (minarad <= FTINY)
93 greg 2.83 minarad = 10.0*FTINY;
94 greg 2.25 if (maxarad <= minarad)
95 greg 2.83 maxarad = 64.0 * minarad;
96 greg 2.3 }
97    
98    
99 greg 2.71 void
100 schorsch 2.56 setambacc( /* set ambient accuracy */
101     double newa
102     )
103 greg 2.20 {
104 greg 2.84 static double olda; /* remember previous setting here */
105 greg 2.82
106     newa *= (newa > 0);
107     if (fabs(newa - olda) >= .05*(newa + olda)) {
108 greg 2.84 ambacc = newa;
109 greg 2.107 if (ambacc > FTINY && nambvals > 0)
110 greg 2.112 sortambvals(); /* rebuild tree */
111 greg 2.74 }
112 greg 2.20 }
113    
114    
115 greg 2.71 void
116 schorsch 2.56 setambient(void) /* initialize calculation */
117 greg 1.1 {
118 gwlarson 2.46 int readonly = 0;
119 greg 2.66 long flen;
120 greg 2.12 AMBVAL amb;
121 greg 2.47 /* make sure we're fresh */
122     ambdone();
123 greg 2.3 /* init ambient limits */
124     setambres(ambres);
125 greg 2.82 setambacc(ambacc);
126 greg 2.47 if (ambfile == NULL || !ambfile[0])
127 greg 2.19 return;
128 greg 2.20 if (ambacc <= FTINY) {
129 greg 2.21 sprintf(errmsg, "zero ambient accuracy so \"%s\" not opened",
130 greg 2.47 ambfile);
131 greg 2.20 error(WARNING, errmsg);
132     return;
133     }
134 greg 2.3 /* open ambient file */
135 greg 2.47 if ((ambfp = fopen(ambfile, "r+")) == NULL)
136     readonly = (ambfp = fopen(ambfile, "r")) != NULL;
137 gwlarson 2.46 if (ambfp != NULL) {
138     initambfile(0); /* file exists */
139 greg 2.66 lastpos = ftell(ambfp);
140 greg 2.19 while (readambval(&amb, ambfp))
141 greg 2.88 avstore(&amb);
142 gwlarson 2.46 nambshare = nambvals; /* share loaded values */
143     if (readonly) {
144     sprintf(errmsg,
145     "loaded %u values from read-only ambient file",
146     nambvals);
147     error(WARNING, errmsg);
148     fclose(ambfp); /* close file so no writes */
149     ambfp = NULL;
150     return; /* avoid ambsync() */
151     }
152     /* align file pointer */
153 greg 2.66 lastpos += (long)nambvals*AMBVALSIZ;
154 greg 2.55 flen = lseek(fileno(ambfp), (off_t)0, SEEK_END);
155 greg 2.66 if (flen != lastpos) {
156 greg 2.42 sprintf(errmsg,
157 greg 2.37 "ignoring last %ld values in ambient file (corrupted)",
158 greg 2.66 (flen - lastpos)/AMBVALSIZ);
159 greg 2.42 error(WARNING, errmsg);
160 greg 2.66 fseek(ambfp, lastpos, SEEK_SET);
161     ftruncate(fileno(ambfp), (off_t)lastpos);
162 greg 2.37 }
163 greg 2.47 } else if ((ambfp = fopen(ambfile, "w+")) != NULL) {
164 gwlarson 2.46 initambfile(1); /* else create new file */
165 greg 2.68 fflush(ambfp);
166 greg 2.66 lastpos = ftell(ambfp);
167 gwlarson 2.46 } else {
168 greg 2.47 sprintf(errmsg, "cannot open ambient file \"%s\"", ambfile);
169 greg 2.19 error(SYSTEM, errmsg);
170 greg 2.16 }
171 greg 2.68 #ifdef F_SETLKW
172     aflock(F_UNLCK); /* release file */
173     #endif
174 greg 1.8 }
175    
176    
177 greg 2.71 void
178 schorsch 2.56 ambdone(void) /* close ambient file and free memory */
179 greg 2.47 {
180     if (ambfp != NULL) { /* close ambient file */
181     ambsync();
182     fclose(ambfp);
183     ambfp = NULL;
184     if (ambinp != NULL) {
185     fclose(ambinp);
186     ambinp = NULL;
187     }
188     lastpos = -1;
189     }
190     /* free ambient tree */
191 greg 2.99 unloadatree(&atrunk, avfree);
192 greg 2.47 /* reset state variables */
193     avsum = 0.;
194     navsum = 0;
195     nambvals = 0;
196     nambshare = 0;
197     }
198    
199    
200 greg 2.71 void
201 schorsch 2.56 ambnotify( /* record new modifier */
202     OBJECT obj
203     )
204 greg 1.8 {
205 greg 1.11 static int hitlimit = 0;
206 greg 2.70 OBJREC *o;
207     char **amblp;
208 greg 1.8
209 greg 2.47 if (obj == OVOID) { /* starting over */
210     ambset[0] = 0;
211     hitlimit = 0;
212     return;
213     }
214     o = objptr(obj);
215 greg 1.11 if (hitlimit || !ismodifier(o->otype))
216 greg 1.8 return;
217     for (amblp = amblist; *amblp != NULL; amblp++)
218     if (!strcmp(o->oname, *amblp)) {
219 greg 1.11 if (ambset[0] >= MAXASET) {
220     error(WARNING, "too many modifiers in ambient list");
221     hitlimit++;
222     return; /* should this be fatal? */
223     }
224 greg 1.8 insertelem(ambset, obj);
225     return;
226 greg 1.1 }
227     }
228    
229 greg 2.71
230     void
231     multambient( /* compute ambient component & multiply by coef. */
232     COLOR aval,
233     RAY *r,
234     FVECT nrm
235     )
236     {
237 greg 2.111 static double logAvgAbsorp = 1;
238 greg 2.71 static int rdepth = 0; /* ambient recursion */
239 greg 2.94 COLOR acol, caustic;
240 greg 2.103 int i, ok;
241 greg 2.71 double d, l;
242    
243 greg 2.94 /* PMAP: Factor in ambient from photon map, if enabled and ray is
244     * ambient. Return as all ambient components accounted for, else
245     * continue. */
246     if (ambPmap(aval, r, rdepth))
247     return;
248    
249 greg 2.111 if (logAvgAbsorp > 0) /* exclude in -aw to avoid growth */
250     logAvgAbsorp = log(1.-AVGREFL);
251    
252 greg 2.94 /* PMAP: Factor in specular-diffuse ambient (caustics) from photon
253     * map, if enabled and ray is primary, else caustic is zero. Continue
254     * with RADIANCE ambient calculation */
255     copycolor(caustic, aval);
256     ambPmapCaustic(caustic, r, rdepth);
257    
258 greg 2.71 if (ambdiv <= 0) /* no ambient calculation */
259     goto dumbamb;
260     /* check number of bounces */
261     if (rdepth >= ambounce)
262     goto dumbamb;
263     /* check ambient list */
264     if (ambincl != -1 && r->ro != NULL &&
265     ambincl != inset(ambset, r->ro->omod))
266     goto dumbamb;
267    
268     if (ambacc <= FTINY) { /* no ambient storage */
269 greg 2.103 FVECT uvd[2];
270     float dgrad[2], *dgp = NULL;
271    
272     if (nrm != r->ron && DOT(nrm,r->ron) < 0.9999)
273     dgp = dgrad; /* compute rotational grad. */
274 greg 2.71 copycolor(acol, aval);
275     rdepth++;
276 greg 2.86 ok = doambient(acol, r, r->rweight,
277 greg 2.103 uvd, NULL, NULL, dgp, NULL);
278 greg 2.71 rdepth--;
279     if (!ok)
280     goto dumbamb;
281 greg 2.103 if ((ok > 0) & (dgp != NULL)) { /* apply texture */
282     FVECT v1;
283     VCROSS(v1, r->ron, nrm);
284     d = 1.0;
285     for (i = 3; i--; )
286     d += v1[i] * (dgp[0]*uvd[0][i] + dgp[1]*uvd[1][i]);
287     if (d >= 0.05)
288     scalecolor(acol, d);
289     }
290 greg 2.71 copycolor(aval, acol);
291 greg 2.94
292     /* PMAP: add in caustic */
293     addcolor(aval, caustic);
294 greg 2.71 return;
295     }
296     /* interpolate ambient value */
297     setcolor(acol, 0.0, 0.0, 0.0);
298     d = sumambient(acol, r, nrm, rdepth,
299     &atrunk, thescene.cuorg, thescene.cusize);
300 greg 2.94
301 greg 2.71 if (d > FTINY) {
302     d = 1.0/d;
303     scalecolor(acol, d);
304     multcolor(aval, acol);
305 greg 2.94
306     /* PMAP: add in caustic */
307     addcolor(aval, caustic);
308 greg 2.71 return;
309     }
310 greg 2.94
311 greg 2.71 rdepth++; /* need to cache new value */
312     ok = makeambient(acol, r, nrm, rdepth-1);
313     rdepth--;
314 greg 2.94
315 greg 2.71 if (ok) {
316 greg 2.73 multcolor(aval, acol); /* computed new value */
317 greg 2.94
318     /* PMAP: add in caustic */
319     addcolor(aval, caustic);
320 greg 2.71 return;
321     }
322 greg 2.94
323 greg 2.71 dumbamb: /* return global value */
324     if ((ambvwt <= 0) | (navsum == 0)) {
325     multcolor(aval, ambval);
326 greg 2.94
327     /* PMAP: add in caustic */
328     addcolor(aval, caustic);
329 greg 2.71 return;
330     }
331 greg 2.94
332     l = bright(ambval); /* average in computations */
333 greg 2.71 if (l > FTINY) {
334 greg 2.111 d = (log(l)*(double)ambvwt + avsum + logAvgAbsorp*navsum) /
335 greg 2.71 (double)(ambvwt + navsum);
336     d = exp(d) / l;
337     scalecolor(aval, d);
338     multcolor(aval, ambval); /* apply color of ambval */
339     } else {
340 greg 2.111 d = exp( avsum/(double)navsum + logAvgAbsorp );
341 greg 2.71 scalecolor(aval, d); /* neutral color */
342     }
343     }
344    
345    
346 greg 2.86 /* Plug a potential leak where ambient cache value is occluded */
347     static int
348     plugaleak(RAY *r, AMBVAL *ap, FVECT anorm, double ang)
349     {
350     const double cost70sq = 0.1169778; /* cos(70deg)^2 */
351     RAY rtst;
352     FVECT vdif;
353     double normdot, ndotd, nadotd;
354     double a, b, c, t[2];
355    
356     ang += 2.*PI*(ang < 0); /* check direction flags */
357     if ( !(ap->corral>>(int)(ang*(16./PI)) & 1) )
358     return(0);
359     /*
360     * Generate test ray, targeting 20 degrees above sample point plane
361     * along surface normal from cache position. This should be high
362     * enough to miss local geometry we don't really care about.
363     */
364     VSUB(vdif, ap->pos, r->rop);
365     normdot = DOT(anorm, r->ron);
366     ndotd = DOT(vdif, r->ron);
367     nadotd = DOT(vdif, anorm);
368     a = normdot*normdot - cost70sq;
369     b = 2.0*(normdot*ndotd - nadotd*cost70sq);
370     c = ndotd*ndotd - DOT(vdif,vdif)*cost70sq;
371     if (quadratic(t, a, b, c) != 2)
372     return(1); /* should rarely happen */
373     if (t[1] <= FTINY)
374     return(0); /* should fail behind test */
375     rayorigin(&rtst, SHADOW, r, NULL);
376     VSUM(rtst.rdir, vdif, anorm, t[1]); /* further dist. > plane */
377     rtst.rmax = normalize(rtst.rdir); /* short ray test */
378     while (localhit(&rtst, &thescene)) { /* check for occluder */
379 greg 2.105 OBJREC *m = findmaterial(rtst.ro);
380     if (m != NULL && !istransp(m->otype) && !isBSDFproxy(m) &&
381 greg 2.86 (rtst.clipset == NULL ||
382     !inset(rtst.clipset, rtst.ro->omod)))
383     return(1); /* plug light leak */
384     VCOPY(rtst.rorg, rtst.rop); /* skip invisible surface */
385     rtst.rmax -= rtst.rot;
386     rayclear(&rtst);
387     }
388     return(0); /* seems we're OK */
389     }
390    
391    
392     static double
393 greg 2.72 sumambient( /* get interpolated ambient value */
394 greg 2.71 COLOR acol,
395     RAY *r,
396     FVECT rn,
397     int al,
398     AMBTREE *at,
399     FVECT c0,
400     double s
401     )
402 greg 2.80 { /* initial limit is 10 degrees plus ambacc radians */
403     const double minangle = 10.0 * PI/180.;
404 greg 2.84 double maxangle = minangle + ambacc;
405 greg 2.71 double wsum = 0.0;
406     FVECT ck0;
407     int i, j;
408     AMBVAL *av;
409 greg 2.83
410     if (at->kid != NULL) { /* sum children first */
411     s *= 0.5;
412     for (i = 0; i < 8; i++) {
413     for (j = 0; j < 3; j++) {
414     ck0[j] = c0[j];
415     if (1<<j & i)
416     ck0[j] += s;
417     if (r->rop[j] < ck0[j] - OCTSCALE*s)
418     break;
419     if (r->rop[j] > ck0[j] + (1.0+OCTSCALE)*s)
420     break;
421     }
422     if (j == 3)
423     wsum += sumambient(acol, r, rn, al,
424     at->kid+i, ck0, s);
425     }
426     /* good enough? */
427 greg 2.84 if (wsum >= 0.05 && s > minarad*10.0)
428 greg 2.83 return(wsum);
429     }
430 greg 2.84 /* adjust maximum angle */
431     if (at->alist != NULL && (at->alist->lvl <= al) & (r->rweight < 0.6))
432     maxangle = (maxangle - PI/2.)*pow(r->rweight,0.13) + PI/2.;
433 greg 2.71 /* sum this node */
434     for (av = at->alist; av != NULL; av = av->next) {
435 greg 2.86 double u, v, d, delta_r2, delta_t2;
436 greg 2.71 COLOR ct;
437     FVECT uvw[3];
438     /*
439 greg 2.72 * Ambient level test
440 greg 2.71 */
441 greg 2.87 if (av->lvl > al || /* list sorted, so this works */
442     (av->lvl == al) & (av->weight < 0.9*r->rweight))
443 greg 2.71 break;
444     /*
445 greg 2.72 * Direction test using unperturbed normal
446 greg 2.71 */
447     decodedir(uvw[2], av->ndir);
448     d = DOT(uvw[2], r->ron);
449     if (d <= 0.0) /* >= 90 degrees */
450     continue;
451     delta_r2 = 2.0 - 2.0*d; /* approx. radians^2 */
452     if (delta_r2 >= maxangle*maxangle)
453     continue;
454     /*
455 greg 2.81 * Modified ray behind test
456     */
457 greg 2.85 VSUB(ck0, r->rop, av->pos);
458 greg 2.81 d = DOT(ck0, uvw[2]);
459 greg 2.84 if (d < -minarad*ambacc-.001)
460 greg 2.81 continue;
461     d /= av->rad[0];
462     delta_t2 = d*d;
463 greg 2.84 if (delta_t2 >= ambacc*ambacc)
464 greg 2.81 continue;
465     /*
466 greg 2.72 * Elliptical radii test based on Hessian
467 greg 2.71 */
468     decodedir(uvw[0], av->udir);
469     VCROSS(uvw[1], uvw[2], uvw[0]);
470 greg 2.86 d = (u = DOT(ck0, uvw[0])) / av->rad[0];
471 greg 2.81 delta_t2 += d*d;
472 greg 2.86 d = (v = DOT(ck0, uvw[1])) / av->rad[1];
473 greg 2.71 delta_t2 += d*d;
474 greg 2.84 if (delta_t2 >= ambacc*ambacc)
475 greg 2.71 continue;
476     /*
477 greg 2.86 * Test for potential light leak
478     */
479     if (av->corral && plugaleak(r, av, uvw[2], atan2a(v,u)))
480     continue;
481     /*
482 greg 2.72 * Extrapolate value and compute final weight (hat function)
483 greg 2.71 */
484 greg 2.92 if (!extambient(ct, av, r->rop, rn, uvw))
485     continue;
486 greg 2.71 d = tfunc(maxangle, sqrt(delta_r2), 0.0) *
487 greg 2.84 tfunc(ambacc, sqrt(delta_t2), 0.0);
488 greg 2.71 scalecolor(ct, d);
489     addcolor(acol, ct);
490 greg 2.72 wsum += d;
491 greg 2.71 }
492     return(wsum);
493     }
494    
495    
496 greg 2.86 static int
497 greg 2.71 makeambient( /* make a new ambient value for storage */
498     COLOR acol,
499     RAY *r,
500     FVECT rn,
501     int al
502     )
503     {
504     AMBVAL amb;
505 greg 2.72 FVECT uvw[3];
506 greg 2.71 int i;
507    
508     amb.weight = 1.0; /* compute weight */
509     for (i = al; i-- > 0; )
510     amb.weight *= AVGREFL;
511     if (r->rweight < 0.1*amb.weight) /* heuristic override */
512     amb.weight = 1.25*r->rweight;
513     setcolor(acol, AVGREFL, AVGREFL, AVGREFL);
514     /* compute ambient */
515 greg 2.86 i = doambient(acol, r, amb.weight,
516     uvw, amb.rad, amb.gpos, amb.gdir, &amb.corral);
517 greg 2.71 scalecolor(acol, 1./AVGREFL); /* undo assumed reflectance */
518 greg 2.76 if (i <= 0 || amb.rad[0] <= FTINY) /* no Hessian or zero radius */
519 greg 2.73 return(i);
520 greg 2.71 /* store value */
521     VCOPY(amb.pos, r->rop);
522     amb.ndir = encodedir(r->ron);
523 greg 2.72 amb.udir = encodedir(uvw[0]);
524 greg 2.71 amb.lvl = al;
525     copycolor(amb.val, acol);
526     /* insert into tree */
527     avsave(&amb); /* and save to file */
528 greg 2.72 if (rn != r->ron) { /* texture */
529     VCOPY(uvw[2], r->ron);
530     extambient(acol, &amb, r->rop, rn, uvw);
531     }
532 greg 2.71 return(1);
533     }
534    
535    
536 greg 2.92 static int
537 greg 2.71 extambient( /* extrapolate value at pv, nv */
538     COLOR cr,
539     AMBVAL *ap,
540     FVECT pv,
541 greg 2.72 FVECT nv,
542     FVECT uvw[3]
543 greg 2.71 )
544     {
545 greg 2.93 const double min_d = 0.05;
546 greg 2.110 const double max_d = 20.;
547 greg 2.72 static FVECT my_uvw[3];
548     FVECT v1;
549     int i;
550     double d = 1.0; /* zeroeth order */
551    
552     if (uvw == NULL) { /* need local coordinates? */
553     decodedir(my_uvw[2], ap->ndir);
554     decodedir(my_uvw[0], ap->udir);
555     VCROSS(my_uvw[1], my_uvw[2], my_uvw[0]);
556     uvw = my_uvw;
557     }
558 greg 2.71 for (i = 3; i--; ) /* gradient due to translation */
559     d += (pv[i] - ap->pos[i]) *
560     (ap->gpos[0]*uvw[0][i] + ap->gpos[1]*uvw[1][i]);
561    
562     VCROSS(v1, uvw[2], nv); /* gradient due to rotation */
563     for (i = 3; i--; )
564     d += v1[i] * (ap->gdir[0]*uvw[0][i] + ap->gdir[1]*uvw[1][i]);
565    
566 greg 2.110 if (d < min_d) /* clamp min/max scaling */
567 greg 2.93 d = min_d;
568 greg 2.110 else if (d > max_d)
569     d = max_d;
570 greg 2.71 copycolor(cr, ap->val);
571     scalecolor(cr, d);
572 greg 2.93 return(d > min_d);
573 greg 2.71 }
574    
575    
576     static void
577     avinsert( /* insert ambient value in our tree */
578     AMBVAL *av
579     )
580     {
581     AMBTREE *at;
582     AMBVAL *ap;
583     AMBVAL avh;
584     FVECT ck0;
585     double s;
586     int branch;
587     int i;
588    
589     if (av->rad[0] <= FTINY)
590     error(CONSISTENCY, "zero ambient radius in avinsert");
591     at = &atrunk;
592     VCOPY(ck0, thescene.cuorg);
593     s = thescene.cusize;
594 greg 2.84 while (s*(OCTSCALE/2) > av->rad[1]*ambacc) {
595 greg 2.71 if (at->kid == NULL)
596     if ((at->kid = newambtree()) == NULL)
597     error(SYSTEM, "out of memory in avinsert");
598     s *= 0.5;
599     branch = 0;
600     for (i = 0; i < 3; i++)
601     if (av->pos[i] > ck0[i] + s) {
602     ck0[i] += s;
603     branch |= 1 << i;
604     }
605     at = at->kid + branch;
606     }
607     avh.next = at->alist; /* order by increasing level */
608     for (ap = &avh; ap->next != NULL; ap = ap->next)
609 greg 2.87 if ( ap->next->lvl > av->lvl ||
610     (ap->next->lvl == av->lvl) &
611     (ap->next->weight <= av->weight) )
612 greg 2.71 break;
613     av->next = ap->next;
614     ap->next = (AMBVAL*)av;
615     at->alist = avh.next;
616     }
617    
618    
619     static void
620 schorsch 2.56 initambfile( /* initialize ambient file */
621 greg 2.68 int cre8
622 schorsch 2.56 )
623 greg 2.9 {
624 greg 2.47 extern char *progname, *octname;
625     static char *mybuf = NULL;
626 greg 2.9
627 greg 2.19 #ifdef F_SETLKW
628 greg 2.69 aflock(cre8 ? F_WRLCK : F_RDLCK);
629 greg 2.19 #endif
630 schorsch 2.50 SET_FILE_BINARY(ambfp);
631 greg 2.47 if (mybuf == NULL)
632     mybuf = (char *)bmalloc(BUFSIZ+8);
633     setbuf(ambfp, mybuf);
634 greg 2.68 if (cre8) { /* new file */
635 greg 2.24 newheader("RADIANCE", ambfp);
636 greg 2.41 fprintf(ambfp, "%s -av %g %g %g -aw %d -ab %d -aa %g ",
637 greg 2.9 progname, colval(ambval,RED),
638     colval(ambval,GRN), colval(ambval,BLU),
639 greg 2.41 ambvwt, ambounce, ambacc);
640 greg 2.47 fprintf(ambfp, "-ad %d -as %d -ar %d ",
641     ambdiv, ambssamp, ambres);
642     if (octname != NULL)
643 greg 2.68 fputs(octname, ambfp);
644     fputc('\n', ambfp);
645 greg 2.9 fprintf(ambfp, "SOFTWARE= %s\n", VersionID);
646 greg 2.47 fputnow(ambfp);
647 greg 2.9 fputformat(AMBFMT, ambfp);
648 greg 2.68 fputc('\n', ambfp);
649 greg 2.9 putambmagic(ambfp);
650 greg 2.17 } else if (checkheader(ambfp, AMBFMT, NULL) < 0 || !hasambmagic(ambfp))
651     error(USER, "bad ambient file");
652 greg 2.9 }
653    
654    
655 greg 2.47 static void
656 schorsch 2.56 avsave( /* insert and save an ambient value */
657     AMBVAL *av
658     )
659 greg 1.1 {
660 greg 2.88 avstore(av);
661 greg 1.1 if (ambfp == NULL)
662     return;
663 greg 2.5 if (writambval(av, ambfp) < 0)
664 greg 1.1 goto writerr;
665 greg 2.16 if (++nunflshed >= AMBFLUSH)
666 greg 2.7 if (ambsync() == EOF)
667 greg 1.1 goto writerr;
668     return;
669     writerr:
670 greg 2.47 error(SYSTEM, "error writing to ambient file");
671 greg 1.1 }
672    
673    
674 greg 2.20 static AMBVAL *
675 greg 2.88 avstore( /* allocate memory and save aval */
676 greg 2.70 AMBVAL *aval
677 schorsch 2.56 )
678 greg 2.20 {
679 greg 2.70 AMBVAL *av;
680 gregl 2.43 double d;
681 greg 2.20
682     if ((av = newambval()) == NULL)
683     error(SYSTEM, "out of memory in avstore");
684 schorsch 2.53 *av = *aval;
685 greg 2.26 av->next = NULL;
686     nambvals++;
687 gregl 2.43 d = bright(av->val);
688     if (d > FTINY) { /* add to log sum for averaging */
689     avsum += log(d);
690     navsum++;
691     }
692 greg 2.88 avinsert(av); /* insert in our cache tree */
693 greg 2.20 return(av);
694     }
695    
696    
697 greg 2.26 #define ATALLOCSZ 512 /* #/8 trees to allocate at once */
698    
699     static AMBTREE *atfreelist = NULL; /* free ambient tree structures */
700    
701    
702 greg 2.47 static AMBTREE *
703 schorsch 2.56 newambtree(void) /* allocate 8 ambient tree structs */
704 greg 2.26 {
705 greg 2.70 AMBTREE *atp, *upperlim;
706 greg 2.26
707     if (atfreelist == NULL) { /* get more nodes */
708 greg 2.47 atfreelist = (AMBTREE *)malloc(ATALLOCSZ*8*sizeof(AMBTREE));
709 greg 2.26 if (atfreelist == NULL)
710     return(NULL);
711     /* link new free list */
712     upperlim = atfreelist + 8*(ATALLOCSZ-1);
713     for (atp = atfreelist; atp < upperlim; atp += 8)
714     atp->kid = atp + 8;
715     atp->kid = NULL;
716     }
717     atp = atfreelist;
718     atfreelist = atp->kid;
719 greg 2.102 memset(atp, 0, 8*sizeof(AMBTREE));
720 greg 2.26 return(atp);
721     }
722    
723    
724 greg 2.47 static void
725 schorsch 2.56 freeambtree( /* free 8 ambient tree structs */
726     AMBTREE *atp
727     )
728 greg 2.26 {
729 greg 2.100 atp->kid = atfreelist;
730 greg 2.26 atfreelist = atp;
731     }
732    
733    
734 greg 2.47 static void
735 schorsch 2.56 unloadatree( /* unload an ambient value tree */
736 greg 2.70 AMBTREE *at,
737 schorsch 2.56 unloadtf_t *f
738     )
739 greg 2.20 {
740 greg 2.70 AMBVAL *av;
741     int i;
742 greg 2.20 /* transfer values at this node */
743     for (av = at->alist; av != NULL; av = at->alist) {
744     at->alist = av->next;
745 greg 2.102 av->next = NULL;
746 greg 2.26 (*f)(av);
747 greg 2.20 }
748 greg 2.21 if (at->kid == NULL)
749     return;
750 greg 2.20 for (i = 0; i < 8; i++) /* transfer and free children */
751 greg 2.26 unloadatree(at->kid+i, f);
752 greg 2.20 freeambtree(at->kid);
753 greg 2.26 at->kid = NULL;
754     }
755    
756    
757 schorsch 2.56 static void
758 greg 2.71 avfree(AMBVAL *av)
759     {
760     free(av);
761     }
762    
763 greg 2.112
764 greg 2.71 static void
765 greg 2.112 sortambvals(void) /* resort ambient values */
766 greg 2.27 {
767 greg 2.112 AMBTREE oldatrunk = atrunk;
768 greg 2.27
769 greg 2.112 atrunk.alist = NULL;
770     atrunk.kid = NULL;
771     unloadatree(&oldatrunk, avinsert);
772 greg 2.20 }
773    
774    
775 greg 2.18 #ifdef F_SETLKW
776 greg 2.10
777 greg 2.47 static void
778 schorsch 2.56 aflock( /* lock/unlock ambient file */
779     int typ
780     )
781 greg 2.19 {
782     static struct flock fls; /* static so initialized to zeroes */
783    
784 greg 2.66 if (typ == fls.l_type) /* already called? */
785     return;
786 greg 2.109
787 greg 2.19 fls.l_type = typ;
788 greg 2.109 do
789     if (fcntl(fileno(ambfp), F_SETLKW, &fls) != -1)
790     return;
791     while (errno == EINTR);
792    
793     error(SYSTEM, "cannot (un)lock ambient file");
794 greg 2.19 }
795    
796    
797 greg 2.71 int
798 schorsch 2.56 ambsync(void) /* synchronize ambient file */
799 greg 2.7 {
800 greg 2.15 long flen;
801 greg 2.12 AMBVAL avs;
802 greg 2.70 int n;
803 greg 2.16
804 greg 2.65 if (ambfp == NULL) /* no ambient file? */
805     return(0);
806 greg 2.63 /* gain appropriate access */
807     aflock(nunflshed ? F_WRLCK : F_RDLCK);
808 greg 2.7 /* see if file has grown */
809 greg 2.55 if ((flen = lseek(fileno(ambfp), (off_t)0, SEEK_END)) < 0)
810 greg 2.21 goto seekerr;
811 greg 2.68 if ((n = flen - lastpos) > 0) { /* file has grown */
812 greg 2.104 if (ambinp == NULL) { /* get new file pointer */
813     ambinp = fopen(ambfile, "rb");
814 greg 2.14 if (ambinp == NULL)
815 greg 2.104 error(SYSTEM, "fopen failed in ambsync");
816 greg 2.14 }
817 greg 2.66 if (fseek(ambinp, lastpos, SEEK_SET) < 0)
818 greg 2.21 goto seekerr;
819 greg 2.15 while (n >= AMBVALSIZ) { /* load contributed values */
820 greg 2.37 if (!readambval(&avs, ambinp)) {
821     sprintf(errmsg,
822 greg 2.47 "ambient file \"%s\" corrupted near character %ld",
823     ambfile, flen - n);
824 greg 2.37 error(WARNING, errmsg);
825     break;
826     }
827 greg 2.88 avstore(&avs);
828 greg 2.15 n -= AMBVALSIZ;
829     }
830 greg 2.104 lastpos = flen - n; /* check alignment */
831     if (n && lseek(fileno(ambfp), (off_t)lastpos, SEEK_SET) < 0)
832     goto seekerr;
833 greg 2.7 }
834     n = fflush(ambfp); /* calls write() at last */
835 greg 2.104 lastpos += (long)nunflshed*AMBVALSIZ;
836 greg 2.19 aflock(F_UNLCK); /* release file */
837 greg 2.16 nunflshed = 0;
838 greg 2.7 return(n);
839 greg 2.21 seekerr:
840     error(SYSTEM, "seek failed in ambsync");
841 greg 2.104 return(EOF); /* pro forma return */
842 greg 2.18 }
843    
844 greg 2.71 #else /* ! F_SETLKW */
845 greg 2.18
846 greg 2.71 int
847 schorsch 2.56 ambsync(void) /* flush ambient file */
848 greg 2.18 {
849 greg 2.65 if (ambfp == NULL)
850     return(0);
851 greg 2.18 nunflshed = 0;
852     return(fflush(ambfp));
853 greg 1.1 }
854 greg 2.10
855 greg 2.71 #endif /* ! F_SETLKW */