ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/ambient.c
Revision: 2.72
Committed: Fri Apr 11 22:54:34 2014 UTC (10 years ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.71: +43 -24 lines
Log Message:
Fixed compile errors and tidied up new code

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: ambient.c,v 2.71 2014/04/11 20:31:37 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 static double avsum = 0.; /* computed ambient value sum (log) */
55 static unsigned int navsum = 0; /* number of values in avsum */
56 static unsigned int nambvals = 0; /* total number of indirect values */
57 static unsigned int nambshare = 0; /* number of values from file */
58 static unsigned long ambclock = 0; /* ambient access clock */
59 static unsigned long lastsort = 0; /* time of last value sort */
60 static long sortintvl = SORT_INTVL; /* time until next sort */
61 static FILE *ambinp = NULL; /* auxiliary file for input */
62 static long lastpos = -1; /* last flush position */
63
64 #define MAXACLOCK (1L<<30) /* clock turnover value */
65 /*
66 * Track access times unless we are sharing ambient values
67 * through memory on a multiprocessor, when we want to avoid
68 * claiming our own memory (copy on write). Go ahead anyway
69 * if more than two thirds of our values are unshared.
70 * Compile with -Dtracktime=0 to turn this code off.
71 */
72 #ifndef tracktime
73 #define tracktime (shm_boundary == NULL || nambvals > 3*nambshare)
74 #endif
75
76 #define AMBFLUSH (BUFSIZ/AMBVALSIZ)
77
78 #define newambval() (AMBVAL *)malloc(sizeof(AMBVAL))
79 #define freeav(av) free((void *)av);
80
81 static void initambfile(int creat);
82 static void avsave(AMBVAL *av);
83 static AMBVAL *avstore(AMBVAL *aval);
84 static AMBTREE *newambtree(void);
85 static void freeambtree(AMBTREE *atp);
86
87 typedef void unloadtf_t(AMBVAL *);
88 static unloadtf_t avinsert;
89 static unloadtf_t av2list;
90 static unloadtf_t avfree;
91 static void unloadatree(AMBTREE *at, unloadtf_t *f);
92
93 static int aposcmp(const void *avp1, const void *avp2);
94 static int avlmemi(AMBVAL *avaddr);
95 static void sortambvals(int always);
96
97 #ifdef F_SETLKW
98 static void aflock(int typ);
99 #endif
100
101
102 void
103 setambres( /* set ambient resolution */
104 int ar
105 )
106 {
107 ambres = ar < 0 ? 0 : ar; /* may be done already */
108 /* set min & max radii */
109 if (ar <= 0) {
110 minarad = 0;
111 maxarad = thescene.cusize / 2.0;
112 } else {
113 minarad = thescene.cusize / ar;
114 maxarad = 64 * minarad; /* heuristic */
115 if (maxarad > thescene.cusize / 2.0)
116 maxarad = thescene.cusize / 2.0;
117 }
118 if (minarad <= FTINY)
119 minarad = 10*FTINY;
120 if (maxarad <= minarad)
121 maxarad = 64 * minarad;
122 }
123
124
125 void
126 setambacc( /* set ambient accuracy */
127 double newa
128 )
129 {
130 double ambdiff;
131
132 if (newa < 0.0)
133 newa = 0.0;
134 ambdiff = fabs(newa - ambacc);
135 if (ambdiff >= .01 && (ambacc = newa) > FTINY && nambvals > 0)
136 sortambvals(1); /* rebuild tree */
137 }
138
139
140 void
141 setambient(void) /* initialize calculation */
142 {
143 int readonly = 0;
144 long flen;
145 AMBVAL amb;
146 /* make sure we're fresh */
147 ambdone();
148 /* init ambient limits */
149 setambres(ambres);
150 setambacc(ambacc);
151 if (ambfile == NULL || !ambfile[0])
152 return;
153 if (ambacc <= FTINY) {
154 sprintf(errmsg, "zero ambient accuracy so \"%s\" not opened",
155 ambfile);
156 error(WARNING, errmsg);
157 return;
158 }
159 /* open ambient file */
160 if ((ambfp = fopen(ambfile, "r+")) == NULL)
161 readonly = (ambfp = fopen(ambfile, "r")) != NULL;
162 if (ambfp != NULL) {
163 initambfile(0); /* file exists */
164 lastpos = ftell(ambfp);
165 while (readambval(&amb, ambfp))
166 avinsert(avstore(&amb));
167 nambshare = nambvals; /* share loaded values */
168 if (readonly) {
169 sprintf(errmsg,
170 "loaded %u values from read-only ambient file",
171 nambvals);
172 error(WARNING, errmsg);
173 fclose(ambfp); /* close file so no writes */
174 ambfp = NULL;
175 return; /* avoid ambsync() */
176 }
177 /* align file pointer */
178 lastpos += (long)nambvals*AMBVALSIZ;
179 flen = lseek(fileno(ambfp), (off_t)0, SEEK_END);
180 if (flen != lastpos) {
181 sprintf(errmsg,
182 "ignoring last %ld values in ambient file (corrupted)",
183 (flen - lastpos)/AMBVALSIZ);
184 error(WARNING, errmsg);
185 fseek(ambfp, lastpos, SEEK_SET);
186 #ifndef _WIN32 /* XXX we need a replacement for that one */
187 ftruncate(fileno(ambfp), (off_t)lastpos);
188 #endif
189 }
190 } else if ((ambfp = fopen(ambfile, "w+")) != NULL) {
191 initambfile(1); /* else create new file */
192 fflush(ambfp);
193 lastpos = ftell(ambfp);
194 } else {
195 sprintf(errmsg, "cannot open ambient file \"%s\"", ambfile);
196 error(SYSTEM, errmsg);
197 }
198 #ifdef getc_unlocked
199 flockfile(ambfp); /* application-level lock */
200 #endif
201 #ifdef F_SETLKW
202 aflock(F_UNLCK); /* release file */
203 #endif
204 }
205
206
207 void
208 ambdone(void) /* close ambient file and free memory */
209 {
210 if (ambfp != NULL) { /* close ambient file */
211 ambsync();
212 fclose(ambfp);
213 ambfp = NULL;
214 if (ambinp != NULL) {
215 fclose(ambinp);
216 ambinp = NULL;
217 }
218 lastpos = -1;
219 }
220 /* free ambient tree */
221 unloadatree(&atrunk, &avfree);
222 /* reset state variables */
223 avsum = 0.;
224 navsum = 0;
225 nambvals = 0;
226 nambshare = 0;
227 ambclock = 0;
228 lastsort = 0;
229 sortintvl = SORT_INTVL;
230 }
231
232
233 void
234 ambnotify( /* record new modifier */
235 OBJECT obj
236 )
237 {
238 static int hitlimit = 0;
239 OBJREC *o;
240 char **amblp;
241
242 if (obj == OVOID) { /* starting over */
243 ambset[0] = 0;
244 hitlimit = 0;
245 return;
246 }
247 o = objptr(obj);
248 if (hitlimit || !ismodifier(o->otype))
249 return;
250 for (amblp = amblist; *amblp != NULL; amblp++)
251 if (!strcmp(o->oname, *amblp)) {
252 if (ambset[0] >= MAXASET) {
253 error(WARNING, "too many modifiers in ambient list");
254 hitlimit++;
255 return; /* should this be fatal? */
256 }
257 insertelem(ambset, obj);
258 return;
259 }
260 }
261
262 /************ THE FOLLOWING ROUTINES DIFFER BETWEEN NEW & OLD ***************/
263
264 #ifdef NEWAMB
265
266 #define tfunc(lwr, x, upr) (((x)-(lwr))/((upr)-(lwr)))
267
268 static double sumambient(COLOR acol, RAY *r, FVECT rn, int al,
269 AMBTREE *at, FVECT c0, double s);
270 static int makeambient(COLOR acol, RAY *r, FVECT rn, int al);
271 static void extambient(COLOR cr, AMBVAL *ap, FVECT pv, FVECT nv,
272 FVECT uvw[3]);
273
274 void
275 multambient( /* compute ambient component & multiply by coef. */
276 COLOR aval,
277 RAY *r,
278 FVECT nrm
279 )
280 {
281 static int rdepth = 0; /* ambient recursion */
282 COLOR acol;
283 int ok;
284 double d, l;
285
286 if (ambdiv <= 0) /* no ambient calculation */
287 goto dumbamb;
288 /* check number of bounces */
289 if (rdepth >= ambounce)
290 goto dumbamb;
291 /* check ambient list */
292 if (ambincl != -1 && r->ro != NULL &&
293 ambincl != inset(ambset, r->ro->omod))
294 goto dumbamb;
295
296 if (ambacc <= FTINY) { /* no ambient storage */
297 copycolor(acol, aval);
298 rdepth++;
299 ok = doambient(acol, r, r->rweight, NULL, NULL, NULL, NULL);
300 rdepth--;
301 if (!ok)
302 goto dumbamb;
303 copycolor(aval, acol);
304 return;
305 }
306
307 if (tracktime) /* sort to minimize thrashing */
308 sortambvals(0);
309 /* interpolate ambient value */
310 setcolor(acol, 0.0, 0.0, 0.0);
311 d = sumambient(acol, r, nrm, rdepth,
312 &atrunk, thescene.cuorg, thescene.cusize);
313 if (d > FTINY) {
314 d = 1.0/d;
315 scalecolor(acol, d);
316 multcolor(aval, acol);
317 return;
318 }
319 rdepth++; /* need to cache new value */
320 ok = makeambient(acol, r, nrm, rdepth-1);
321 rdepth--;
322 if (ok) {
323 multcolor(aval, acol); /* got new value */
324 return;
325 }
326 dumbamb: /* return global value */
327 if ((ambvwt <= 0) | (navsum == 0)) {
328 multcolor(aval, ambval);
329 return;
330 }
331 l = bright(ambval); /* average in computations */
332 if (l > FTINY) {
333 d = (log(l)*(double)ambvwt + avsum) /
334 (double)(ambvwt + navsum);
335 d = exp(d) / l;
336 scalecolor(aval, d);
337 multcolor(aval, ambval); /* apply color of ambval */
338 } else {
339 d = exp( avsum / (double)navsum );
340 scalecolor(aval, d); /* neutral color */
341 }
342 }
343
344
345 double
346 sumambient( /* get interpolated ambient value */
347 COLOR acol,
348 RAY *r,
349 FVECT rn,
350 int al,
351 AMBTREE *at,
352 FVECT c0,
353 double s
354 )
355 { /* initial limit is ambacc radians */
356 const double maxangle = (ambacc-PI/2.)*pow(r->rweight,0.13) + PI/2.;
357 double wsum = 0.0;
358 FVECT ck0;
359 int i, j;
360 AMBVAL *av;
361 /* sum this node */
362 for (av = at->alist; av != NULL; av = av->next) {
363 double d, delta_r2, delta_t2;
364 COLOR ct;
365 FVECT uvw[3];
366 /* record access */
367 if (tracktime)
368 av->latick = ambclock;
369 /*
370 * Ambient level test
371 */
372 if (av->lvl > al) /* list sorted, so this works */
373 break;
374 if (av->weight < 0.9*r->rweight)
375 continue;
376 /*
377 * Direction test using unperturbed normal
378 */
379 decodedir(uvw[2], av->ndir);
380 d = DOT(uvw[2], r->ron);
381 if (d <= 0.0) /* >= 90 degrees */
382 continue;
383 delta_r2 = 2.0 - 2.0*d; /* approx. radians^2 */
384 if (delta_r2 >= maxangle*maxangle)
385 continue;
386 /*
387 * Elliptical radii test based on Hessian
388 */
389 decodedir(uvw[0], av->udir);
390 VCROSS(uvw[1], uvw[2], uvw[0]);
391 VSUB(ck0, av->pos, r->rop);
392 d = DOT(ck0, uvw[0]) / av->rad[0];
393 delta_t2 = d*d;
394 d = DOT(ck0, uvw[1]) / av->rad[1];
395 delta_t2 += d*d;
396 if (delta_t2 >= ambacc*ambacc)
397 continue;
398 /*
399 * Intersection behind test
400 */
401 d = 0.0;
402 for (j = 0; j < 3; j++)
403 d += (r->rop[j] - av->pos[j])*(uvw[2][j] + r->ron[j]);
404 if (d*0.5 < -minarad*ambacc-.001)
405 continue;
406 /*
407 * Extrapolate value and compute final weight (hat function)
408 */
409 extambient(ct, av, r->rop, rn, uvw);
410 d = tfunc(maxangle, sqrt(delta_r2), 0.0) *
411 tfunc(ambacc, sqrt(delta_t2), 0.0);
412 scalecolor(ct, d);
413 addcolor(acol, ct);
414 wsum += d;
415 }
416 if (at->kid == NULL)
417 return(wsum);
418 /* sum children */
419 s *= 0.5;
420 for (i = 0; i < 8; i++) {
421 for (j = 0; j < 3; j++) {
422 ck0[j] = c0[j];
423 if (1<<j & i)
424 ck0[j] += s;
425 if (r->rop[j] < ck0[j] - OCTSCALE*s)
426 break;
427 if (r->rop[j] > ck0[j] + (1.0+OCTSCALE)*s)
428 break;
429 }
430 if (j == 3)
431 wsum += sumambient(acol, r, rn, al,
432 at->kid+i, ck0, s);
433 }
434 return(wsum);
435 }
436
437
438 int
439 makeambient( /* make a new ambient value for storage */
440 COLOR acol,
441 RAY *r,
442 FVECT rn,
443 int al
444 )
445 {
446 AMBVAL amb;
447 FVECT uvw[3];
448 int i;
449
450 amb.weight = 1.0; /* compute weight */
451 for (i = al; i-- > 0; )
452 amb.weight *= AVGREFL;
453 if (r->rweight < 0.1*amb.weight) /* heuristic override */
454 amb.weight = 1.25*r->rweight;
455 setcolor(acol, AVGREFL, AVGREFL, AVGREFL);
456 /* compute ambient */
457 if (!doambient(acol, r, amb.weight, uvw, amb.rad, amb.gpos, amb.gdir)) {
458 setcolor(acol, 0.0, 0.0, 0.0);
459 return(0);
460 }
461 scalecolor(acol, 1./AVGREFL); /* undo assumed reflectance */
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]*ambacc) {
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 */