ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/ambient.c
Revision: 2.76
Committed: Wed Apr 23 06:04:18 2014 UTC (10 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.75: +2 -2 lines
Log Message:
Fixed numerous errors in Hessian (-DNEWAMB) calculation, doubtless more to go

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: ambient.c,v 2.75 2014/04/19 19:20:47 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 setambacc(ambacc);
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 ambacc radians */
361 const double maxangle = (ambacc-PI/2.)*pow(r->rweight,0.13) + PI/2.;
362 double wsum = 0.0;
363 FVECT ck0;
364 int i, j;
365 AMBVAL *av;
366 /* sum this node */
367 for (av = at->alist; av != NULL; av = av->next) {
368 double d, delta_r2, delta_t2;
369 COLOR ct;
370 FVECT uvw[3];
371 /* record access */
372 if (tracktime)
373 av->latick = ambclock;
374 /*
375 * Ambient level test
376 */
377 if (av->lvl > al) /* list sorted, so this works */
378 break;
379 if (av->weight < 0.9*r->rweight)
380 continue;
381 /*
382 * Direction test using unperturbed normal
383 */
384 decodedir(uvw[2], av->ndir);
385 d = DOT(uvw[2], r->ron);
386 if (d <= 0.0) /* >= 90 degrees */
387 continue;
388 delta_r2 = 2.0 - 2.0*d; /* approx. radians^2 */
389 if (delta_r2 >= maxangle*maxangle)
390 continue;
391 /*
392 * Elliptical radii test based on Hessian
393 */
394 decodedir(uvw[0], av->udir);
395 VCROSS(uvw[1], uvw[2], uvw[0]);
396 VSUB(ck0, av->pos, r->rop);
397 d = DOT(ck0, uvw[0]) / av->rad[0];
398 delta_t2 = d*d;
399 d = DOT(ck0, uvw[1]) / av->rad[1];
400 delta_t2 += d*d;
401 if (delta_t2 >= qambacc*qambacc)
402 continue;
403 /*
404 * Intersection behind test
405 */
406 d = 0.0;
407 for (j = 0; j < 3; j++)
408 d += (r->rop[j] - av->pos[j])*(uvw[2][j] + r->ron[j]);
409 if (d*0.5 < -minarad*ambacc-.001)
410 continue;
411 /*
412 * Extrapolate value and compute final weight (hat function)
413 */
414 extambient(ct, av, r->rop, rn, uvw);
415 d = tfunc(maxangle, sqrt(delta_r2), 0.0) *
416 tfunc(qambacc, sqrt(delta_t2), 0.0);
417 scalecolor(ct, d);
418 addcolor(acol, ct);
419 wsum += d;
420 }
421 if (at->kid == NULL)
422 return(wsum);
423 /* sum children */
424 s *= 0.5;
425 for (i = 0; i < 8; i++) {
426 for (j = 0; j < 3; j++) {
427 ck0[j] = c0[j];
428 if (1<<j & i)
429 ck0[j] += s;
430 if (r->rop[j] < ck0[j] - OCTSCALE*s)
431 break;
432 if (r->rop[j] > ck0[j] + (1.0+OCTSCALE)*s)
433 break;
434 }
435 if (j == 3)
436 wsum += sumambient(acol, r, rn, al,
437 at->kid+i, ck0, s);
438 }
439 return(wsum);
440 }
441
442
443 int
444 makeambient( /* make a new ambient value for storage */
445 COLOR acol,
446 RAY *r,
447 FVECT rn,
448 int al
449 )
450 {
451 AMBVAL amb;
452 FVECT uvw[3];
453 int i;
454
455 amb.weight = 1.0; /* compute weight */
456 for (i = al; i-- > 0; )
457 amb.weight *= AVGREFL;
458 if (r->rweight < 0.1*amb.weight) /* heuristic override */
459 amb.weight = 1.25*r->rweight;
460 setcolor(acol, AVGREFL, AVGREFL, AVGREFL);
461 /* compute ambient */
462 i = doambient(acol, r, amb.weight, uvw, amb.rad, amb.gpos, amb.gdir);
463 scalecolor(acol, 1./AVGREFL); /* undo assumed reflectance */
464 if (i <= 0 || amb.rad[0] <= FTINY) /* no Hessian or zero radius */
465 return(i);
466 /* store value */
467 VCOPY(amb.pos, r->rop);
468 amb.ndir = encodedir(r->ron);
469 amb.udir = encodedir(uvw[0]);
470 amb.lvl = al;
471 copycolor(amb.val, acol);
472 /* insert into tree */
473 avsave(&amb); /* and save to file */
474 if (rn != r->ron) { /* texture */
475 VCOPY(uvw[2], r->ron);
476 extambient(acol, &amb, r->rop, rn, uvw);
477 }
478 return(1);
479 }
480
481
482 void
483 extambient( /* extrapolate value at pv, nv */
484 COLOR cr,
485 AMBVAL *ap,
486 FVECT pv,
487 FVECT nv,
488 FVECT uvw[3]
489 )
490 {
491 static FVECT my_uvw[3];
492 FVECT v1;
493 int i;
494 double d = 1.0; /* zeroeth order */
495
496 if (uvw == NULL) { /* need local coordinates? */
497 decodedir(my_uvw[2], ap->ndir);
498 decodedir(my_uvw[0], ap->udir);
499 VCROSS(my_uvw[1], my_uvw[2], my_uvw[0]);
500 uvw = my_uvw;
501 }
502 for (i = 3; i--; ) /* gradient due to translation */
503 d += (pv[i] - ap->pos[i]) *
504 (ap->gpos[0]*uvw[0][i] + ap->gpos[1]*uvw[1][i]);
505
506 VCROSS(v1, uvw[2], nv); /* gradient due to rotation */
507 for (i = 3; i--; )
508 d += v1[i] * (ap->gdir[0]*uvw[0][i] + ap->gdir[1]*uvw[1][i]);
509
510 if (d <= 0.0) {
511 setcolor(cr, 0.0, 0.0, 0.0);
512 return;
513 }
514 copycolor(cr, ap->val);
515 scalecolor(cr, d);
516 }
517
518
519 static void
520 avinsert( /* insert ambient value in our tree */
521 AMBVAL *av
522 )
523 {
524 AMBTREE *at;
525 AMBVAL *ap;
526 AMBVAL avh;
527 FVECT ck0;
528 double s;
529 int branch;
530 int i;
531
532 if (av->rad[0] <= FTINY)
533 error(CONSISTENCY, "zero ambient radius in avinsert");
534 at = &atrunk;
535 VCOPY(ck0, thescene.cuorg);
536 s = thescene.cusize;
537 while (s*(OCTSCALE/2) > av->rad[1]*qambacc) {
538 if (at->kid == NULL)
539 if ((at->kid = newambtree()) == NULL)
540 error(SYSTEM, "out of memory in avinsert");
541 s *= 0.5;
542 branch = 0;
543 for (i = 0; i < 3; i++)
544 if (av->pos[i] > ck0[i] + s) {
545 ck0[i] += s;
546 branch |= 1 << i;
547 }
548 at = at->kid + branch;
549 }
550 avh.next = at->alist; /* order by increasing level */
551 for (ap = &avh; ap->next != NULL; ap = ap->next)
552 if (ap->next->lvl >= av->lvl)
553 break;
554 av->next = ap->next;
555 ap->next = (AMBVAL*)av;
556 at->alist = avh.next;
557 }
558
559
560 #else /* ! NEWAMB */
561
562 static double sumambient(COLOR acol, RAY *r, FVECT rn, int al,
563 AMBTREE *at, FVECT c0, double s);
564 static double makeambient(COLOR acol, RAY *r, FVECT rn, int al);
565 static void extambient(COLOR cr, AMBVAL *ap, FVECT pv, FVECT nv);
566
567
568 void
569 multambient( /* compute ambient component & multiply by coef. */
570 COLOR aval,
571 RAY *r,
572 FVECT nrm
573 )
574 {
575 static int rdepth = 0; /* ambient recursion */
576 COLOR acol;
577 double d, l;
578
579 if (ambdiv <= 0) /* no ambient calculation */
580 goto dumbamb;
581 /* check number of bounces */
582 if (rdepth >= ambounce)
583 goto dumbamb;
584 /* check ambient list */
585 if (ambincl != -1 && r->ro != NULL &&
586 ambincl != inset(ambset, r->ro->omod))
587 goto dumbamb;
588
589 if (ambacc <= FTINY) { /* no ambient storage */
590 copycolor(acol, aval);
591 rdepth++;
592 d = doambient(acol, r, r->rweight, NULL, NULL);
593 rdepth--;
594 if (d <= FTINY)
595 goto dumbamb;
596 copycolor(aval, acol);
597 return;
598 }
599
600 if (tracktime) /* sort to minimize thrashing */
601 sortambvals(0);
602 /* interpolate ambient value */
603 setcolor(acol, 0.0, 0.0, 0.0);
604 d = sumambient(acol, r, nrm, rdepth,
605 &atrunk, thescene.cuorg, thescene.cusize);
606 if (d > FTINY) {
607 d = 1.0/d;
608 scalecolor(acol, d);
609 multcolor(aval, acol);
610 return;
611 }
612 rdepth++; /* need to cache new value */
613 d = makeambient(acol, r, nrm, rdepth-1);
614 rdepth--;
615 if (d > FTINY) {
616 multcolor(aval, acol); /* got new value */
617 return;
618 }
619 dumbamb: /* return global value */
620 if ((ambvwt <= 0) | (navsum == 0)) {
621 multcolor(aval, ambval);
622 return;
623 }
624 l = bright(ambval); /* average in computations */
625 if (l > FTINY) {
626 d = (log(l)*(double)ambvwt + avsum) /
627 (double)(ambvwt + navsum);
628 d = exp(d) / l;
629 scalecolor(aval, d);
630 multcolor(aval, ambval); /* apply color of ambval */
631 } else {
632 d = exp( avsum / (double)navsum );
633 scalecolor(aval, d); /* neutral color */
634 }
635 }
636
637
638 static double
639 sumambient( /* get interpolated ambient value */
640 COLOR acol,
641 RAY *r,
642 FVECT rn,
643 int al,
644 AMBTREE *at,
645 FVECT c0,
646 double s
647 )
648 {
649 double d, e1, e2, wt, wsum;
650 COLOR ct;
651 FVECT ck0;
652 int i;
653 int j;
654 AMBVAL *av;
655
656 wsum = 0.0;
657 /* do this node */
658 for (av = at->alist; av != NULL; av = av->next) {
659 double rn_dot = -2.0;
660 if (tracktime)
661 av->latick = ambclock;
662 /*
663 * Ambient level test.
664 */
665 if (av->lvl > al) /* list sorted, so this works */
666 break;
667 if (av->weight < 0.9*r->rweight)
668 continue;
669 /*
670 * Ambient radius test.
671 */
672 VSUB(ck0, av->pos, r->rop);
673 e1 = DOT(ck0, ck0) / (av->rad * av->rad);
674 if (e1 > ambacc*ambacc*1.21)
675 continue;
676 /*
677 * Direction test using closest normal.
678 */
679 d = DOT(av->dir, r->ron);
680 if (rn != r->ron) {
681 rn_dot = DOT(av->dir, rn);
682 if (rn_dot > 1.0-FTINY)
683 rn_dot = 1.0-FTINY;
684 if (rn_dot >= d-FTINY) {
685 d = rn_dot;
686 rn_dot = -2.0;
687 }
688 }
689 e2 = (1.0 - d) * r->rweight;
690 if (e2 < 0.0)
691 e2 = 0.0;
692 else if (e1 + e2 > ambacc*ambacc*1.21)
693 continue;
694 /*
695 * Ray behind test.
696 */
697 d = 0.0;
698 for (j = 0; j < 3; j++)
699 d += (r->rop[j] - av->pos[j]) *
700 (av->dir[j] + r->ron[j]);
701 if (d*0.5 < -minarad*ambacc-.001)
702 continue;
703 /*
704 * Jittering final test reduces image artifacts.
705 */
706 e1 = sqrt(e1);
707 e2 = sqrt(e2);
708 wt = e1 + e2;
709 if (wt > ambacc*(.9+.2*urand(9015+samplendx)))
710 continue;
711 /*
712 * Recompute directional error using perturbed normal
713 */
714 if (rn_dot > 0.0) {
715 e2 = sqrt((1.0 - rn_dot)*r->rweight);
716 wt = e1 + e2;
717 }
718 if (wt <= 1e-3)
719 wt = 1e3;
720 else
721 wt = 1.0 / wt;
722 wsum += wt;
723 extambient(ct, av, r->rop, rn);
724 scalecolor(ct, wt);
725 addcolor(acol, ct);
726 }
727 if (at->kid == NULL)
728 return(wsum);
729 /* do children */
730 s *= 0.5;
731 for (i = 0; i < 8; i++) {
732 for (j = 0; j < 3; j++) {
733 ck0[j] = c0[j];
734 if (1<<j & i)
735 ck0[j] += s;
736 if (r->rop[j] < ck0[j] - OCTSCALE*s)
737 break;
738 if (r->rop[j] > ck0[j] + (1.0+OCTSCALE)*s)
739 break;
740 }
741 if (j == 3)
742 wsum += sumambient(acol, r, rn, al,
743 at->kid+i, ck0, s);
744 }
745 return(wsum);
746 }
747
748
749 static double
750 makeambient( /* make a new ambient value for storage */
751 COLOR acol,
752 RAY *r,
753 FVECT rn,
754 int al
755 )
756 {
757 AMBVAL amb;
758 FVECT gp, gd;
759 int i;
760
761 amb.weight = 1.0; /* compute weight */
762 for (i = al; i-- > 0; )
763 amb.weight *= AVGREFL;
764 if (r->rweight < 0.1*amb.weight) /* heuristic override */
765 amb.weight = 1.25*r->rweight;
766 setcolor(acol, AVGREFL, AVGREFL, AVGREFL);
767 /* compute ambient */
768 amb.rad = doambient(acol, r, amb.weight, gp, gd);
769 if (amb.rad <= FTINY) {
770 setcolor(acol, 0.0, 0.0, 0.0);
771 return(0.0);
772 }
773 scalecolor(acol, 1./AVGREFL); /* undo assumed reflectance */
774 /* store value */
775 VCOPY(amb.pos, r->rop);
776 VCOPY(amb.dir, r->ron);
777 amb.lvl = al;
778 copycolor(amb.val, acol);
779 VCOPY(amb.gpos, gp);
780 VCOPY(amb.gdir, gd);
781 /* insert into tree */
782 avsave(&amb); /* and save to file */
783 if (rn != r->ron)
784 extambient(acol, &amb, r->rop, rn); /* texture */
785 return(amb.rad);
786 }
787
788
789 static void
790 extambient( /* extrapolate value at pv, nv */
791 COLOR cr,
792 AMBVAL *ap,
793 FVECT pv,
794 FVECT nv
795 )
796 {
797 FVECT v1;
798 int i;
799 double d;
800
801 d = 1.0; /* zeroeth order */
802 /* gradient due to translation */
803 for (i = 0; i < 3; i++)
804 d += ap->gpos[i]*(pv[i]-ap->pos[i]);
805 /* gradient due to rotation */
806 VCROSS(v1, ap->dir, nv);
807 d += DOT(ap->gdir, v1);
808 if (d <= 0.0) {
809 setcolor(cr, 0.0, 0.0, 0.0);
810 return;
811 }
812 copycolor(cr, ap->val);
813 scalecolor(cr, d);
814 }
815
816
817 static void
818 avinsert( /* insert ambient value in our tree */
819 AMBVAL *av
820 )
821 {
822 AMBTREE *at;
823 AMBVAL *ap;
824 AMBVAL avh;
825 FVECT ck0;
826 double s;
827 int branch;
828 int i;
829
830 if (av->rad <= FTINY)
831 error(CONSISTENCY, "zero ambient radius in avinsert");
832 at = &atrunk;
833 VCOPY(ck0, thescene.cuorg);
834 s = thescene.cusize;
835 while (s*(OCTSCALE/2) > av->rad*ambacc) {
836 if (at->kid == NULL)
837 if ((at->kid = newambtree()) == NULL)
838 error(SYSTEM, "out of memory in avinsert");
839 s *= 0.5;
840 branch = 0;
841 for (i = 0; i < 3; i++)
842 if (av->pos[i] > ck0[i] + s) {
843 ck0[i] += s;
844 branch |= 1 << i;
845 }
846 at = at->kid + branch;
847 }
848 avh.next = at->alist; /* order by increasing level */
849 for (ap = &avh; ap->next != NULL; ap = ap->next)
850 if (ap->next->lvl >= av->lvl)
851 break;
852 av->next = ap->next;
853 ap->next = (AMBVAL*)av;
854 at->alist = avh.next;
855 }
856
857 #endif /* ! NEWAMB */
858
859 /************* FOLLOWING ROUTINES SAME FOR NEW & OLD METHODS ***************/
860
861 static void
862 initambfile( /* initialize ambient file */
863 int cre8
864 )
865 {
866 extern char *progname, *octname;
867 static char *mybuf = NULL;
868
869 #ifdef F_SETLKW
870 aflock(cre8 ? F_WRLCK : F_RDLCK);
871 #endif
872 SET_FILE_BINARY(ambfp);
873 if (mybuf == NULL)
874 mybuf = (char *)bmalloc(BUFSIZ+8);
875 setbuf(ambfp, mybuf);
876 if (cre8) { /* new file */
877 newheader("RADIANCE", ambfp);
878 fprintf(ambfp, "%s -av %g %g %g -aw %d -ab %d -aa %g ",
879 progname, colval(ambval,RED),
880 colval(ambval,GRN), colval(ambval,BLU),
881 ambvwt, ambounce, ambacc);
882 fprintf(ambfp, "-ad %d -as %d -ar %d ",
883 ambdiv, ambssamp, ambres);
884 if (octname != NULL)
885 fputs(octname, ambfp);
886 fputc('\n', ambfp);
887 fprintf(ambfp, "SOFTWARE= %s\n", VersionID);
888 fputnow(ambfp);
889 fputformat(AMBFMT, ambfp);
890 fputc('\n', ambfp);
891 putambmagic(ambfp);
892 } else if (checkheader(ambfp, AMBFMT, NULL) < 0 || !hasambmagic(ambfp))
893 error(USER, "bad ambient file");
894 }
895
896
897 static void
898 avsave( /* insert and save an ambient value */
899 AMBVAL *av
900 )
901 {
902 avinsert(avstore(av));
903 if (ambfp == NULL)
904 return;
905 if (writambval(av, ambfp) < 0)
906 goto writerr;
907 if (++nunflshed >= AMBFLUSH)
908 if (ambsync() == EOF)
909 goto writerr;
910 return;
911 writerr:
912 error(SYSTEM, "error writing to ambient file");
913 }
914
915
916 static AMBVAL *
917 avstore( /* allocate memory and store aval */
918 AMBVAL *aval
919 )
920 {
921 AMBVAL *av;
922 double d;
923
924 if ((av = newambval()) == NULL)
925 error(SYSTEM, "out of memory in avstore");
926 *av = *aval;
927 av->latick = ambclock;
928 av->next = NULL;
929 nambvals++;
930 d = bright(av->val);
931 if (d > FTINY) { /* add to log sum for averaging */
932 avsum += log(d);
933 navsum++;
934 }
935 return(av);
936 }
937
938
939 #define ATALLOCSZ 512 /* #/8 trees to allocate at once */
940
941 static AMBTREE *atfreelist = NULL; /* free ambient tree structures */
942
943
944 static AMBTREE *
945 newambtree(void) /* allocate 8 ambient tree structs */
946 {
947 AMBTREE *atp, *upperlim;
948
949 if (atfreelist == NULL) { /* get more nodes */
950 atfreelist = (AMBTREE *)malloc(ATALLOCSZ*8*sizeof(AMBTREE));
951 if (atfreelist == NULL)
952 return(NULL);
953 /* link new free list */
954 upperlim = atfreelist + 8*(ATALLOCSZ-1);
955 for (atp = atfreelist; atp < upperlim; atp += 8)
956 atp->kid = atp + 8;
957 atp->kid = NULL;
958 }
959 atp = atfreelist;
960 atfreelist = atp->kid;
961 memset((char *)atp, '\0', 8*sizeof(AMBTREE));
962 return(atp);
963 }
964
965
966 static void
967 freeambtree( /* free 8 ambient tree structs */
968 AMBTREE *atp
969 )
970 {
971 atp->kid = atfreelist;
972 atfreelist = atp;
973 }
974
975
976 static void
977 unloadatree( /* unload an ambient value tree */
978 AMBTREE *at,
979 unloadtf_t *f
980 )
981 {
982 AMBVAL *av;
983 int i;
984 /* transfer values at this node */
985 for (av = at->alist; av != NULL; av = at->alist) {
986 at->alist = av->next;
987 (*f)(av);
988 }
989 if (at->kid == NULL)
990 return;
991 for (i = 0; i < 8; i++) /* transfer and free children */
992 unloadatree(at->kid+i, f);
993 freeambtree(at->kid);
994 at->kid = NULL;
995 }
996
997
998 static struct avl {
999 AMBVAL *p;
1000 unsigned long t;
1001 } *avlist1; /* ambient value list with ticks */
1002 static AMBVAL **avlist2; /* memory positions for sorting */
1003 static int i_avlist; /* index for lists */
1004
1005 static int alatcmp(const void *av1, const void *av2);
1006
1007 static void
1008 avfree(AMBVAL *av)
1009 {
1010 free(av);
1011 }
1012
1013 static void
1014 av2list(
1015 AMBVAL *av
1016 )
1017 {
1018 #ifdef DEBUG
1019 if (i_avlist >= nambvals)
1020 error(CONSISTENCY, "too many ambient values in av2list1");
1021 #endif
1022 avlist1[i_avlist].p = avlist2[i_avlist] = (AMBVAL*)av;
1023 avlist1[i_avlist++].t = av->latick;
1024 }
1025
1026
1027 static int
1028 alatcmp( /* compare ambient values for MRA */
1029 const void *av1,
1030 const void *av2
1031 )
1032 {
1033 long lc = ((struct avl *)av2)->t - ((struct avl *)av1)->t;
1034 return(lc<0 ? -1 : lc>0 ? 1 : 0);
1035 }
1036
1037
1038 /* GW NOTE 2002/10/3:
1039 * I used to compare AMBVAL pointers, but found that this was the
1040 * cause of a serious consistency error with gcc, since the optimizer
1041 * uses some dangerous trick in pointer subtraction that
1042 * assumes pointers differ by exact struct size increments.
1043 */
1044 static int
1045 aposcmp( /* compare ambient value positions */
1046 const void *avp1,
1047 const void *avp2
1048 )
1049 {
1050 long diff = *(char * const *)avp1 - *(char * const *)avp2;
1051 if (diff < 0)
1052 return(-1);
1053 return(diff > 0);
1054 }
1055
1056
1057 static int
1058 avlmemi( /* find list position from address */
1059 AMBVAL *avaddr
1060 )
1061 {
1062 AMBVAL **avlpp;
1063
1064 avlpp = (AMBVAL **)bsearch((char *)&avaddr, (char *)avlist2,
1065 nambvals, sizeof(AMBVAL *), &aposcmp);
1066 if (avlpp == NULL)
1067 error(CONSISTENCY, "address not found in avlmemi");
1068 return(avlpp - avlist2);
1069 }
1070
1071
1072 static void
1073 sortambvals( /* resort ambient values */
1074 int always
1075 )
1076 {
1077 AMBTREE oldatrunk;
1078 AMBVAL tav, *tap, *pnext;
1079 int i, j;
1080 /* see if it's time yet */
1081 if (!always && (ambclock++ < lastsort+sortintvl ||
1082 nambvals < SORT_THRESH))
1083 return;
1084 /*
1085 * The idea here is to minimize memory thrashing
1086 * in VM systems by improving reference locality.
1087 * We do this by periodically sorting our stored ambient
1088 * values in memory in order of most recently to least
1089 * recently accessed. This ordering was chosen so that new
1090 * ambient values (which tend to be less important) go into
1091 * higher memory with the infrequently accessed values.
1092 * Since we expect our values to need sorting less
1093 * frequently as the process continues, we double our
1094 * waiting interval after each call.
1095 * This routine is also called by setambacc() with
1096 * the "always" parameter set to 1 so that the ambient
1097 * tree will be rebuilt with the new accuracy parameter.
1098 */
1099 if (tracktime) { /* allocate pointer arrays to sort */
1100 avlist2 = (AMBVAL **)malloc(nambvals*sizeof(AMBVAL *));
1101 avlist1 = (struct avl *)malloc(nambvals*sizeof(struct avl));
1102 } else {
1103 avlist2 = NULL;
1104 avlist1 = NULL;
1105 }
1106 if (avlist1 == NULL) { /* no time tracking -- rebuild tree? */
1107 if (avlist2 != NULL)
1108 free((void *)avlist2);
1109 if (always) { /* rebuild without sorting */
1110 oldatrunk = atrunk;
1111 atrunk.alist = NULL;
1112 atrunk.kid = NULL;
1113 unloadatree(&oldatrunk, &avinsert);
1114 }
1115 } else { /* sort memory by last access time */
1116 /*
1117 * Sorting memory is tricky because it isn't contiguous.
1118 * We have to sort an array of pointers by MRA and also
1119 * by memory position. We then copy values in "loops"
1120 * to minimize memory hits. Nevertheless, we will visit
1121 * everyone at least twice, and this is an expensive process
1122 * when we're thrashing, which is when we need to do it.
1123 */
1124 #ifdef DEBUG
1125 sprintf(errmsg, "sorting %u ambient values at ambclock=%lu...",
1126 nambvals, ambclock);
1127 eputs(errmsg);
1128 #endif
1129 i_avlist = 0;
1130 unloadatree(&atrunk, &av2list); /* empty current tree */
1131 #ifdef DEBUG
1132 if (i_avlist < nambvals)
1133 error(CONSISTENCY, "missing ambient values in sortambvals");
1134 #endif
1135 qsort((char *)avlist1, nambvals, sizeof(struct avl), &alatcmp);
1136 qsort((char *)avlist2, nambvals, sizeof(AMBVAL *), &aposcmp);
1137 for (i = 0; i < nambvals; i++) {
1138 if (avlist1[i].p == NULL)
1139 continue;
1140 tap = avlist2[i];
1141 tav = *tap;
1142 for (j = i; (pnext = avlist1[j].p) != tap;
1143 j = avlmemi(pnext)) {
1144 *(avlist2[j]) = *pnext;
1145 avinsert(avlist2[j]);
1146 avlist1[j].p = NULL;
1147 }
1148 *(avlist2[j]) = tav;
1149 avinsert(avlist2[j]);
1150 avlist1[j].p = NULL;
1151 }
1152 free((void *)avlist1);
1153 free((void *)avlist2);
1154 /* compute new sort interval */
1155 sortintvl = ambclock - lastsort;
1156 if (sortintvl >= MAX_SORT_INTVL/2)
1157 sortintvl = MAX_SORT_INTVL;
1158 else
1159 sortintvl <<= 1; /* wait twice as long next */
1160 #ifdef DEBUG
1161 eputs("done\n");
1162 #endif
1163 }
1164 if (ambclock >= MAXACLOCK)
1165 ambclock = MAXACLOCK/2;
1166 lastsort = ambclock;
1167 }
1168
1169
1170 #ifdef F_SETLKW
1171
1172 static void
1173 aflock( /* lock/unlock ambient file */
1174 int typ
1175 )
1176 {
1177 static struct flock fls; /* static so initialized to zeroes */
1178
1179 if (typ == fls.l_type) /* already called? */
1180 return;
1181 fls.l_type = typ;
1182 if (fcntl(fileno(ambfp), F_SETLKW, &fls) < 0)
1183 error(SYSTEM, "cannot (un)lock ambient file");
1184 }
1185
1186
1187 int
1188 ambsync(void) /* synchronize ambient file */
1189 {
1190 long flen;
1191 AMBVAL avs;
1192 int n;
1193
1194 if (ambfp == NULL) /* no ambient file? */
1195 return(0);
1196 /* gain appropriate access */
1197 aflock(nunflshed ? F_WRLCK : F_RDLCK);
1198 /* see if file has grown */
1199 if ((flen = lseek(fileno(ambfp), (off_t)0, SEEK_END)) < 0)
1200 goto seekerr;
1201 if ((n = flen - lastpos) > 0) { /* file has grown */
1202 if (ambinp == NULL) { /* use duplicate filedes */
1203 ambinp = fdopen(dup(fileno(ambfp)), "r");
1204 if (ambinp == NULL)
1205 error(SYSTEM, "fdopen failed in ambsync");
1206 }
1207 if (fseek(ambinp, lastpos, SEEK_SET) < 0)
1208 goto seekerr;
1209 while (n >= AMBVALSIZ) { /* load contributed values */
1210 if (!readambval(&avs, ambinp)) {
1211 sprintf(errmsg,
1212 "ambient file \"%s\" corrupted near character %ld",
1213 ambfile, flen - n);
1214 error(WARNING, errmsg);
1215 break;
1216 }
1217 avinsert(avstore(&avs));
1218 n -= AMBVALSIZ;
1219 }
1220 lastpos = flen - n;
1221 /*** seek always as safety measure
1222 if (n) ***/ /* alignment */
1223 if (lseek(fileno(ambfp), (off_t)lastpos, SEEK_SET) < 0)
1224 goto seekerr;
1225 }
1226 n = fflush(ambfp); /* calls write() at last */
1227 if (n != EOF)
1228 lastpos += (long)nunflshed*AMBVALSIZ;
1229 else if ((lastpos = lseek(fileno(ambfp), (off_t)0, SEEK_CUR)) < 0)
1230 goto seekerr;
1231
1232 aflock(F_UNLCK); /* release file */
1233 nunflshed = 0;
1234 return(n);
1235 seekerr:
1236 error(SYSTEM, "seek failed in ambsync");
1237 return -1; /* pro forma return */
1238 }
1239
1240 #else /* ! F_SETLKW */
1241
1242 int
1243 ambsync(void) /* flush ambient file */
1244 {
1245 if (ambfp == NULL)
1246 return(0);
1247 nunflshed = 0;
1248 return(fflush(ambfp));
1249 }
1250
1251 #endif /* ! F_SETLKW */