--- ray/src/hd/rhd_qtree.c 1997/11/20 18:03:43 3.3 +++ ray/src/hd/rhd_qtree.c 2003/07/21 22:30:18 3.24 @@ -1,34 +1,47 @@ -/* Copyright (c) 1997 Silicon Graphics, Inc. */ - #ifndef lint -static char SCCSid[] = "$SunId$ SGI"; +static const char RCSid[] = "$Id: rhd_qtree.c,v 3.24 2003/07/21 22:30:18 schorsch Exp $"; #endif - /* * Quadtree driver support routines. */ +#include + #include "standard.h" #include "rhd_qtree.h" + /* quantity of leaves to free at a time */ +#ifndef LFREEPCT +#define LFREEPCT 25 +#endif + /* maximum allowed angle difference (deg.) */ +#ifndef MAXANG +#define MAXANG 20 +#endif +#if MAXANG>0 +#define MAXDIFF2 ( MAXANG*MAXANG * (PI*PI/180./180.)) +#endif +#define abs(i) ((i) < 0 ? -(i) : (i)) + RTREE qtrunk; /* our quadtree trunk */ -double qtDepthEps = .02; /* epsilon to compare depths (z fraction) */ +double qtDepthEps = .05; /* epsilon to compare depths (z fraction) */ int qtMinNodesiz = 2; /* minimum node dimension (pixels) */ +struct rleaves qtL; /* our pile of leaves */ -static RLEAF *leafpile; /* our collection of leaf values */ -static int nleaves; /* count of leaves in our pile */ -static int bleaf, tleaf; /* bottom and top (next) leaf index (ring) */ +int rayqleft = 0; /* rays left to queue before flush */ +static int32 falleaves; /* our list of fallen leaves */ + +#define composted(li) (qtL.bl <= qtL.tl ? \ + ((li) < qtL.bl || (li) >= qtL.tl) : \ + ((li) < qtL.bl && (li) >= qtL.tl)) + #define TBUNDLESIZ 409 /* number of twigs in a bundle */ static RTREE **twigbundle; /* free twig blocks (NULL term.) */ static int nexttwig; /* next free twig */ -static RTREE emptytree; /* empty tree for test below */ -#define is_stump(t) (!bcmp((char *)(t), (char *)&emptytree, sizeof(RTREE))) - - static RTREE * newtwig() /* allocate a twig */ { @@ -42,7 +55,7 @@ newtwig() /* allocate a twig */ } bi = nexttwig / TBUNDLESIZ; if (twigbundle[bi] == NULL) { /* new block */ - twigbundle = (RTREE **)realloc((char *)twigbundle, + twigbundle = (RTREE **)realloc((void *)twigbundle, (bi+2)*sizeof(RTREE *)); if (twigbundle == NULL) goto memerr; @@ -63,39 +76,30 @@ int really; { register int i; - if (tmTop != NULL) - tmClearHisto(); - bzero((char *)&qtrunk, sizeof(RTREE)); - nexttwig = 0; + qtrunk.flgs = CH_ANY; /* chop down tree */ if (twigbundle == NULL) return; + i = (TBUNDLESIZ-1+nexttwig)/TBUNDLESIZ; + nexttwig = 0; if (!really) { /* just clear allocated blocks */ - for (i = 0; twigbundle[i] != NULL; i++) - bzero((char *)twigbundle[i], TBUNDLESIZ*sizeof(RTREE)); + while (i--) + memset((char *)twigbundle[i], '\0', TBUNDLESIZ*sizeof(RTREE)); return; } /* else "really" means free up memory */ for (i = 0; twigbundle[i] != NULL; i++) - free((char *)twigbundle[i]); - free((char *)twigbundle); + free((void *)twigbundle[i]); + free((void *)twigbundle); twigbundle = NULL; } -static RLEAF * -newleaf() /* allocate a leaf from our pile */ -{ - if (tleaf++ >= nleaves) /* get next leaf in ring */ - tleaf = 0; - if (tleaf == bleaf) /* need to shake some free */ - qtCompost(LFREEPCT); - return(leafpile + tleaf); -} +#define LEAFSIZ (3*sizeof(float)+sizeof(int32)+\ + sizeof(TMbright)+6*sizeof(BYTE)) - int qtAllocLeaves(n) /* allocate space for n leaves */ -int n; +register int n; { unsigned nbytes; register unsigned i; @@ -103,32 +107,41 @@ int n; qtFreeTree(0); /* make sure tree is empty */ if (n <= 0) return(0); - if (nleaves >= n) - return(nleaves); - else if (nleaves > 0) - free((char *)leafpile); + if (qtL.nl >= n) + return(qtL.nl); + else if (qtL.nl > 0) + free(qtL.base); /* round space up to nearest power of 2 */ - nbytes = n*sizeof(RLEAF) + 8; + nbytes = n*LEAFSIZ + 8; for (i = 1024; nbytes > i; i <<= 1) ; - n = (i - 8) / sizeof(RLEAF); - leafpile = (RLEAF *)malloc(n*sizeof(RLEAF)); - if (leafpile == NULL) - return(-1); - nleaves = n; - bleaf = tleaf = 0; - return(nleaves); + n = (i - 8) / LEAFSIZ; /* should we make sure n is even? */ + qtL.base = (char *)malloc(n*LEAFSIZ); + if (qtL.base == NULL) + return(0); + /* assign larger alignment types earlier */ + qtL.wp = (float (*)[3])qtL.base; + qtL.wd = (int32 *)(qtL.wp + n); + qtL.brt = (TMbright *)(qtL.wd + n); + qtL.chr = (BYTE (*)[3])(qtL.brt + n); + qtL.rgb = (BYTE (*)[3])(qtL.chr + n); + qtL.nl = n; + qtL.tml = qtL.bl = qtL.tl = 0; + falleaves = -1; + return(n); } +#undef LEAFSIZ + qtFreeLeaves() /* free our allocated leaves and twigs */ { qtFreeTree(1); /* free tree also */ - if (nleaves <= 0) + if (qtL.nl <= 0) return; - free((char *)leafpile); - leafpile = NULL; - nleaves = 0; + free(qtL.base); + qtL.base = NULL; + qtL.nl = 0; } @@ -139,15 +152,14 @@ register RTREE *tp; register int i, li; for (i = 0; i < 4; i++) - if (tp->flgs & BRF(i)) + if (tp->flgs & BRF(i)) { shaketree(tp->k[i].b); - else if (tp->k[i].l != NULL) { - li = tp->k[i].l - leafpile; - if (bleaf < tleaf ? (li < bleaf || li >= tleaf) : - (li < bleaf && li >= tleaf)) { - tmAddHisto(&tp->k[i].l->brt, 1, -1); - tp->k[i].l = NULL; - } + if (is_stump(tp->k[i].b)) + tp->flgs &= ~BRF(i); + } else if (tp->flgs & LFF(i)) { + li = tp->k[i].li; + if (composted(li)) + tp->flgs &= ~LFF(i); } } @@ -156,41 +168,53 @@ int qtCompost(pct) /* free up some leaves */ int pct; { - int nused, nclear; + register int32 *fl; + int nused, nclear, nmapped; /* figure out how many leaves to clear */ - nclear = nleaves * pct / 100; + nclear = qtL.nl * pct / 100; + nused = qtL.tl - qtL.bl; + if (nused <= 0) nused += qtL.nl; + nclear -= qtL.nl - nused; if (nclear <= 0) return(0); - nused = tleaf > bleaf ? tleaf-bleaf : tleaf+nleaves-bleaf; if (nclear >= nused) { /* clear them all */ qtFreeTree(0); - bleaf = tleaf = 0; + qtL.tml = qtL.bl = qtL.tl = 0; + falleaves = -1; return(nused); } /* else clear leaves from bottom */ - bleaf = (bleaf + nclear) % nleaves; - shaketree(&qtrunk); + nmapped = qtL.tml - qtL.bl; + if (nmapped < 0) nmapped += qtL.nl; + qtL.bl += nclear; + if (qtL.bl >= qtL.nl) qtL.bl -= qtL.nl; + if (nmapped <= nclear) qtL.tml = qtL.bl; + shaketree(&qtrunk); /* dereference composted leaves */ + for (fl = &falleaves; *fl >= 0; fl = qtL.wd + *fl) + while (composted(*fl)) + if ((*fl = qtL.wd[*fl]) < 0) + return(nclear); return(nclear); } -RLEAF * +int qtFindLeaf(x, y) /* find closest leaf to (x,y) */ int x, y; { register RTREE *tp = &qtrunk; - RLEAF *lp = NULL; + int li = -1; int x0=0, y0=0, x1=odev.hres, y1=odev.vres; int mx, my; register int q; /* check limits */ if (x < 0 || x >= odev.hres || y < 0 || y >= odev.vres) - return(NULL); + return(-1); /* find nearby leaf in our tree */ for ( ; ; ) { for (q = 0; q < 4; q++) /* find any leaf this level */ - if (!(tp->flgs & BRF(q)) && tp->k[q].l != NULL) { - lp = tp->k[q].l; + if (tp->flgs & LFF(q)) { + li = tp->k[q].li; break; } q = 0; /* which quadrant are we? */ @@ -204,30 +228,46 @@ int x, y; tp = tp->k[q].b; continue; } - if (tp->k[q].l != NULL) /* good shot! */ - return(tp->k[q].l); - return(lp); /* else return what we have */ + if (tp->flgs & LFF(q)) /* good shot! */ + return(tp->k[q].li); + return(li); /* else return what we have */ } } static -addleaf(lp) /* add a leaf to our tree */ -RLEAF *lp; +putleaf(li, drop) /* put a leaf in our tree */ +register int li; +int drop; { register RTREE *tp = &qtrunk; int x0=0, y0=0, x1=odev.hres, y1=odev.vres; - RLEAF *lo = NULL; + register int lo = -1; + double d2; int x, y, mx, my; double z; - FVECT ip, wp; + FVECT ip, wp, vd; register int q; - /* compute leaf location */ - VCOPY(wp, lp->wp); + /* check for dead leaf */ + if (!qtL.chr[li][1] && !(qtL.chr[li][0] | qtL.chr[li][2])) + return(0); + /* compute leaf location in view */ + VCOPY(wp, qtL.wp[li]); viewloc(ip, &odev.v, wp); if (ip[2] <= 0. || ip[0] < 0. || ip[0] >= 1. || ip[1] < 0. || ip[1] >= 1.) - return; + goto dropit; /* behind or outside view */ +#ifdef DEBUG + if (odev.v.type == VT_PAR | odev.v.vfore > FTINY) + error(INTERNAL, "bad view assumption in putleaf"); +#endif + for (q = 0; q < 3; q++) + vd[q] = (wp[q] - odev.v.vp[q])/ip[2]; + d2 = fdir2diff(qtL.wd[li], vd); +#ifdef MAXDIFF2 + if (d2 > MAXDIFF2) + goto dropit; /* leaf dir. too far off */ +#endif x = ip[0] * odev.hres; y = ip[1] * odev.vres; z = ip[2]; @@ -245,203 +285,140 @@ RLEAF *lp; tp = tp->k[q].b; continue; } - if (tp->k[q].l == NULL) { /* found stem for leaf */ - tp->k[q].l = lp; - tp->flgs |= CHF(q); - break; + if (!(tp->flgs & LFF(q))) { /* found stem for leaf */ + tp->k[q].li = li; + tp->flgs |= CHLFF(q); + return(1); } - /* check existing leaf */ - if (lo != tp->k[q].l) { - lo = tp->k[q].l; - VCOPY(wp, lo->wp); + if (lo != tp->k[q].li) { /* check old leaf */ + lo = tp->k[q].li; + VCOPY(wp, qtL.wp[lo]); viewloc(ip, &odev.v, wp); } /* is node minimum size? */ - if (x1-x0 <= qtMinNodesiz || y1-y0 <= qtMinNodesiz) { - if (z > (1.-qtDepthEps)*ip[2]) /* who is closer? */ - return; /* old one is */ - tp->k[q].l = lp; /* new one is */ + if (y1-y0 <= qtMinNodesiz || x1-x0 <= qtMinNodesiz) { + if (z > (1.+qtDepthEps)*ip[2]) + break; /* old one closer */ + if (z >= (1.-qtDepthEps)*ip[2] && + fdir2diff(qtL.wd[lo], vd) < d2) + break; /* old one better */ + tp->k[q].li = li; /* attach new */ tp->flgs |= CHF(q); - tmAddHisto(&lo->brt, 1, -1); /* drop old one */ + li = lo; /* drop old... */ break; } - tp->flgs |= CHBRF(q); /* else grow tree */ + tp->flgs &= ~LFF(q); /* else grow tree */ + tp->flgs |= CHBRF(q); tp = tp->k[q].b = newtwig(); - tp->flgs |= CH_ANY; /* all new */ q = 0; /* old leaf -> new branch */ mx = ip[0] * odev.hres; my = ip[1] * odev.vres; if (mx >= (x0 + x1) >> 1) q |= 01; if (my >= (y0 + y1) >> 1) q |= 02; - tp->k[q].l = lo; + tp->flgs = CH_ANY|LFF(q); /* all new */ + tp->k[q].li = lo; } - tmAddHisto(&lp->brt, 1, 1); /* add leaf to histogram */ +dropit: + if (drop) { + if (li+1 == (qtL.tl ? qtL.tl : qtL.nl)) + qtL.tl = li; /* special case */ + else { + qtL.chr[li][0] = qtL.chr[li][1] = qtL.chr[li][2] = 0; + qtL.wd[li] = falleaves; + falleaves = li; + } + } + return(li == lo); } -dev_value(c, p) /* add a pixel value to our output queue */ +dev_value(c, d, p) /* add a pixel value to our quadtree */ COLR c; -FVECT p; +FVECT d, p; { - register RLEAF *lp; - - lp = newleaf(); - VCOPY(lp->wp, p); - tmCvColrs(&lp->brt, lp->chr, c, 1); - addleaf(lp); + register int li; + int mapit; + /* grab a leaf */ + if (!imm_mode && falleaves >= 0) { /* check for fallen leaves */ + li = falleaves; + falleaves = qtL.wd[li]; + mapit = qtL.tml <= qtL.tl ? + (li < qtL.tml || li >= qtL.tl) : + (li < qtL.tml && li >= qtL.tl) ; + } else { /* else allocate new one */ + li = qtL.tl++; + if (qtL.tl >= qtL.nl) /* next leaf in ring */ + qtL.tl = 0; + if (qtL.tl == qtL.bl) /* need to shake some free */ + qtCompost(LFREEPCT); + mapit = 0; /* we'll map it later */ + } + if (p == NULL) + VSUM(qtL.wp[li], odev.v.vp, d, FHUGE); + else + VCOPY(qtL.wp[li], p); + qtL.wd[li] = encodedir(d); + tmCvColrs(&qtL.brt[li], qtL.chr[li], (COLR *)c, 1); + if (putleaf(li, 1)) { + if (mapit) + tmMapPixels((BYTE *)(qtL.rgb+li), qtL.brt+li, + (BYTE *)(qtL.chr+li), 1); + if (--rayqleft == 0) + dev_flush(); /* flush output */ + } } qtReplant() /* replant our tree using new view */ { register int i; - - if (bleaf == tleaf) /* anything to replant? */ + /* anything to replant? */ + if (qtL.bl == qtL.tl) return; - qtFreeTree(0); /* blow the tree away */ - /* now rebuild it */ - for (i = bleaf; i != tleaf; ) { - addleaf(leafpile+i); - if (++i >= nleaves) i = 0; + qtFreeTree(0); /* blow the old tree away */ + /* regrow it in new place */ + for (i = qtL.bl; i != qtL.tl; ) { + putleaf(i, 0); + if (++i >= qtL.nl) i = 0; } - tmComputeMapping(0., 0., 0.); /* update the display */ - qtUpdate(); } -static -redraw(ca, tp, x0, y0, x1, y1, l) /* redraw portion of a tree */ -BYTE ca[3]; /* returned average color */ -register RTREE *tp; -int x0, y0, x1, y1; -int l[2][2]; +qtMapLeaves(redo) /* map our leaves to RGB */ +int redo; { - int csm[3], nc; - BYTE rgb[3]; - int quads = CH_ANY; - int mx, my; - register int i; - /* compute midpoint */ - mx = (x0 + x1) >> 1; - my = (y0 + y1) >> 1; - /* see what to do */ - if (l[0][0] >= mx) - quads &= ~(CHF(2)|CHF(0)); - else if (l[0][1] <= mx) - quads &= ~(CHF(3)|CHF(1)); - if (l[1][0] >= my) - quads &= ~(CHF(1)|CHF(0)); - else if (l[1][1] <= my) - quads &= ~(CHF(3)|CHF(2)); - tp->flgs &= ~quads; /* mark them done */ - csm[0] = csm[1] = csm[2] = nc = 0; - /* do leaves first */ - for (i = 0; i < 4; i++) - if (quads & CHF(i) && !(tp->flgs & BRF(i)) && - tp->k[i].l != NULL) { - tmMapPixels(rgb, &tp->k[i].l->brt, tp->k[i].l->chr, 1); - dev_paintr(rgb, i&01 ? mx : x0, i&02 ? my : y0, - i&01 ? x1 : mx, i&02 ? y1 : my); - csm[0] += rgb[0]; csm[1] += rgb[1]; csm[2] += rgb[2]; - nc++; - quads &= ~CHF(i); - } - /* now do branches */ - for (i = 0; i < 4; i++) - if (quads & CHF(i) && tp->flgs & BRF(i)) { - redraw(rgb, tp->k[i].b, i&01 ? mx : x0, i&02 ? my : y0, - i&01 ? x1 : mx, i&02 ? y1 : my, l); - csm[0] += rgb[0]; csm[1] += rgb[1]; csm[2] += rgb[2]; - nc++; - quads &= ~CHF(i); - } - if (nc > 1) { - ca[0] = csm[0]/nc; ca[1] = csm[1]/nc; ca[2] = csm[2]/nc; + int aorg, alen, borg, blen; + /* recompute mapping? */ + if (redo) + qtL.tml = qtL.bl; + /* already done? */ + if (qtL.tml == qtL.tl) + return(1); + /* compute segments */ + aorg = qtL.tml; + if (qtL.tl >= aorg) { + alen = qtL.tl - aorg; + blen = 0; } else { - ca[0] = csm[0]; ca[1] = csm[1]; ca[2] = csm[2]; + alen = qtL.nl - aorg; + borg = 0; + blen = qtL.tl; } - if (!quads) return; - /* fill in gaps with average */ - for (i = 0; i < 4; i++) - if (quads & CHF(i)) - dev_paintr(ca, i&01 ? mx : x0, i&02 ? my : y0, - i&01 ? x1 : mx, i&02 ? y1 : my); -} - - -static -update(ca, tp, x0, y0, x1, y1) /* update tree display as needed */ -BYTE ca[3]; /* returned average color */ -register RTREE *tp; -int x0, y0, x1, y1; -{ - int csm[3], nc; - BYTE rgb[3]; - int gaps = 0; - int mx, my; - register int i; - /* compute midpoint */ - mx = (x0 + x1) >> 1; - my = (y0 + y1) >> 1; - csm[0] = csm[1] = csm[2] = nc = 0; - /* do leaves first */ - for (i = 0; i < 4; i++) - if ((tp->flgs & CHBRF(i)) == CHF(i)) { - if (tp->k[i].l == NULL) { - gaps |= 1<k[i].l->brt, tp->k[i].l->chr, 1); - dev_paintr(rgb, i&01 ? mx : x0, i&02 ? my : y0, - i&01 ? x1 : mx, i&02 ? y1 : my); - csm[0] += rgb[0]; csm[1] += rgb[1]; csm[2] += rgb[2]; - nc++; - } - /* now do branches */ - for (i = 0; i < 4; i++) - if ((tp->flgs & CHBRF(i)) == CHBRF(i)) { - update(rgb, tp->k[i].b, i&01 ? mx : x0, i&02 ? my : y0, - i&01 ? x1 : mx, i&02 ? y1 : my); - csm[0] += rgb[0]; csm[1] += rgb[1]; csm[2] += rgb[2]; - nc++; - } - if (nc > 1) { - ca[0] = csm[0]/nc; ca[1] = csm[1]/nc; ca[2] = csm[2]/nc; - } else { - ca[0] = csm[0]; ca[1] = csm[1]; ca[2] = csm[2]; + /* (re)compute tone mapping? */ + if (qtL.tml == qtL.bl) { + tmClearHisto(); + tmAddHisto(qtL.brt+aorg, alen, 1); + if (blen > 0) + tmAddHisto(qtL.brt+borg, blen, 1); + if (tmComputeMapping(0., 0., 0.) != TM_E_OK) + return(0); } - /* fill in gaps with average */ - for (i = 0; gaps && i < 4; gaps >>= 1, i++) - if (gaps & 01) - dev_paintr(ca, i&01 ? mx : x0, i&02 ? my : y0, - i&01 ? x1 : mx, i&02 ? y1 : my); - tp->flgs &= ~CH_ANY; /* all done */ -} - - -qtRedraw(x0, y0, x1, y1) /* redraw part of our screen */ -int x0, y0, x1, y1; -{ - int lim[2][2]; - BYTE ca[3]; - - if (is_stump(&qtrunk)) - return; - if ((lim[0][0]=x0) == 0 & (lim[1][0]=y0) == 0 & - (lim[0][1]=x1) == odev.hres & (lim[1][1]=y1) == odev.vres || - tmTop->lumap == NULL) - tmComputeMapping(0., 0., 0.); - redraw(ca, &qtrunk, 0, 0, odev.hres, odev.vres, lim); -} - - -qtUpdate() /* update our tree display */ -{ - BYTE ca[3]; - - if (is_stump(&qtrunk)) - return; - if (tmTop->lumap == NULL) - tmComputeMapping(0., 0., 0.); - update(ca, &qtrunk, 0, 0, odev.hres, odev.vres); + if (tmMapPixels((BYTE *)(qtL.rgb+aorg), qtL.brt+aorg, + (BYTE *)(qtL.chr+aorg), alen) != TM_E_OK) + return(0); + if (blen > 0) + tmMapPixels((BYTE *)(qtL.rgb+borg), qtL.brt+borg, + (BYTE *)(qtL.chr+borg), blen); + qtL.tml = qtL.tl; + return(1); }