ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/ambient.c
Revision: 2.80
Committed: Fri Apr 25 18:38:47 2014 UTC (10 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.79: +5 -11 lines
Log Message:
Replaced behind test in sumambient with distanc metric used by Schwarzhaupt

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: ambient.c,v 2.79 2014/04/25 00:21:52 greg Exp $";
3 #endif
4 /*
5 * ambient.c - routines dealing with ambient (inter-reflected) component.
6 *
7 * Declarations of external symbols in ambient.h
8 */
9
10 #include "copyright.h"
11
12 #include <string.h>
13
14 #include "platform.h"
15 #include "ray.h"
16 #include "otypes.h"
17 #include "resolu.h"
18 #include "ambient.h"
19 #include "random.h"
20
21 #ifndef OCTSCALE
22 #define OCTSCALE 1.0 /* ceil((valid rad.)/(cube size)) */
23 #endif
24
25 extern char *shm_boundary; /* memory sharing boundary */
26
27 #ifndef MAXASET
28 #define MAXASET 4095 /* maximum number of elements in ambient set */
29 #endif
30 OBJECT ambset[MAXASET+1]={0}; /* ambient include/exclude set */
31
32 double maxarad; /* maximum ambient radius */
33 double minarad; /* minimum ambient radius */
34
35 static AMBTREE atrunk; /* our ambient trunk node */
36
37 static FILE *ambfp = NULL; /* ambient file pointer */
38 static int nunflshed = 0; /* number of unflushed ambient values */
39
40 #ifndef SORT_THRESH
41 #ifdef SMLMEM
42 #define SORT_THRESH ((16L<<20)/sizeof(AMBVAL))
43 #else
44 #define SORT_THRESH ((64L<<20)/sizeof(AMBVAL))
45 #endif
46 #endif
47 #ifndef SORT_INTVL
48 #define SORT_INTVL (SORT_THRESH<<1)
49 #endif
50 #ifndef MAX_SORT_INTVL
51 #define MAX_SORT_INTVL (SORT_INTVL<<6)
52 #endif
53
54
55 static double qambacc = 0.; /* ambient accuracy to the 1/4 power */
56 static double avsum = 0.; /* computed ambient value sum (log) */
57 static unsigned int navsum = 0; /* number of values in avsum */
58 static unsigned int nambvals = 0; /* total number of indirect values */
59 static unsigned int nambshare = 0; /* number of values from file */
60 static unsigned long ambclock = 0; /* ambient access clock */
61 static unsigned long lastsort = 0; /* time of last value sort */
62 static long sortintvl = SORT_INTVL; /* time until next sort */
63 static FILE *ambinp = NULL; /* auxiliary file for input */
64 static long lastpos = -1; /* last flush position */
65
66 #define MAXACLOCK (1L<<30) /* clock turnover value */
67 /*
68 * Track access times unless we are sharing ambient values
69 * through memory on a multiprocessor, when we want to avoid
70 * claiming our own memory (copy on write). Go ahead anyway
71 * if more than two thirds of our values are unshared.
72 * Compile with -Dtracktime=0 to turn this code off.
73 */
74 #ifndef tracktime
75 #define tracktime (shm_boundary == NULL || nambvals > 3*nambshare)
76 #endif
77
78 #define AMBFLUSH (BUFSIZ/AMBVALSIZ)
79
80 #define newambval() (AMBVAL *)malloc(sizeof(AMBVAL))
81 #define freeav(av) free((void *)av);
82
83 static void initambfile(int creat);
84 static void avsave(AMBVAL *av);
85 static AMBVAL *avstore(AMBVAL *aval);
86 static AMBTREE *newambtree(void);
87 static void freeambtree(AMBTREE *atp);
88
89 typedef void unloadtf_t(AMBVAL *);
90 static unloadtf_t avinsert;
91 static unloadtf_t av2list;
92 static unloadtf_t avfree;
93 static void unloadatree(AMBTREE *at, unloadtf_t *f);
94
95 static int aposcmp(const void *avp1, const void *avp2);
96 static int avlmemi(AMBVAL *avaddr);
97 static void sortambvals(int always);
98
99 #ifdef F_SETLKW
100 static void aflock(int typ);
101 #endif
102
103
104 void
105 setambres( /* set ambient resolution */
106 int ar
107 )
108 {
109 ambres = ar < 0 ? 0 : ar; /* may be done already */
110 /* set min & max radii */
111 if (ar <= 0) {
112 minarad = 0;
113 maxarad = thescene.cusize / 2.0;
114 } else {
115 minarad = thescene.cusize / ar;
116 maxarad = 64 * minarad; /* heuristic */
117 if (maxarad > thescene.cusize / 2.0)
118 maxarad = thescene.cusize / 2.0;
119 }
120 if (minarad <= FTINY)
121 minarad = 10*FTINY;
122 if (maxarad <= minarad)
123 maxarad = 64 * minarad;
124 }
125
126
127 void
128 setambacc( /* set ambient accuracy */
129 double newa
130 )
131 {
132 double ambdiff;
133
134 if (newa < 0.0)
135 newa = 0.0;
136 ambdiff = fabs(newa - ambacc);
137 if (ambdiff >= .01 && (ambacc = newa) > FTINY) {
138 qambacc = sqrt(sqrt(ambacc));
139 if (nambvals > 0)
140 sortambvals(1); /* rebuild tree */
141 }
142 }
143
144
145 void
146 setambient(void) /* initialize calculation */
147 {
148 int readonly = 0;
149 long flen;
150 AMBVAL amb;
151 /* make sure we're fresh */
152 ambdone();
153 /* init ambient limits */
154 setambres(ambres);
155 qambacc = sqrt(sqrt(ambacc *= (ambacc > FTINY)));
156 if (ambfile == NULL || !ambfile[0])
157 return;
158 if (ambacc <= FTINY) {
159 sprintf(errmsg, "zero ambient accuracy so \"%s\" not opened",
160 ambfile);
161 error(WARNING, errmsg);
162 return;
163 }
164 /* open ambient file */
165 if ((ambfp = fopen(ambfile, "r+")) == NULL)
166 readonly = (ambfp = fopen(ambfile, "r")) != NULL;
167 if (ambfp != NULL) {
168 initambfile(0); /* file exists */
169 lastpos = ftell(ambfp);
170 while (readambval(&amb, ambfp))
171 avinsert(avstore(&amb));
172 nambshare = nambvals; /* share loaded values */
173 if (readonly) {
174 sprintf(errmsg,
175 "loaded %u values from read-only ambient file",
176 nambvals);
177 error(WARNING, errmsg);
178 fclose(ambfp); /* close file so no writes */
179 ambfp = NULL;
180 return; /* avoid ambsync() */
181 }
182 /* align file pointer */
183 lastpos += (long)nambvals*AMBVALSIZ;
184 flen = lseek(fileno(ambfp), (off_t)0, SEEK_END);
185 if (flen != lastpos) {
186 sprintf(errmsg,
187 "ignoring last %ld values in ambient file (corrupted)",
188 (flen - lastpos)/AMBVALSIZ);
189 error(WARNING, errmsg);
190 fseek(ambfp, lastpos, SEEK_SET);
191 #ifndef _WIN32 /* XXX we need a replacement for that one */
192 ftruncate(fileno(ambfp), (off_t)lastpos);
193 #endif
194 }
195 } else if ((ambfp = fopen(ambfile, "w+")) != NULL) {
196 initambfile(1); /* else create new file */
197 fflush(ambfp);
198 lastpos = ftell(ambfp);
199 } else {
200 sprintf(errmsg, "cannot open ambient file \"%s\"", ambfile);
201 error(SYSTEM, errmsg);
202 }
203 #ifdef getc_unlocked
204 flockfile(ambfp); /* application-level lock */
205 #endif
206 #ifdef F_SETLKW
207 aflock(F_UNLCK); /* release file */
208 #endif
209 }
210
211
212 void
213 ambdone(void) /* close ambient file and free memory */
214 {
215 if (ambfp != NULL) { /* close ambient file */
216 ambsync();
217 fclose(ambfp);
218 ambfp = NULL;
219 if (ambinp != NULL) {
220 fclose(ambinp);
221 ambinp = NULL;
222 }
223 lastpos = -1;
224 }
225 /* free ambient tree */
226 unloadatree(&atrunk, &avfree);
227 /* reset state variables */
228 avsum = 0.;
229 navsum = 0;
230 nambvals = 0;
231 nambshare = 0;
232 ambclock = 0;
233 lastsort = 0;
234 sortintvl = SORT_INTVL;
235 }
236
237
238 void
239 ambnotify( /* record new modifier */
240 OBJECT obj
241 )
242 {
243 static int hitlimit = 0;
244 OBJREC *o;
245 char **amblp;
246
247 if (obj == OVOID) { /* starting over */
248 ambset[0] = 0;
249 hitlimit = 0;
250 return;
251 }
252 o = objptr(obj);
253 if (hitlimit || !ismodifier(o->otype))
254 return;
255 for (amblp = amblist; *amblp != NULL; amblp++)
256 if (!strcmp(o->oname, *amblp)) {
257 if (ambset[0] >= MAXASET) {
258 error(WARNING, "too many modifiers in ambient list");
259 hitlimit++;
260 return; /* should this be fatal? */
261 }
262 insertelem(ambset, obj);
263 return;
264 }
265 }
266
267 /************ THE FOLLOWING ROUTINES DIFFER BETWEEN NEW & OLD ***************/
268
269 #ifdef NEWAMB
270
271 #define tfunc(lwr, x, upr) (((x)-(lwr))/((upr)-(lwr)))
272
273 static double sumambient(COLOR acol, RAY *r, FVECT rn, int al,
274 AMBTREE *at, FVECT c0, double s);
275 static int makeambient(COLOR acol, RAY *r, FVECT rn, int al);
276 static void extambient(COLOR cr, AMBVAL *ap, FVECT pv, FVECT nv,
277 FVECT uvw[3]);
278
279 void
280 multambient( /* compute ambient component & multiply by coef. */
281 COLOR aval,
282 RAY *r,
283 FVECT nrm
284 )
285 {
286 static int rdepth = 0; /* ambient recursion */
287 COLOR acol;
288 int ok;
289 double d, l;
290
291 if (ambdiv <= 0) /* no ambient calculation */
292 goto dumbamb;
293 /* check number of bounces */
294 if (rdepth >= ambounce)
295 goto dumbamb;
296 /* check ambient list */
297 if (ambincl != -1 && r->ro != NULL &&
298 ambincl != inset(ambset, r->ro->omod))
299 goto dumbamb;
300
301 if (ambacc <= FTINY) { /* no ambient storage */
302 copycolor(acol, aval);
303 rdepth++;
304 ok = doambient(acol, r, r->rweight, NULL, NULL, NULL, NULL);
305 rdepth--;
306 if (!ok)
307 goto dumbamb;
308 copycolor(aval, acol);
309 return;
310 }
311
312 if (tracktime) /* sort to minimize thrashing */
313 sortambvals(0);
314 /* interpolate ambient value */
315 setcolor(acol, 0.0, 0.0, 0.0);
316 d = sumambient(acol, r, nrm, rdepth,
317 &atrunk, thescene.cuorg, thescene.cusize);
318 if (d > FTINY) {
319 d = 1.0/d;
320 scalecolor(acol, d);
321 multcolor(aval, acol);
322 return;
323 }
324 rdepth++; /* need to cache new value */
325 ok = makeambient(acol, r, nrm, rdepth-1);
326 rdepth--;
327 if (ok) {
328 multcolor(aval, acol); /* computed new value */
329 return;
330 }
331 dumbamb: /* return global value */
332 if ((ambvwt <= 0) | (navsum == 0)) {
333 multcolor(aval, ambval);
334 return;
335 }
336 l = bright(ambval); /* average in computations */
337 if (l > FTINY) {
338 d = (log(l)*(double)ambvwt + avsum) /
339 (double)(ambvwt + navsum);
340 d = exp(d) / l;
341 scalecolor(aval, d);
342 multcolor(aval, ambval); /* apply color of ambval */
343 } else {
344 d = exp( avsum / (double)navsum );
345 scalecolor(aval, d); /* neutral color */
346 }
347 }
348
349
350 double
351 sumambient( /* get interpolated ambient value */
352 COLOR acol,
353 RAY *r,
354 FVECT rn,
355 int al,
356 AMBTREE *at,
357 FVECT c0,
358 double s
359 )
360 { /* initial limit is 10 degrees plus ambacc radians */
361 const double minangle = 10.0 * PI/180.;
362 const double maxangle = (minangle+ambacc-PI/2.)*pow(r->rweight,0.13)
363 + PI/2.;
364 double wsum = 0.0;
365 FVECT ck0;
366 int i, j;
367 AMBVAL *av;
368 /* sum this node */
369 for (av = at->alist; av != NULL; av = av->next) {
370 double d, delta_r2, delta_t2;
371 COLOR ct;
372 FVECT uvw[3];
373 /* record access */
374 if (tracktime)
375 av->latick = ambclock;
376 /*
377 * Ambient level test
378 */
379 if (av->lvl > al) /* list sorted, so this works */
380 break;
381 if (av->weight < 0.9*r->rweight)
382 continue;
383 /*
384 * Direction test using unperturbed normal
385 */
386 decodedir(uvw[2], av->ndir);
387 d = DOT(uvw[2], r->ron);
388 if (d <= 0.0) /* >= 90 degrees */
389 continue;
390 delta_r2 = 2.0 - 2.0*d; /* approx. radians^2 */
391 if (delta_r2 >= maxangle*maxangle)
392 continue;
393 /*
394 * Elliptical radii test based on Hessian
395 */
396 decodedir(uvw[0], av->udir);
397 VCROSS(uvw[1], uvw[2], uvw[0]);
398 VSUB(ck0, av->pos, r->rop);
399 d = DOT(ck0, uvw[0]) / av->rad[0];
400 delta_t2 = d*d;
401 d = DOT(ck0, uvw[1]) / av->rad[1];
402 delta_t2 += d*d;
403 d = DOT(ck0, uvw[2]) / av->rad[0];
404 delta_t2 += d*d;
405 if (delta_t2 >= qambacc*qambacc)
406 continue;
407 /*
408 * Extrapolate value and compute final weight (hat function)
409 */
410 extambient(ct, av, r->rop, rn, uvw);
411 d = tfunc(maxangle, sqrt(delta_r2), 0.0) *
412 tfunc(qambacc, sqrt(delta_t2), 0.0);
413 scalecolor(ct, d);
414 addcolor(acol, ct);
415 wsum += d;
416 }
417 if (at->kid == NULL)
418 return(wsum);
419 /* sum children */
420 s *= 0.5;
421 for (i = 0; i < 8; i++) {
422 for (j = 0; j < 3; j++) {
423 ck0[j] = c0[j];
424 if (1<<j & i)
425 ck0[j] += s;
426 if (r->rop[j] < ck0[j] - OCTSCALE*s)
427 break;
428 if (r->rop[j] > ck0[j] + (1.0+OCTSCALE)*s)
429 break;
430 }
431 if (j == 3)
432 wsum += sumambient(acol, r, rn, al,
433 at->kid+i, ck0, s);
434 }
435 return(wsum);
436 }
437
438
439 int
440 makeambient( /* make a new ambient value for storage */
441 COLOR acol,
442 RAY *r,
443 FVECT rn,
444 int al
445 )
446 {
447 AMBVAL amb;
448 FVECT uvw[3];
449 int i;
450
451 amb.weight = 1.0; /* compute weight */
452 for (i = al; i-- > 0; )
453 amb.weight *= AVGREFL;
454 if (r->rweight < 0.1*amb.weight) /* heuristic override */
455 amb.weight = 1.25*r->rweight;
456 setcolor(acol, AVGREFL, AVGREFL, AVGREFL);
457 /* compute ambient */
458 i = doambient(acol, r, amb.weight, uvw, amb.rad, amb.gpos, amb.gdir);
459 scalecolor(acol, 1./AVGREFL); /* undo assumed reflectance */
460 if (i <= 0 || amb.rad[0] <= FTINY) /* no Hessian or zero radius */
461 return(i);
462 /* store value */
463 VCOPY(amb.pos, r->rop);
464 amb.ndir = encodedir(r->ron);
465 amb.udir = encodedir(uvw[0]);
466 amb.lvl = al;
467 copycolor(amb.val, acol);
468 /* insert into tree */
469 avsave(&amb); /* and save to file */
470 if (rn != r->ron) { /* texture */
471 VCOPY(uvw[2], r->ron);
472 extambient(acol, &amb, r->rop, rn, uvw);
473 }
474 return(1);
475 }
476
477
478 void
479 extambient( /* extrapolate value at pv, nv */
480 COLOR cr,
481 AMBVAL *ap,
482 FVECT pv,
483 FVECT nv,
484 FVECT uvw[3]
485 )
486 {
487 static FVECT my_uvw[3];
488 FVECT v1;
489 int i;
490 double d = 1.0; /* zeroeth order */
491
492 if (uvw == NULL) { /* need local coordinates? */
493 decodedir(my_uvw[2], ap->ndir);
494 decodedir(my_uvw[0], ap->udir);
495 VCROSS(my_uvw[1], my_uvw[2], my_uvw[0]);
496 uvw = my_uvw;
497 }
498 for (i = 3; i--; ) /* gradient due to translation */
499 d += (pv[i] - ap->pos[i]) *
500 (ap->gpos[0]*uvw[0][i] + ap->gpos[1]*uvw[1][i]);
501
502 VCROSS(v1, uvw[2], nv); /* gradient due to rotation */
503 for (i = 3; i--; )
504 d += v1[i] * (ap->gdir[0]*uvw[0][i] + ap->gdir[1]*uvw[1][i]);
505
506 if (d <= 0.0) {
507 setcolor(cr, 0.0, 0.0, 0.0);
508 return;
509 }
510 copycolor(cr, ap->val);
511 scalecolor(cr, d);
512 }
513
514
515 static void
516 avinsert( /* insert ambient value in our tree */
517 AMBVAL *av
518 )
519 {
520 AMBTREE *at;
521 AMBVAL *ap;
522 AMBVAL avh;
523 FVECT ck0;
524 double s;
525 int branch;
526 int i;
527
528 if (av->rad[0] <= FTINY)
529 error(CONSISTENCY, "zero ambient radius in avinsert");
530 at = &atrunk;
531 VCOPY(ck0, thescene.cuorg);
532 s = thescene.cusize;
533 while (s*(OCTSCALE/2) > av->rad[1]*qambacc) {
534 if (at->kid == NULL)
535 if ((at->kid = newambtree()) == NULL)
536 error(SYSTEM, "out of memory in avinsert");
537 s *= 0.5;
538 branch = 0;
539 for (i = 0; i < 3; i++)
540 if (av->pos[i] > ck0[i] + s) {
541 ck0[i] += s;
542 branch |= 1 << i;
543 }
544 at = at->kid + branch;
545 }
546 avh.next = at->alist; /* order by increasing level */
547 for (ap = &avh; ap->next != NULL; ap = ap->next)
548 if (ap->next->lvl >= av->lvl)
549 break;
550 av->next = ap->next;
551 ap->next = (AMBVAL*)av;
552 at->alist = avh.next;
553 }
554
555
556 #else /* ! NEWAMB */
557
558 static double sumambient(COLOR acol, RAY *r, FVECT rn, int al,
559 AMBTREE *at, FVECT c0, double s);
560 static double makeambient(COLOR acol, RAY *r, FVECT rn, int al);
561 static void extambient(COLOR cr, AMBVAL *ap, FVECT pv, FVECT nv);
562
563
564 void
565 multambient( /* compute ambient component & multiply by coef. */
566 COLOR aval,
567 RAY *r,
568 FVECT nrm
569 )
570 {
571 static int rdepth = 0; /* ambient recursion */
572 COLOR acol;
573 double d, l;
574
575 if (ambdiv <= 0) /* no ambient calculation */
576 goto dumbamb;
577 /* check number of bounces */
578 if (rdepth >= ambounce)
579 goto dumbamb;
580 /* check ambient list */
581 if (ambincl != -1 && r->ro != NULL &&
582 ambincl != inset(ambset, r->ro->omod))
583 goto dumbamb;
584
585 if (ambacc <= FTINY) { /* no ambient storage */
586 copycolor(acol, aval);
587 rdepth++;
588 d = doambient(acol, r, r->rweight, NULL, NULL);
589 rdepth--;
590 if (d <= FTINY)
591 goto dumbamb;
592 copycolor(aval, acol);
593 return;
594 }
595
596 if (tracktime) /* sort to minimize thrashing */
597 sortambvals(0);
598 /* interpolate ambient value */
599 setcolor(acol, 0.0, 0.0, 0.0);
600 d = sumambient(acol, r, nrm, rdepth,
601 &atrunk, thescene.cuorg, thescene.cusize);
602 if (d > FTINY) {
603 d = 1.0/d;
604 scalecolor(acol, d);
605 multcolor(aval, acol);
606 return;
607 }
608 rdepth++; /* need to cache new value */
609 d = makeambient(acol, r, nrm, rdepth-1);
610 rdepth--;
611 if (d > FTINY) {
612 multcolor(aval, acol); /* got new value */
613 return;
614 }
615 dumbamb: /* return global value */
616 if ((ambvwt <= 0) | (navsum == 0)) {
617 multcolor(aval, ambval);
618 return;
619 }
620 l = bright(ambval); /* average in computations */
621 if (l > FTINY) {
622 d = (log(l)*(double)ambvwt + avsum) /
623 (double)(ambvwt + navsum);
624 d = exp(d) / l;
625 scalecolor(aval, d);
626 multcolor(aval, ambval); /* apply color of ambval */
627 } else {
628 d = exp( avsum / (double)navsum );
629 scalecolor(aval, d); /* neutral color */
630 }
631 }
632
633
634 static double
635 sumambient( /* get interpolated ambient value */
636 COLOR acol,
637 RAY *r,
638 FVECT rn,
639 int al,
640 AMBTREE *at,
641 FVECT c0,
642 double s
643 )
644 {
645 double d, e1, e2, wt, wsum;
646 COLOR ct;
647 FVECT ck0;
648 int i;
649 int j;
650 AMBVAL *av;
651
652 wsum = 0.0;
653 /* do this node */
654 for (av = at->alist; av != NULL; av = av->next) {
655 double rn_dot = -2.0;
656 if (tracktime)
657 av->latick = ambclock;
658 /*
659 * Ambient level test.
660 */
661 if (av->lvl > al) /* list sorted, so this works */
662 break;
663 if (av->weight < 0.9*r->rweight)
664 continue;
665 /*
666 * Ambient radius test.
667 */
668 VSUB(ck0, av->pos, r->rop);
669 e1 = DOT(ck0, ck0) / (av->rad * av->rad);
670 if (e1 > ambacc*ambacc*1.21)
671 continue;
672 /*
673 * Direction test using closest normal.
674 */
675 d = DOT(av->dir, r->ron);
676 if (rn != r->ron) {
677 rn_dot = DOT(av->dir, rn);
678 if (rn_dot > 1.0-FTINY)
679 rn_dot = 1.0-FTINY;
680 if (rn_dot >= d-FTINY) {
681 d = rn_dot;
682 rn_dot = -2.0;
683 }
684 }
685 e2 = (1.0 - d) * r->rweight;
686 if (e2 < 0.0)
687 e2 = 0.0;
688 else if (e1 + e2 > ambacc*ambacc*1.21)
689 continue;
690 /*
691 * Ray behind test.
692 */
693 d = 0.0;
694 for (j = 0; j < 3; j++)
695 d += (r->rop[j] - av->pos[j]) *
696 (av->dir[j] + r->ron[j]);
697 if (d*0.5 < -minarad*ambacc-.001)
698 continue;
699 /*
700 * Jittering final test reduces image artifacts.
701 */
702 e1 = sqrt(e1);
703 e2 = sqrt(e2);
704 wt = e1 + e2;
705 if (wt > ambacc*(.9+.2*urand(9015+samplendx)))
706 continue;
707 /*
708 * Recompute directional error using perturbed normal
709 */
710 if (rn_dot > 0.0) {
711 e2 = sqrt((1.0 - rn_dot)*r->rweight);
712 wt = e1 + e2;
713 }
714 if (wt <= 1e-3)
715 wt = 1e3;
716 else
717 wt = 1.0 / wt;
718 wsum += wt;
719 extambient(ct, av, r->rop, rn);
720 scalecolor(ct, wt);
721 addcolor(acol, ct);
722 }
723 if (at->kid == NULL)
724 return(wsum);
725 /* do children */
726 s *= 0.5;
727 for (i = 0; i < 8; i++) {
728 for (j = 0; j < 3; j++) {
729 ck0[j] = c0[j];
730 if (1<<j & i)
731 ck0[j] += s;
732 if (r->rop[j] < ck0[j] - OCTSCALE*s)
733 break;
734 if (r->rop[j] > ck0[j] + (1.0+OCTSCALE)*s)
735 break;
736 }
737 if (j == 3)
738 wsum += sumambient(acol, r, rn, al,
739 at->kid+i, ck0, s);
740 }
741 return(wsum);
742 }
743
744
745 static double
746 makeambient( /* make a new ambient value for storage */
747 COLOR acol,
748 RAY *r,
749 FVECT rn,
750 int al
751 )
752 {
753 AMBVAL amb;
754 FVECT gp, gd;
755 int i;
756
757 amb.weight = 1.0; /* compute weight */
758 for (i = al; i-- > 0; )
759 amb.weight *= AVGREFL;
760 if (r->rweight < 0.1*amb.weight) /* heuristic override */
761 amb.weight = 1.25*r->rweight;
762 setcolor(acol, AVGREFL, AVGREFL, AVGREFL);
763 /* compute ambient */
764 amb.rad = doambient(acol, r, amb.weight, gp, gd);
765 if (amb.rad <= FTINY) {
766 setcolor(acol, 0.0, 0.0, 0.0);
767 return(0.0);
768 }
769 scalecolor(acol, 1./AVGREFL); /* undo assumed reflectance */
770 /* store value */
771 VCOPY(amb.pos, r->rop);
772 VCOPY(amb.dir, r->ron);
773 amb.lvl = al;
774 copycolor(amb.val, acol);
775 VCOPY(amb.gpos, gp);
776 VCOPY(amb.gdir, gd);
777 /* insert into tree */
778 avsave(&amb); /* and save to file */
779 if (rn != r->ron)
780 extambient(acol, &amb, r->rop, rn); /* texture */
781 return(amb.rad);
782 }
783
784
785 static void
786 extambient( /* extrapolate value at pv, nv */
787 COLOR cr,
788 AMBVAL *ap,
789 FVECT pv,
790 FVECT nv
791 )
792 {
793 FVECT v1;
794 int i;
795 double d;
796
797 d = 1.0; /* zeroeth order */
798 /* gradient due to translation */
799 for (i = 0; i < 3; i++)
800 d += ap->gpos[i]*(pv[i]-ap->pos[i]);
801 /* gradient due to rotation */
802 VCROSS(v1, ap->dir, nv);
803 d += DOT(ap->gdir, v1);
804 if (d <= 0.0) {
805 setcolor(cr, 0.0, 0.0, 0.0);
806 return;
807 }
808 copycolor(cr, ap->val);
809 scalecolor(cr, d);
810 }
811
812
813 static void
814 avinsert( /* insert ambient value in our tree */
815 AMBVAL *av
816 )
817 {
818 AMBTREE *at;
819 AMBVAL *ap;
820 AMBVAL avh;
821 FVECT ck0;
822 double s;
823 int branch;
824 int i;
825
826 if (av->rad <= FTINY)
827 error(CONSISTENCY, "zero ambient radius in avinsert");
828 at = &atrunk;
829 VCOPY(ck0, thescene.cuorg);
830 s = thescene.cusize;
831 while (s*(OCTSCALE/2) > av->rad*ambacc) {
832 if (at->kid == NULL)
833 if ((at->kid = newambtree()) == NULL)
834 error(SYSTEM, "out of memory in avinsert");
835 s *= 0.5;
836 branch = 0;
837 for (i = 0; i < 3; i++)
838 if (av->pos[i] > ck0[i] + s) {
839 ck0[i] += s;
840 branch |= 1 << i;
841 }
842 at = at->kid + branch;
843 }
844 avh.next = at->alist; /* order by increasing level */
845 for (ap = &avh; ap->next != NULL; ap = ap->next)
846 if (ap->next->lvl >= av->lvl)
847 break;
848 av->next = ap->next;
849 ap->next = (AMBVAL*)av;
850 at->alist = avh.next;
851 }
852
853 #endif /* ! NEWAMB */
854
855 /************* FOLLOWING ROUTINES SAME FOR NEW & OLD METHODS ***************/
856
857 static void
858 initambfile( /* initialize ambient file */
859 int cre8
860 )
861 {
862 extern char *progname, *octname;
863 static char *mybuf = NULL;
864
865 #ifdef F_SETLKW
866 aflock(cre8 ? F_WRLCK : F_RDLCK);
867 #endif
868 SET_FILE_BINARY(ambfp);
869 if (mybuf == NULL)
870 mybuf = (char *)bmalloc(BUFSIZ+8);
871 setbuf(ambfp, mybuf);
872 if (cre8) { /* new file */
873 newheader("RADIANCE", ambfp);
874 fprintf(ambfp, "%s -av %g %g %g -aw %d -ab %d -aa %g ",
875 progname, colval(ambval,RED),
876 colval(ambval,GRN), colval(ambval,BLU),
877 ambvwt, ambounce, ambacc);
878 fprintf(ambfp, "-ad %d -as %d -ar %d ",
879 ambdiv, ambssamp, ambres);
880 if (octname != NULL)
881 fputs(octname, ambfp);
882 fputc('\n', ambfp);
883 fprintf(ambfp, "SOFTWARE= %s\n", VersionID);
884 fputnow(ambfp);
885 fputformat(AMBFMT, ambfp);
886 fputc('\n', ambfp);
887 putambmagic(ambfp);
888 } else if (checkheader(ambfp, AMBFMT, NULL) < 0 || !hasambmagic(ambfp))
889 error(USER, "bad ambient file");
890 }
891
892
893 static void
894 avsave( /* insert and save an ambient value */
895 AMBVAL *av
896 )
897 {
898 avinsert(avstore(av));
899 if (ambfp == NULL)
900 return;
901 if (writambval(av, ambfp) < 0)
902 goto writerr;
903 if (++nunflshed >= AMBFLUSH)
904 if (ambsync() == EOF)
905 goto writerr;
906 return;
907 writerr:
908 error(SYSTEM, "error writing to ambient file");
909 }
910
911
912 static AMBVAL *
913 avstore( /* allocate memory and store aval */
914 AMBVAL *aval
915 )
916 {
917 AMBVAL *av;
918 double d;
919
920 if ((av = newambval()) == NULL)
921 error(SYSTEM, "out of memory in avstore");
922 *av = *aval;
923 av->latick = ambclock;
924 av->next = NULL;
925 nambvals++;
926 d = bright(av->val);
927 if (d > FTINY) { /* add to log sum for averaging */
928 avsum += log(d);
929 navsum++;
930 }
931 return(av);
932 }
933
934
935 #define ATALLOCSZ 512 /* #/8 trees to allocate at once */
936
937 static AMBTREE *atfreelist = NULL; /* free ambient tree structures */
938
939
940 static AMBTREE *
941 newambtree(void) /* allocate 8 ambient tree structs */
942 {
943 AMBTREE *atp, *upperlim;
944
945 if (atfreelist == NULL) { /* get more nodes */
946 atfreelist = (AMBTREE *)malloc(ATALLOCSZ*8*sizeof(AMBTREE));
947 if (atfreelist == NULL)
948 return(NULL);
949 /* link new free list */
950 upperlim = atfreelist + 8*(ATALLOCSZ-1);
951 for (atp = atfreelist; atp < upperlim; atp += 8)
952 atp->kid = atp + 8;
953 atp->kid = NULL;
954 }
955 atp = atfreelist;
956 atfreelist = atp->kid;
957 memset((char *)atp, '\0', 8*sizeof(AMBTREE));
958 return(atp);
959 }
960
961
962 static void
963 freeambtree( /* free 8 ambient tree structs */
964 AMBTREE *atp
965 )
966 {
967 atp->kid = atfreelist;
968 atfreelist = atp;
969 }
970
971
972 static void
973 unloadatree( /* unload an ambient value tree */
974 AMBTREE *at,
975 unloadtf_t *f
976 )
977 {
978 AMBVAL *av;
979 int i;
980 /* transfer values at this node */
981 for (av = at->alist; av != NULL; av = at->alist) {
982 at->alist = av->next;
983 (*f)(av);
984 }
985 if (at->kid == NULL)
986 return;
987 for (i = 0; i < 8; i++) /* transfer and free children */
988 unloadatree(at->kid+i, f);
989 freeambtree(at->kid);
990 at->kid = NULL;
991 }
992
993
994 static struct avl {
995 AMBVAL *p;
996 unsigned long t;
997 } *avlist1; /* ambient value list with ticks */
998 static AMBVAL **avlist2; /* memory positions for sorting */
999 static int i_avlist; /* index for lists */
1000
1001 static int alatcmp(const void *av1, const void *av2);
1002
1003 static void
1004 avfree(AMBVAL *av)
1005 {
1006 free(av);
1007 }
1008
1009 static void
1010 av2list(
1011 AMBVAL *av
1012 )
1013 {
1014 #ifdef DEBUG
1015 if (i_avlist >= nambvals)
1016 error(CONSISTENCY, "too many ambient values in av2list1");
1017 #endif
1018 avlist1[i_avlist].p = avlist2[i_avlist] = (AMBVAL*)av;
1019 avlist1[i_avlist++].t = av->latick;
1020 }
1021
1022
1023 static int
1024 alatcmp( /* compare ambient values for MRA */
1025 const void *av1,
1026 const void *av2
1027 )
1028 {
1029 long lc = ((struct avl *)av2)->t - ((struct avl *)av1)->t;
1030 return(lc<0 ? -1 : lc>0 ? 1 : 0);
1031 }
1032
1033
1034 /* GW NOTE 2002/10/3:
1035 * I used to compare AMBVAL pointers, but found that this was the
1036 * cause of a serious consistency error with gcc, since the optimizer
1037 * uses some dangerous trick in pointer subtraction that
1038 * assumes pointers differ by exact struct size increments.
1039 */
1040 static int
1041 aposcmp( /* compare ambient value positions */
1042 const void *avp1,
1043 const void *avp2
1044 )
1045 {
1046 long diff = *(char * const *)avp1 - *(char * const *)avp2;
1047 if (diff < 0)
1048 return(-1);
1049 return(diff > 0);
1050 }
1051
1052
1053 static int
1054 avlmemi( /* find list position from address */
1055 AMBVAL *avaddr
1056 )
1057 {
1058 AMBVAL **avlpp;
1059
1060 avlpp = (AMBVAL **)bsearch((char *)&avaddr, (char *)avlist2,
1061 nambvals, sizeof(AMBVAL *), &aposcmp);
1062 if (avlpp == NULL)
1063 error(CONSISTENCY, "address not found in avlmemi");
1064 return(avlpp - avlist2);
1065 }
1066
1067
1068 static void
1069 sortambvals( /* resort ambient values */
1070 int always
1071 )
1072 {
1073 AMBTREE oldatrunk;
1074 AMBVAL tav, *tap, *pnext;
1075 int i, j;
1076 /* see if it's time yet */
1077 if (!always && (ambclock++ < lastsort+sortintvl ||
1078 nambvals < SORT_THRESH))
1079 return;
1080 /*
1081 * The idea here is to minimize memory thrashing
1082 * in VM systems by improving reference locality.
1083 * We do this by periodically sorting our stored ambient
1084 * values in memory in order of most recently to least
1085 * recently accessed. This ordering was chosen so that new
1086 * ambient values (which tend to be less important) go into
1087 * higher memory with the infrequently accessed values.
1088 * Since we expect our values to need sorting less
1089 * frequently as the process continues, we double our
1090 * waiting interval after each call.
1091 * This routine is also called by setambacc() with
1092 * the "always" parameter set to 1 so that the ambient
1093 * tree will be rebuilt with the new accuracy parameter.
1094 */
1095 if (tracktime) { /* allocate pointer arrays to sort */
1096 avlist2 = (AMBVAL **)malloc(nambvals*sizeof(AMBVAL *));
1097 avlist1 = (struct avl *)malloc(nambvals*sizeof(struct avl));
1098 } else {
1099 avlist2 = NULL;
1100 avlist1 = NULL;
1101 }
1102 if (avlist1 == NULL) { /* no time tracking -- rebuild tree? */
1103 if (avlist2 != NULL)
1104 free((void *)avlist2);
1105 if (always) { /* rebuild without sorting */
1106 oldatrunk = atrunk;
1107 atrunk.alist = NULL;
1108 atrunk.kid = NULL;
1109 unloadatree(&oldatrunk, &avinsert);
1110 }
1111 } else { /* sort memory by last access time */
1112 /*
1113 * Sorting memory is tricky because it isn't contiguous.
1114 * We have to sort an array of pointers by MRA and also
1115 * by memory position. We then copy values in "loops"
1116 * to minimize memory hits. Nevertheless, we will visit
1117 * everyone at least twice, and this is an expensive process
1118 * when we're thrashing, which is when we need to do it.
1119 */
1120 #ifdef DEBUG
1121 sprintf(errmsg, "sorting %u ambient values at ambclock=%lu...",
1122 nambvals, ambclock);
1123 eputs(errmsg);
1124 #endif
1125 i_avlist = 0;
1126 unloadatree(&atrunk, &av2list); /* empty current tree */
1127 #ifdef DEBUG
1128 if (i_avlist < nambvals)
1129 error(CONSISTENCY, "missing ambient values in sortambvals");
1130 #endif
1131 qsort((char *)avlist1, nambvals, sizeof(struct avl), &alatcmp);
1132 qsort((char *)avlist2, nambvals, sizeof(AMBVAL *), &aposcmp);
1133 for (i = 0; i < nambvals; i++) {
1134 if (avlist1[i].p == NULL)
1135 continue;
1136 tap = avlist2[i];
1137 tav = *tap;
1138 for (j = i; (pnext = avlist1[j].p) != tap;
1139 j = avlmemi(pnext)) {
1140 *(avlist2[j]) = *pnext;
1141 avinsert(avlist2[j]);
1142 avlist1[j].p = NULL;
1143 }
1144 *(avlist2[j]) = tav;
1145 avinsert(avlist2[j]);
1146 avlist1[j].p = NULL;
1147 }
1148 free((void *)avlist1);
1149 free((void *)avlist2);
1150 /* compute new sort interval */
1151 sortintvl = ambclock - lastsort;
1152 if (sortintvl >= MAX_SORT_INTVL/2)
1153 sortintvl = MAX_SORT_INTVL;
1154 else
1155 sortintvl <<= 1; /* wait twice as long next */
1156 #ifdef DEBUG
1157 eputs("done\n");
1158 #endif
1159 }
1160 if (ambclock >= MAXACLOCK)
1161 ambclock = MAXACLOCK/2;
1162 lastsort = ambclock;
1163 }
1164
1165
1166 #ifdef F_SETLKW
1167
1168 static void
1169 aflock( /* lock/unlock ambient file */
1170 int typ
1171 )
1172 {
1173 static struct flock fls; /* static so initialized to zeroes */
1174
1175 if (typ == fls.l_type) /* already called? */
1176 return;
1177 fls.l_type = typ;
1178 if (fcntl(fileno(ambfp), F_SETLKW, &fls) < 0)
1179 error(SYSTEM, "cannot (un)lock ambient file");
1180 }
1181
1182
1183 int
1184 ambsync(void) /* synchronize ambient file */
1185 {
1186 long flen;
1187 AMBVAL avs;
1188 int n;
1189
1190 if (ambfp == NULL) /* no ambient file? */
1191 return(0);
1192 /* gain appropriate access */
1193 aflock(nunflshed ? F_WRLCK : F_RDLCK);
1194 /* see if file has grown */
1195 if ((flen = lseek(fileno(ambfp), (off_t)0, SEEK_END)) < 0)
1196 goto seekerr;
1197 if ((n = flen - lastpos) > 0) { /* file has grown */
1198 if (ambinp == NULL) { /* use duplicate filedes */
1199 ambinp = fdopen(dup(fileno(ambfp)), "r");
1200 if (ambinp == NULL)
1201 error(SYSTEM, "fdopen failed in ambsync");
1202 }
1203 if (fseek(ambinp, lastpos, SEEK_SET) < 0)
1204 goto seekerr;
1205 while (n >= AMBVALSIZ) { /* load contributed values */
1206 if (!readambval(&avs, ambinp)) {
1207 sprintf(errmsg,
1208 "ambient file \"%s\" corrupted near character %ld",
1209 ambfile, flen - n);
1210 error(WARNING, errmsg);
1211 break;
1212 }
1213 avinsert(avstore(&avs));
1214 n -= AMBVALSIZ;
1215 }
1216 lastpos = flen - n;
1217 /*** seek always as safety measure
1218 if (n) ***/ /* alignment */
1219 if (lseek(fileno(ambfp), (off_t)lastpos, SEEK_SET) < 0)
1220 goto seekerr;
1221 }
1222 n = fflush(ambfp); /* calls write() at last */
1223 if (n != EOF)
1224 lastpos += (long)nunflshed*AMBVALSIZ;
1225 else if ((lastpos = lseek(fileno(ambfp), (off_t)0, SEEK_CUR)) < 0)
1226 goto seekerr;
1227
1228 aflock(F_UNLCK); /* release file */
1229 nunflshed = 0;
1230 return(n);
1231 seekerr:
1232 error(SYSTEM, "seek failed in ambsync");
1233 return -1; /* pro forma return */
1234 }
1235
1236 #else /* ! F_SETLKW */
1237
1238 int
1239 ambsync(void) /* flush ambient file */
1240 {
1241 if (ambfp == NULL)
1242 return(0);
1243 nunflshed = 0;
1244 return(fflush(ambfp));
1245 }
1246
1247 #endif /* ! F_SETLKW */