ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/ambient.c
Revision: 2.81
Committed: Fri Apr 25 23:04:16 2014 UTC (10 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.80: +13 -5 lines
Log Message:
Added back version of ray behind test, but still some problem cases

File Contents

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