--- ray/src/rt/virtuals.c 1991/06/24 16:10:59 1.7 +++ ray/src/rt/virtuals.c 2003/07/21 22:30:19 2.11 @@ -1,33 +1,32 @@ -/* Copyright (c) 1991 Regents of the University of California */ - #ifndef lint -static char SCCSid[] = "$SunId$ LBL"; +static const char RCSid[] = "$Id: virtuals.c,v 2.11 2003/07/21 22:30:19 schorsch Exp $"; #endif - /* * Routines for simulating virtual light sources * Thus far, we only support planar mirrors. + * + * External symbols declared in source.h */ +#include "copyright.h" + #include "ray.h" -#include "octree.h" - #include "otypes.h" #include "source.h" #include "random.h" +#define MINSAMPLES 16 /* minimum number of pretest samples */ +#define STESTMAX 32 /* maximum seeks per sample */ -#define DISKTFRAC 0.25 /* disk area pretest fraction */ -double getdisk(); - static OBJECT *vobject; /* virtual source objects */ static int nvobjects = 0; /* number of virtual source objects */ +void markvirtuals() /* find and mark virtual sources */ { register OBJREC *o; @@ -36,19 +35,21 @@ markvirtuals() /* find and mark virtual sources */ if (directrelay <= 0) return; /* find virtual source objects */ - for (i = 0; i < nobjects; i++) { + for (i = 0; i < nsceneobjs; i++) { o = objptr(i); if (!issurface(o->otype) || o->omod == OVOID) continue; - if (!isvlight(objptr(o->omod)->otype)) + if (!isvlight(vsmaterial(o)->otype)) continue; if (sfun[o->otype].of == NULL || - sfun[o->otype].of->getpleq == NULL) - objerror(o, USER, "illegal material"); + sfun[o->otype].of->getpleq == NULL) { + objerror(o,WARNING,"secondary sources not supported"); + continue; + } if (nvobjects == 0) vobject = (OBJECT *)malloc(sizeof(OBJECT)); else - vobject = (OBJECT *)realloc((char *)vobject, + vobject = (OBJECT *)realloc((void *)vobject, (unsigned)(nvobjects+1)*sizeof(OBJECT)); if (vobject == NULL) error(SYSTEM, "out of memory in addvirtuals"); @@ -63,11 +64,12 @@ markvirtuals() /* find and mark virtual sources */ for (i = nsources; i-- > 0; ) addvirtuals(i, directrelay); /* done with our object list */ - free((char *)vobject); + free((void *)vobject); nvobjects = 0; } +void addvirtuals(sn, nr) /* add virtuals associated with source */ int sn; int nr; @@ -85,6 +87,7 @@ int nr; } +void vproject(o, sn, n) /* create projected source(s) if they exist */ OBJREC *o; int sn; @@ -98,11 +101,12 @@ int n; if (o == source[sn].so) /* objects cannot project themselves */ return; /* get virtual source material */ - vsmat = sfun[objptr(o->omod)->otype].mf; + vsmat = sfun[vsmaterial(o)->otype].mf; /* project virtual sources */ for (i = 0; i < vsmat->nproj; i++) if ((*vsmat->vproj)(proj, o, &source[sn], i)) if ((ns = makevsrc(o, sn, proj)) >= 0) { + source[ns].sa.sv.pn = i; #ifdef DEBUG virtverb(ns, stderr); #endif @@ -111,16 +115,32 @@ int n; } +OBJREC * +vsmaterial(o) /* get virtual source material pointer */ +OBJREC *o; +{ + register int i; + register OBJREC *m; + + i = o->omod; + m = objptr(i); + if (m->otype != MAT_ILLUM || m->oargs.nsargs < 1 || + !strcmp(m->oargs.sarg[0], VOIDID) || + (i = lastmod(objndx(m), m->oargs.sarg[0])) == OVOID) + return(m); /* direct modifier */ + return(objptr(i)); /* illum alternate */ +} + + int makevsrc(op, sn, pm) /* make virtual source if reasonable */ OBJREC *op; register int sn; MAT4 pm; { - FVECT nsloc, nsnorm, ocent; - double maxrad2; + FVECT nsloc, nsnorm, ocent, v; + double maxrad2, d; int nsflags; - double d1; SPOT theirspot, ourspot; register int i; @@ -134,35 +154,73 @@ MAT4 pm; if (source[sn].sflags & SPROX) return(-1); /* should never get here! */ multv3(nsloc, source[sn].sloc, pm); + normalize(nsloc); VCOPY(ourspot.aim, ocent); ourspot.siz = PI*maxrad2; - ourspot.flen = 0.; + ourspot.flen = -1.; if (source[sn].sflags & SSPOT) { - copystruct(&theirspot, source[sn].sl.s); multp3(theirspot.aim, source[sn].sl.s->aim, pm); + /* adjust for source size */ + d = sqrt(dist2(ourspot.aim, theirspot.aim)); + d = sqrt(source[sn].sl.s->siz/PI) + d*source[sn].srad; + theirspot.siz = PI*d*d; + ourspot.flen = theirspot.flen = source[sn].sl.s->flen; + d = ourspot.siz; if (!commonbeam(&ourspot, &theirspot, nsloc)) - return(-1); /* no overlap */ + return(-1); /* no overlap */ + if (ourspot.siz < d-FTINY) { /* it shrunk */ + d = beamdisk(v, op, &ourspot, nsloc); + if (d <= FTINY) + return(-1); + if (d < maxrad2) { + maxrad2 = d; + VCOPY(ocent, v); + } + } } } else { /* local source */ multp3(nsloc, source[sn].sloc, pm); for (i = 0; i < 3; i++) ourspot.aim[i] = ocent[i] - nsloc[i]; - if ((d1 = normalize(ourspot.aim)) == 0.) + if ((d = normalize(ourspot.aim)) == 0.) return(-1); /* at source!! */ - if (source[sn].sflags & SPROX && d1 > source[sn].sl.prox) + if (source[sn].sflags & SPROX && d > source[sn].sl.prox) return(-1); /* too far away */ - ourspot.siz = 2.*PI*(1. - d1/sqrt(d1*d1+maxrad2)); ourspot.flen = 0.; + /* adjust for source size */ + d = (sqrt(maxrad2) + source[sn].srad) / d; + if (d < 1.-FTINY) + ourspot.siz = 2.*PI*(1. - sqrt(1.-d*d)); + else + nsflags &= ~SSPOT; if (source[sn].sflags & SSPOT) { - copystruct(&theirspot, source[sn].sl.s); + theirspot = *(source[sn].sl.s); multv3(theirspot.aim, source[sn].sl.s->aim, pm); - if (!commonspot(&ourspot, &theirspot, nsloc)) - return(-1); /* no overlap */ - ourspot.flen = theirspot.flen; + normalize(theirspot.aim); + if (nsflags & SSPOT) { + ourspot.flen = theirspot.flen; + d = ourspot.siz; + if (!commonspot(&ourspot, &theirspot, nsloc)) + return(-1); /* no overlap */ + } else { + nsflags |= SSPOT; + ourspot = theirspot; + d = 2.*ourspot.siz; + } + if (ourspot.siz < d-FTINY) { /* it shrunk */ + d = spotdisk(v, op, &ourspot, nsloc); + if (d <= FTINY) + return(-1); + if (d < maxrad2) { + maxrad2 = d; + VCOPY(ocent, v); + } + } } if (source[sn].sflags & SFLAT) { /* behind source? */ multv3(nsnorm, source[sn].snorm, pm); - if (checkspot(&ourspot, nsnorm) < 0) + normalize(nsnorm); + if (nsflags & SSPOT && !checkspot(&ourspot, nsnorm)) return(-1); } } @@ -175,15 +233,22 @@ MAT4 pm; goto memerr; source[i].sflags = nsflags; VCOPY(source[i].sloc, nsloc); + multv3(source[i].ss[SU], source[sn].ss[SU], pm); + multv3(source[i].ss[SV], source[sn].ss[SV], pm); if (nsflags & SFLAT) VCOPY(source[i].snorm, nsnorm); - source[i].ss = source[sn].ss; source[i].ss2 = source[sn].ss2; - if ((source[i].sl.s = (SPOT *)malloc(sizeof(SPOT))) == NULL) - goto memerr; - copystruct(source[i].sl.s, &ourspot); + else + multv3(source[i].ss[SW], source[sn].ss[SW], pm); + source[i].srad = source[sn].srad; + source[i].ss2 = source[sn].ss2; + if (nsflags & SSPOT) { + if ((source[i].sl.s = (SPOT *)malloc(sizeof(SPOT))) == NULL) + goto memerr; + *(source[i].sl.s) = ourspot; + } if (nsflags & SPROX) source[i].sl.prox = source[sn].sl.prox; - source[i].sa.svnext = sn; + source[i].sa.sv.sn = sn; source[i].so = op; return(i); memerr: @@ -200,11 +265,11 @@ register int sn; double rad2, roffs, offs, d, rd, rdoto; FVECT rnrm, nrm; /* first, use object getdisk function */ - rad2 = (*sfun[op->otype].of->getdisk)(oc, op); + rad2 = getmaxdisk(oc, op); if (!(source[sn].sflags & SVIRTUAL)) return(rad2); /* all done for normal source */ /* check for correct side of relay surface */ - roffs = (*sfun[source[sn].so->otype].of->getpleq)(rnrm, source[sn].so); + roffs = getplaneq(rnrm, source[sn].so); rd = DOT(rnrm, source[sn].sloc); /* source projection */ if (!(source[sn].sflags & SDISTANT)) rd -= roffs; @@ -212,9 +277,9 @@ register int sn; if ((d > 0.) ^ (rd > 0.)) return(rad2); /* OK if opposite sides */ if (d*d >= rad2) - return(.0); /* no relay is possible */ + return(0.); /* no relay is possible */ /* we need a closer look */ - offs = (*sfun[op->otype].of->getpleq)(nrm, op); + offs = getplaneq(nrm, op); rdoto = DOT(rnrm, nrm); if (d*d >= rad2*(1.-rdoto*rdoto)) return(0.); /* disk entirely on projection side */ @@ -234,60 +299,103 @@ register int sn; /* target source number */ RAY sr; FVECT onorm; FVECT offsdir; + SRCINDEX si; double or, d; - int nok, nhit; + int stestlim, ssn; + int nhit, nok; register int i, n; /* return if pretesting disabled */ if (vspretest <= 0) return(f); /* get surface normal */ - (*sfun[o->otype].of->getpleq)(onorm, o); + getplaneq(onorm, o); /* set number of rays to sample */ - if (source[sn].sflags & SDISTANT) - n = (2./3.*PI*PI)*or2/(thescene.cusize*thescene.cusize)* - vspretest + .5; - else - n = or2/dist2(oc,source[sn].sloc)*vspretest + .5; - if (n < 1) n = 1; - /* limit tests to central region */ - or = DISKTFRAC*sqrt(or2); + if (source[sn].sflags & SDISTANT) { + /* 32. == heuristic constant */ + n = 32.*or2/(thescene.cusize*thescene.cusize)*vspretest + .5; + } else { + for (i = 0; i < 3; i++) + offsdir[i] = source[sn].sloc[i] - oc[i]; + d = DOT(offsdir,offsdir); + if (d <= FTINY) + n = 2.*PI * vspretest + .5; + else + n = 2.*PI * (1.-sqrt(1./(1.+or2/d)))*vspretest + .5; + } + if (n < MINSAMPLES) n = MINSAMPLES; +#ifdef DEBUG + fprintf(stderr, "pretesting source %d in object %s with %d rays\n", + sn, o->oname, n); +#endif /* sample */ + or = sqrt(or2); + stestlim = n*STESTMAX; + ssn = 0; nhit = nok = 0; + initsrcindex(&si); while (n-- > 0) { - samplendx++; - /* - * We're being real sloppy with our sample locations here. - */ - for (i = 0; i < 3; i++) - offsdir[i] = or*(1. - 2.*urand(931*i+5821+n)); - d = DOT(offsdir,onorm); - for (i = 0; i < 3; i++) - sr.rorg[i] = oc[i] + (1.-d)*offsdir[i]; + /* get sample point */ + do { + if (ssn >= stestlim) { +#ifdef DEBUG + fprintf(stderr, "\ttoo hard to hit\n"); +#endif + return(f); /* too small a target! */ + } + multisamp(offsdir, 3, urand(sn*931+5827+ssn)); + for (i = 0; i < 3; i++) + offsdir[i] = or*(1. - 2.*offsdir[i]); + ssn++; + d = 1. - DOT(offsdir, onorm); + for (i = 0; i < 3; i++) { + sr.rorg[i] = oc[i] + offsdir[i] + d*onorm[i]; + sr.rdir[i] = -onorm[i]; + } + sr.rmax = 0.0; + rayorigin(&sr, NULL, PRIMARY, 1.0); + } while (!(*ofun[o->otype].funp)(o, &sr)); /* check against source */ - if (srcray(&sr, NULL, sn) == 0.0) - continue; + VCOPY(sr.rorg, sr.rop); /* starting from intersection */ + samplendx++; + if (si.sp >= si.np-1 || + !srcray(&sr, NULL, &si) || sr.rsrc != sn) { + si.sn = sn-1; /* reset index to our source */ + si.np = 0; + if (!srcray(&sr, NULL, &si) || sr.rsrc != sn) + continue; /* can't get there from here */ + } sr.revf = srcvalue; - rayvalue(&sr); + rayvalue(&sr); /* check sample validity */ if (bright(sr.rcol) <= FTINY) continue; - nok++; - /* check against obstructions */ - srcray(&sr, NULL, sn); + nok++; /* got sample; check obstructions */ + rayclear(&sr); + sr.revf = raytrace; rayvalue(&sr); - if (bright(sr.rcol) <= FTINY) - continue; - nhit++; + if (bright(sr.rcol) > FTINY) + nhit++; + if (nhit > 0 && nhit < nok) { +#ifdef DEBUG + fprintf(stderr, "\tpartially occluded\n"); +#endif + return(f); /* need to shadow test */ + } } - /* interpret results */ - if (nhit == 0) + if (nhit == 0) { +#ifdef DEBUG + fprintf(stderr, "\t0%% hit rate\n"); +#endif return(f | SSKIP); /* 0% hit rate: totally occluded */ - if (nhit == nok) - return(f & ~SFOLLOW); /* 100% hit rate: no occlusion */ - return(f); /* no comment */ + } +#ifdef DEBUG + fprintf(stderr, "\t100%% hit rate\n"); +#endif + return(f & ~SFOLLOW); /* 100% hit rate: no occlusion */ } #ifdef DEBUG +void virtverb(sn, fp) /* print verbose description of virtual source */ register int sn; FILE *fp; @@ -301,7 +409,7 @@ FILE *fp; fprintf(fp, "\tat (%f,%f,%f)\n", source[sn].sloc[0], source[sn].sloc[1], source[sn].sloc[2]); fprintf(fp, "\tlinked to source %d (%s)\n", - source[sn].sa.svnext, source[source[sn].sa.svnext].so->oname); + source[sn].sa.sv.sn, source[source[sn].sa.sv.sn].so->oname); if (source[sn].sflags & SFOLLOW) fprintf(fp, "\talways followed\n"); else