int o_face( /* determine if face intersects cube */ OBJREC *o, CUBE *cu ) { FVECT cumin, cumax; FVECT v1, v2; double d1, d2; int vloc; register FACE *f; register int i, j; /* get face arguments */ f = getface(o); if (f->area == 0.0) /* empty face */ return(O_MISS); /* compute cube boundaries */ for (j = 0; j < 3; j++) cumax[j] = (cumin[j] = cu->cuorg[j]-FTINY) + cu->cusize + 2.0*FTINY; vloc = ABOVE | BELOW; /* check vertices */ for (i = 0; i < f->nv; i++) if ( (j = plocate(VERTEX(f,i), cumin, cumax)) ) vloc &= j; else return(O_HIT); /* vertex inside */ if (vloc) /* all to one side */ return(O_MISS); for (i = 0; i < f->nv; i++) { /* check edges */ if ((j = i + 1) >= f->nv) j = 0; /* wrap around */ VCOPY(v1, VERTEX(f,i)); /* clip modifies */ VCOPY(v2, VERTEX(f,j)); /* the vertices! */ if (clip(v1, v2, cumin, cumax)) return(O_HIT); /* edge inside */ } /* see if cube cuts plane */ for (j = 0; j < 3; j++) if (f->norm[j] > 0.0) { v1[j] = cumin[j]; v2[j] = cumax[j]; } else { v1[j] = cumax[j]; v2[j] = cumin[j]; } if ((d1 = DOT(v1, f->norm) - f->offset) > FTINY) return(O_MISS); if ((d2 = DOT(v2, f->norm) - f->offset) < -FTINY) return(O_MISS); /* intersect face */ for (j = 0; j < 3; j++) v1[j] = (v1[j]*d2 - v2[j]*d1)/(d2 - d1); if (inface(v1, f)) return(O_HIT); return(O_MISS); /* no intersection */ }
static int /* cast source ray to first blocker */ castshadow(int sn, FVECT rorg, FVECT rdir) { RAY rt; VCOPY(rt.rorg, rorg); VCOPY(rt.rdir, rdir); rt.rmax = 0; rayorigin(&rt, PRIMARY, NULL, NULL); /* check for intersection */ while (localhit(&rt, &thescene)) { RAY rt1 = rt; /* pretend we were aimed at source */ rt1.crtype |= rt1.rtype = SHADOW; rt1.rdir[0] = -rt.rdir[0]; rt1.rdir[1] = -rt.rdir[1]; rt1.rdir[2] = -rt.rdir[2]; rt1.rod = -rt.rod; VSUB(rt1.rorg, rt.rop, rt.rdir); rt1.rot = 1.; rt1.rsrc = sn; /* record blocker */ if (srcblocker(&rt1)) return(1); /* move past failed blocker */ VSUM(rt.rorg, rt.rop, rt.rdir, FTINY); rayclear(&rt); /* & try again... */ } return(0); /* found no blockers */ }
/* Get a vector from stdin */ int getvec(FVECT vec) { float vf[3]; double vd[3]; char buf[32]; int i; switch (inpfmt) { case 'a': /* ascii */ for (i = 0; i < 3; i++) { if (fgetword(buf, sizeof(buf), stdin) == NULL || !isflt(buf)) return(-1); vec[i] = atof(buf); } break; case 'f': /* binary float */ if (fread((char *)vf, sizeof(float), 3, stdin) != 3) return(-1); VCOPY(vec, vf); break; case 'd': /* binary double */ if (fread((char *)vd, sizeof(double), 3, stdin) != 3) return(-1); VCOPY(vec, vd); break; default: error(CONSISTENCY, "botched input format"); } return(0); }
int getinterest( /* get area of interest */ char *s, int direc, FVECT vec, double *mp ) { int x, y; RAY thisray; int i; if (sscanf(s, "%lf", mp) != 1) *mp = 1.0; else if (*mp < -FTINY) /* negative zoom is reduction */ *mp = -1.0 / *mp; else if (*mp <= FTINY) { /* too small */ error(COMMAND, "illegal magnification"); return(-1); } if (!sscanvec(sskip(s), vec)) { if (dev->getcur == NULL) return(-1); (*dev->comout)("Pick view center\n"); if ((*dev->getcur)(&x, &y) == ABORT) return(-1); if ((thisray.rmax = viewray(thisray.rorg, thisray.rdir, &ourview, (x+.5)/hresolu, (y+.5)/vresolu)) < -FTINY) { error(COMMAND, "not on image"); return(-1); } if (!direc || ourview.type == VT_PAR) { rayorigin(&thisray, PRIMARY, NULL, NULL); if (!localhit(&thisray, &thescene)) { error(COMMAND, "not a local object"); return(-1); } } if (direc) if (ourview.type == VT_PAR) for (i = 0; i < 3; i++) vec[i] = thisray.rop[i] - ourview.vp[i]; else VCOPY(vec, thisray.rdir); else VCOPY(vec, thisray.rop); } else if (direc) { for (i = 0; i < 3; i++) vec[i] -= ourview.vp[i]; if (normalize(vec) == 0.0) { error(COMMAND, "point at view origin"); return(-1); } } return(0); }
extern int o_face( /* compute intersection with polygonal face */ OBJREC *o, register RAY *r ) { double rdot; /* direction . normal */ double t; /* distance to intersection */ FVECT pisect; /* intersection point */ register FACE *f; /* face record */ f = getface(o); /* * First, we find the distance to the plane containing the * face. If this distance is less than zero or greater * than a previous intersection, we return. Otherwise, * we determine whether in fact the ray intersects the * face. The ray intersects the face if the * point of intersection with the plane of the face * is inside the face. */ /* compute dist. to plane */ rdot = -DOT(r->rdir, f->norm); if (rdot <= FTINY && rdot >= -FTINY) /* ray parallels plane */ t = FHUGE; else t = (DOT(r->rorg, f->norm) - f->offset) / rdot; if (t <= FTINY || t >= r->rot) /* not good enough */ return(0); /* compute intersection */ VSUM(pisect, r->rorg, r->rdir, t); if (!inface(pisect, f)) /* ray intersects face? */ return(0); r->ro = o; r->rot = t; VCOPY(r->rop, pisect); VCOPY(r->ron, f->norm); r->rod = rdot; r->pert[0] = r->pert[1] = r->pert[2] = 0.0; r->uv[0] = r->uv[1] = 0.0; r->rox = NULL; return(1); /* hit */ }
/* Returns photon position as sorting key for OOC_Octree & friends (notably * for Morton code generation). * !!! Uses type conversion from float via TEMPORARY storage; * !!! THIS IS NOT THREAD SAFE! * !!! RETURNED KEY PERSISTS ONLY IF COPIED BEFORE NEXT CALL! */ RREAL *OOC_PhotonKey (const void *p) { static FVECT photonPos; /* Temp storage for type conversion */ VCOPY(photonPos, ((Photon*)p) -> pos); return photonPos; }
void OOC_Find1Photon (struct PhotonMap* pmap, const FVECT pos, const FVECT norm, Photon *photon) { OOC_SearchFilter filt; OOC_FilterData filtData; float n [3]; /* Lazily init OOC cache */ if (!pmap -> store.cache) OOC_InitPhotonCache(pmap); /* Set up filter callback */ filtData.pmap = pmap; if (norm) VCOPY(n, norm); filtData.norm = norm ? n : NULL; filt.data = &filtData; filt.func = OOC_FilterPhoton; pmap -> maxDist2 = OOC_Find1Nearest(&pmap -> store, OOC_ROOT(&pmap -> store), 0, pmap -> store.org, pmap -> store.size, pos, &filt, photon, pmap -> maxDist2); if (pmap -> maxDist2 < 0) error(INTERNAL, "failed 1-NN photon lookup in OOC_Find1Photon"); }
extern double raynormal( /* compute perturbed normal for ray */ FVECT norm, RAY *r ) { double newdot; int i; /* The perturbation is added to the surface normal to obtain * the new normal. If the new normal would affect the surface * orientation wrt. the ray, a correction is made. The method is * still fraught with problems since reflected rays and similar * directions calculated from the surface normal may spawn rays behind * the surface. The only solution is to curb textures at high * incidence (namely, keep DOT(rdir,pert) < Rdot). */ for (i = 0; i < 3; i++) norm[i] = r->ron[i] + r->pert[i]; if (normalize(norm) == 0.0) { objerror(r->ro, WARNING, "illegal normal perturbation"); VCOPY(norm, r->ron); return(r->rod); } newdot = -DOT(norm, r->rdir); if ((newdot > 0.0) != (r->rod > 0.0)) { /* fix orientation */ for (i = 0; i < 3; i++) norm[i] += 2.0*newdot*r->rdir[i]; newdot = -newdot; } return(newdot); }
/* Compute anisotropic radii and eigenvector directions */ static void eigenvectors(FVECT uv[2], float ra[2], FVECT hessian[3]) { double hess2[2][2]; FVECT a, b; double evalue[2], slope1, xmag1; int i; /* project Hessian to sample plane */ for (i = 3; i--; ) { a[i] = DOT(hessian[i], uv[0]); b[i] = DOT(hessian[i], uv[1]); } hess2[0][0] = DOT(uv[0], a); hess2[0][1] = DOT(uv[0], b); hess2[1][0] = DOT(uv[1], a); hess2[1][1] = DOT(uv[1], b); /* compute eigenvalue(s) */ i = quadratic(evalue, 1.0, -hess2[0][0]-hess2[1][1], hess2[0][0]*hess2[1][1]-hess2[0][1]*hess2[1][0]); if (i == 1) /* double-root (circle) */ evalue[1] = evalue[0]; if (!i || ((evalue[0] = fabs(evalue[0])) <= FTINY*FTINY) | ((evalue[1] = fabs(evalue[1])) <= FTINY*FTINY) ) { ra[0] = ra[1] = maxarad; return; } if (evalue[0] > evalue[1]) { ra[0] = sqrt(sqrt(4.0/evalue[0])); ra[1] = sqrt(sqrt(4.0/evalue[1])); slope1 = evalue[1]; } else { ra[0] = sqrt(sqrt(4.0/evalue[1])); ra[1] = sqrt(sqrt(4.0/evalue[0])); slope1 = evalue[0]; } /* compute unit eigenvectors */ if (fabs(hess2[0][1]) <= FTINY) return; /* uv OK as is */ slope1 = (slope1 - hess2[0][0]) / hess2[0][1]; xmag1 = sqrt(1.0/(1.0 + slope1*slope1)); for (i = 3; i--; ) { b[i] = xmag1*uv[0][i] + slope1*xmag1*uv[1][i]; a[i] = slope1*xmag1*uv[0][i] - xmag1*uv[1][i]; } VCOPY(uv[0], a); VCOPY(uv[1], b); }
void getorigin( /* origin viewpoint */ char *s ) { VIEW nv = ourview; double d; /* get new view origin */ if (sscanf(s, "%lf %lf", &d, &d) == 1) { /* just moving some distance */ VSUM(nv.vp, nv.vp, nv.vdir, d); } else if (!sscanvec(s, nv.vp)) { int x, y; /* need to pick origin */ RAY thisray; if (dev->getcur == NULL) return; (*dev->comout)("Pick point on surface for new origin\n"); if ((*dev->getcur)(&x, &y) == ABORT) return; if ((thisray.rmax = viewray(thisray.rorg, thisray.rdir, &ourview, (x+.5)/hresolu, (y+.5)/vresolu)) < -FTINY) { error(COMMAND, "not on image"); return; } rayorigin(&thisray, PRIMARY, NULL, NULL); if (!localhit(&thisray, &thescene)) { error(COMMAND, "not a local object"); return; } if (thisray.rod < 0.0) /* don't look through other side */ flipsurface(&thisray); VSUM(nv.vp, thisray.rop, thisray.ron, 20.0*FTINY); VCOPY(nv.vdir, thisray.ron); } else if (!sscanvec(sskip2(s,3), nv.vdir) || normalize(nv.vdir) == 0.0) VCOPY(nv.vdir, ourview.vdir); d = DOT(nv.vdir, nv.vup); /* need different up vector? */ if (d*d >= 1.-2.*FTINY) { int i; nv.vup[0] = nv.vup[1] = nv.vup[2] = 0.0; for (i = 3; i--; ) if (nv.vdir[i]*nv.vdir[i] < 0.34) break; nv.vup[i] = 1.; } newview(&nv); }
void xf_xfmvect(FVECT v1, FVECT v2) /* transform a vector using current matrix */ { if (xf_context == NULL) { VCOPY(v1, v2); return; } multv3(v1, v2, xf_context->xf.xfm); }
void xf_xfmpoint(FVECT v1, FVECT v2) /* transform a point by the current matrix */ { if (xf_context == NULL) { VCOPY(v1, v2); return; } multp3(v1, v2, xf_context->xf.xfm); }
void OOC_BuildPhotonMap (struct PhotonMap *pmap, unsigned numProc) { FILE *leafFile; char leafFname [1024]; FVECT d, octOrg; int i; RREAL octSize = 0; /* Determine octree size and origin from pmap extent and init octree */ VCOPY(octOrg, pmap -> minPos); VSUB(d, pmap -> maxPos, pmap -> minPos); for (i = 0; i < 3; i++) if (octSize < d [i]) octSize = d [i]; if (octSize < FTINY) error(INTERNAL, "zero octree size in OOC_BuildPhotonMap"); /* Derive leaf filename from photon map and open file */ strncpy(leafFname, pmap -> fileName, sizeof(leafFname)); strncat(leafFname, PMAP_OOC_LEAFSUFFIX, sizeof(leafFname) - strlen(leafFname) - 1); if (!(leafFile = fopen(leafFname, "w+b"))) error(SYSTEM, "failed to open leaf file in OOC_BuildPhotonMap"); #ifdef DEBUG_OOC eputs("Sorting photons by Morton code...\n"); #endif /* Sort photons in heapfile by Morton code and write to leaf file */ if (OOC_Sort(pmap -> heap, leafFile, PMAP_OOC_NUMBLK, PMAP_OOC_BLKSIZE, numProc, sizeof(Photon), octOrg, octSize, OOC_PhotonKey)) error(INTERNAL, "failed out-of-core photon sort in OOC_BuildPhotonMap"); /* Init and build octree */ OOC_Init(&pmap -> store, sizeof(Photon), octOrg, octSize, OOC_PhotonKey, leafFile); #ifdef DEBUG_OOC eputs("Checking leaf file consistency...\n"); OOC_CheckKeys(leafFile, &pmap -> store); eputs("Building out-of-core octree...\n"); #endif if (!OOC_Build(&pmap -> store, PMAP_OOC_LEAFMAX, PMAP_OOC_MAXDEPTH)) error(INTERNAL, "failed out-of-core octree build in OOC_BuildPhotonMap"); #ifdef DEBUG_OOC eputs("Checking out-of-core octree consistency...\n"); if (OOC_Check(&pmap -> store, OOC_ROOT(&pmap -> store), octOrg, octSize, 0)) error(INTERNAL, "inconsistent out-of-core octree; Time4Harakiri"); #endif }
/* Transform and normalize direction (column) vector */ SDError SDmapDir(FVECT resVec, RREAL vMtx[3][3], const FVECT inpVec) { FVECT vTmp; if ((resVec == NULL) | (inpVec == NULL)) return SDEargument; if (vMtx == NULL) { /* assume they just want to normalize */ if (resVec != inpVec) VCOPY(resVec, inpVec); return (normalize(resVec) > 0) ? SDEnone : SDEargument; } vTmp[0] = DOT(vMtx[0], inpVec); vTmp[1] = DOT(vMtx[1], inpVec); vTmp[2] = DOT(vMtx[2], inpVec); if (normalize(vTmp) == 0) return SDEargument; VCOPY(resVec, vTmp); return SDEnone; }
int nonplanar( /* are vertices non-planar? */ int ac, char **av ) { VNDX vi; RREAL *p0, *p1; FVECT v1, v2, nsum, newn; double d; int i; if (!cvtndx(vi, av[0])) return(0); if (!flatten && vi[2] >= 0) return(1); /* has interpolated normals */ if (ac < 4) return(0); /* it's a triangle! */ /* set up */ p0 = vlist[vi[0]]; if (!cvtndx(vi, av[1])) return(0); /* error gets caught later */ nsum[0] = nsum[1] = nsum[2] = 0.; p1 = vlist[vi[0]]; fvsum(v2, p1, p0, -1.0); for (i = 2; i < ac; i++) { VCOPY(v1, v2); if (!cvtndx(vi, av[i])) return(0); p1 = vlist[vi[0]]; fvsum(v2, p1, p0, -1.0); fcross(newn, v1, v2); if (normalize(newn) == 0.0) { if (i < 3) return(1); /* can't deal with this */ fvsum(nsum, nsum, nsum, 1./(i-2)); continue; } d = fdot(newn,nsum); if (d >= 0) { if (d < (1.0-FTINY)*(i-2)) return(1); fvsum(nsum, nsum, newn, 1.0); } else { if (d > -(1.0-FTINY)*(i-2)) return(1); fvsum(nsum, nsum, newn, -1.0); } } return(0); }
extern double makeambient( /* make a new ambient value for storage */ COLOR acol, RAY *r, FVECT rn, int al ) { AMBVAL amb; FVECT gp, gd; int i; amb.weight = 1.0; /* compute weight */ for (i = al; i-- > 0; ) amb.weight *= AVGREFL; if (r->rweight < 0.1*amb.weight) /* heuristic override */ amb.weight = 1.25*r->rweight; setcolor(acol, AVGREFL, AVGREFL, AVGREFL); /* compute ambient */ amb.rad = doambient(acol, r, amb.weight, gp, gd); if (amb.rad <= FTINY) { setcolor(acol, 0.0, 0.0, 0.0); return(0.0); } scalecolor(acol, 1./AVGREFL); /* undo assumed reflectance */ /* store value */ VCOPY(amb.pos, r->rop); VCOPY(amb.dir, r->ron); amb.lvl = al; copycolor(amb.val, acol); VCOPY(amb.gpos, gp); VCOPY(amb.gdir, gd); /* insert into tree */ avsave(&amb); /* and save to file */ if (rn != r->ron) extambient(acol, &amb, r->rop, rn); /* texture */ return(amb.rad); }
/* Compute World->BSDF transform from surface normal and up (Y) vector */ SDError SDcompXform(RREAL vMtx[3][3], const FVECT sNrm, const FVECT uVec) { if ((vMtx == NULL) | (sNrm == NULL) | (uVec == NULL)) return SDEargument; VCOPY(vMtx[2], sNrm); if (normalize(vMtx[2]) == 0) return SDEargument; fcross(vMtx[0], uVec, vMtx[2]); if (normalize(vMtx[0]) == 0) return SDEargument; fcross(vMtx[1], vMtx[2], vMtx[0]); return SDEnone; }
extern void raytrans( /* transmit ray as is */ RAY *r ) { RAY tr; if (rayorigin(&tr, TRANS, r, NULL) == 0) { VCOPY(tr.rdir, r->rdir); rayvalue(&tr); copycolor(r->rcol, tr.rcol); r->rt = r->rot + tr.rt; } }
void hdcell( /* compute cell coordinates */ FVECT cp[4], /* returned (may be passed as FVECT cp[2][2]) */ HOLO *hp, GCOORD *gc ) { RREAL *v; double d; /* compute common component */ VCOPY(cp[0], hp->orig); if (gc->w & 1) { v = hp->xv[gc->w>>1]; cp[0][0] += v[0]; cp[0][1] += v[1]; cp[0][2] += v[2]; }
static int moveview( /* move our view */ int dx, int dy, int mov, int orb ) { VIEW nv; FVECT odir, v1, wp; double d; /* start with old view */ nv = thisview; /* change view direction */ if ((d = viewray(v1, odir, &thisview, (dx+.5)/hres, (dy+.5)/vres)) < -FTINY) return(0); /* outside view */ if (mov | orb) { if (!getintersect(wp, v1, odir, d)) return(0); VSUM(odir, wp, nv.vp, -1.); } else VCOPY(nv.vdir, odir); if (orb && mov) { /* orbit left/right */ spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov); VSUM(nv.vp, wp, odir, -1.); spinvector(nv.vdir, nv.vdir, nv.vup, d); } else if (orb) { /* orbit up/down */ if (geodesic(odir, odir, nv.vup, d=MOVDEG*PI/180.*orb, GEOD_RAD) == 0.0) return(0); VSUM(nv.vp, wp, odir, -1.); geodesic(nv.vdir, nv.vdir, nv.vup, d, GEOD_RAD); } else if (mov) { /* move forward/backward */ d = MOVPCT/100. * mov; VSUM(nv.vp, nv.vp, odir, d); } if (!mov ^ !orb && headlocked) { /* restore head height */ VSUM(v1, thisview.vp, nv.vp, -1.); d = DOT(v1, thisview.vup); VSUM(nv.vp, nv.vp, thisview.vup, d); } if (setview(&nv) != NULL) return(0); /* illegal view */ dev_view(&nv); return(1); }
static PhotonPrimaryIdx newPhotonPrimary (PhotonMap *pmap, const RAY *primRay, FILE *primHeap) /* Add primary ray for emitted photon and save light source index, origin on * source, and emitted direction; used by contrib photons. The current * primary is stored in pmap -> lastPrimary. If the previous primary * contributed photons (has srcIdx >= 0), it's appended to primHeap. If * primRay == NULL, the current primary is still flushed, but no new primary * is set. Returns updated primary counter pmap -> numPrimary. */ { if (!pmap || !primHeap) return 0; /* Check if last primary ray has spawned photons (srcIdx >= 0, see * newPhoton()), in which case we save it to the primary heap file * before clobbering it */ if (pmap -> lastPrimary.srcIdx >= 0) { if (!fwrite(&pmap -> lastPrimary, sizeof(PhotonPrimary), 1, primHeap)) error(SYSTEM, "failed writing photon primary in newPhotonPrimary"); pmap -> numPrimary++; if (pmap -> numPrimary > PMAP_MAXPRIMARY) error(INTERNAL, "photon primary overflow in newPhotonPrimary"); } /* Mark unused with negative source index until path spawns a photon (see * newPhoton()) */ pmap -> lastPrimary.srcIdx = -1; if (primRay) { FVECT dvec; #ifdef PMAP_PRIMARYDIR /* Reverse incident direction to point to light source */ dvec [0] = -primRay -> rdir [0]; dvec [1] = -primRay -> rdir [1]; dvec [2] = -primRay -> rdir [2]; pmap -> lastPrimary.dir = encodedir(dvec); #endif #ifdef PMAP_PRIMARYPOS VCOPY(pmap -> lastPrimary.pos, primRay -> rop); #endif } return pmap -> numPrimary; }
/* Sample an individual BSDF component */ SDError SDsampComponent(SDValue *sv, FVECT ioVec, double randX, SDComponent *sdc) { float coef[SDmaxCh]; SDError ec; FVECT inVec; const SDCDst *cd; double d; int n; /* check arguments */ if ((sv == NULL) | (ioVec == NULL) | (sdc == NULL)) return SDEargument; /* get cumulative distribution */ VCOPY(inVec, ioVec); cd = (*sdc->func->getCDist)(inVec, sdc); if (cd == NULL) return SDEmemory; if (cd->cTotal <= 1e-6) { /* anything to sample? */ sv->spec = c_dfcolor; sv->cieY = .0; memset(ioVec, 0, 3*sizeof(double)); return SDEnone; } sv->cieY = cd->cTotal; /* compute sample direction */ ec = (*sdc->func->sampCDist)(ioVec, randX, cd); if (ec) return ec; /* get BSDF color */ n = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc); if (n <= 0) { strcpy(SDerrorDetail, "BSDF sample value error"); return SDEinternal; } sv->spec = sdc->cspec[0]; d = coef[0]; while (--n) { c_cmix(&sv->spec, d, &sv->spec, coef[n], &sdc->cspec[n]); d += coef[n]; } /* make sure everything is set */ c_ccvt(&sv->spec, C_CSXY+C_CSSPEC); return SDEnone; }
char * getvertid( /* get/set vertex ID for this point */ char *vname, FVECT vp ) { static char vkey[VKLEN]; register LUENT *lp; register int i, vndx; vclock++; /* increment counter */ mkvkey(vkey, vp); if ((lp = lu_find(&vertab, vkey)) == NULL) goto memerr; if (lp->data == NULL) { /* allocate new vertex entry */ if (lp->key != NULL) /* reclaim deleted entry */ vertab.ndel--; else { if ((lp->key = (char *)malloc(VKLEN)) == NULL) goto memerr; strcpy(lp->key, vkey); } vndx = 0; /* find oldest vertex */ for (i = 1; i < NVERTS; i++) if (vert[i].lused < vert[vndx].lused) vndx = i; if (vert[vndx].lused) { /* free old entry first */ mkvkey(vkey, vert[vndx].p); lu_delete(&vertab, vkey); } VCOPY(vert[vndx].p, vp); /* assign it */ printf("v v%d =\n\tp %.15g %.15g %.15g\n", /* print it */ vndx, vp[0], vp[1], vp[2]); lp->data = (char *)&vert[vndx]; /* set it */ } else vndx = (struct vert *)lp->data - vert; vert[vndx].lused = vclock; /* record this use */ sprintf(vname, "v%d", vndx); return(vname); memerr: fputs("Out of memory in getvertid!\n", stderr); exit(1); }
/* Rotate RBF to correspond to given incident vector */ void rotate_rbf(RBFNODE *rbf, const FVECT invec) { static const FVECT vnorm = {.0, .0, 1.}; const double phi = atan2(invec[1],invec[0]) - atan2(rbf->invec[1],rbf->invec[0]); FVECT outvec; int pos[2]; int n; for (n = ((-.01 > phi) | (phi > .01))*rbf->nrbf; n-- > 0; ) { ovec_from_pos(outvec, rbf->rbfa[n].gx, rbf->rbfa[n].gy); spinvector(outvec, outvec, vnorm, phi); pos_from_vec(pos, outvec); rbf->rbfa[n].gx = pos[0]; rbf->rbfa[n].gy = pos[1]; } VCOPY(rbf->invec, invec); }
R8 V1AIpart( const IX nv, const VERTEX3D p2[], const VERTEX3D *p1, const DIRCOS *u1 ) /* nv number of vertices/edges of surface (polygon) P2 * p2 coordinates of vertices of surface (polygon) P2 * p1 coordinates of surface (point) P1 * u1 components of unit vector normal to surface P1 */ { IX n; /* edge number */ VECTOR3D A, /* A = vector from P1 to P2[n-1]; |A| > 0 */ B, /* B = vector from P1 to P2[n]; |B| > 0 */ C; /* C = vector cross product of A and B */ R8 UdotC; /* dot product of U and C; always >= 0 */ R8 sum=0; /* sum of line integrals */ /* Initialization */ n = nv - 1; VECTOR( p1, (p2+n), (&B) ); /* vector B */ /* For all edges of polygon p2: */ for( n=0; n<nv; n++ ) { VCOPY( (&B), (&A) ); /* A = old B */ VECTOR( p1, (p2+n), (&B) ); /* vector B */ VCROSS( (&A), (&B), (&C) ); /* C = A cross B */ UdotC = VDOT( u1, (&C) ); /* U dot C */ if( fabs(UdotC) > EPS2 ) { R8 Clen = VLEN( (&C) ); /* | C | */ if( Clen > EPS2 ) { /* gamma = angle between A and B; 0 < gamma < 180 */ R8 gamma = PId2 - atan( VDOT( (&A), (&B) ) / Clen ); sum += UdotC * gamma / Clen; } else error( 3, __FILE__, __LINE__, "View1AI failed, call George", "" ); } } /* end edge loop */ sum *= PIt2inv; /* Divide by 2*pi */ return sum; } /* end of V1AIpart
static void add_holo( /* register a new holodeck section */ HDGRID *hdg, char *gfn, char *pfn ) { VIEW nv; double d; register int hd; for (hd = 0; hd < HDMAX && hdlist[hd] != NULL; hd++) ; if (hd >= HDMAX) error(INTERNAL, "too many holodeck sections in add_holo"); hdlist[hd] = (HOLO *)malloc(sizeof(HOLO)); if (hdlist[hd] == NULL) error(SYSTEM, "out of memory in add_holo"); memcpy((void *)hdlist[hd], (void *)hdg, sizeof(HDGRID)); hdcompgrid(hdlist[hd]); hdgfn[hd] = savestr(gfn); hdpfn[hd] = pfn && *pfn ? savestr(pfn) : (char *)NULL; if (hd) return; /* set initial viewpoint */ nv = odev.v; VSUM(nv.vp, hdlist[0]->orig, hdlist[0]->xv[0], 0.5); VSUM(nv.vp, nv.vp, hdlist[0]->xv[1], 0.5); VSUM(nv.vp, nv.vp, hdlist[0]->xv[2], 0.5); fcross(nv.vdir, hdlist[0]->xv[1], hdlist[0]->xv[2]); VCOPY(nv.vup, hdlist[0]->xv[2]); if (do_outside) { normalize(nv.vdir); d = VLEN(hdlist[0]->xv[1]); d += VLEN(hdlist[0]->xv[2]); VSUM(nv.vp, nv.vp, nv.vdir, -d); } new_view(&nv); }
static void avinsert( /* insert ambient value in our tree */ void *av ) { AMBTREE *at; AMBVAL *ap; AMBVAL avh; FVECT ck0; double s; int branch; int i; if (((AMBVAL*)av)->rad <= FTINY) error(CONSISTENCY, "zero ambient radius in avinsert"); at = &atrunk; VCOPY(ck0, thescene.cuorg); s = thescene.cusize; while (s*(OCTSCALE/2) > ((AMBVAL*)av)->rad*ambacc) { if (at->kid == NULL) if ((at->kid = newambtree()) == NULL) error(SYSTEM, "out of memory in avinsert"); s *= 0.5; branch = 0; for (i = 0; i < 3; i++) if (((AMBVAL*)av)->pos[i] > ck0[i] + s) { ck0[i] += s; branch |= 1 << i; } at = at->kid + branch; } avh.next = at->alist; /* order by increasing level */ for (ap = &avh; ap->next != NULL; ap = ap->next) if (ap->next->lvl >= ((AMBVAL*)av)->lvl) break; ((AMBVAL*)av)->next = ap->next; ap->next = (AMBVAL*)av; at->alist = avh.next; }
static int o_cube( /* determine if cubes intersect */ CUBE *cu1, FULLXF *fxf, CUBE *cu ) { static int vstart[4] = {0, 3, 5, 6}; FVECT cumin, cumax; FVECT vert[8]; FVECT v1, v2; int vloc, vout; register int i, j; /* check if cube vertex in octree */ for (j = 0; j < 3; j++) cumax[j] = (cumin[j] = cu1->cuorg[j]) + cu1->cusize; vloc = ABOVE | BELOW; vout = 0; for (i = 0; i < 8; i++) { for (j = 0; j < 3; j++) { v1[j] = cu->cuorg[j]; if (i & 1<<j) v1[j] += cu->cusize; } multp3(v2, v1, fxf->b.xfm); if ( (j = plocate(v2, cumin, cumax)) ) vout++; vloc &= j; } if (vout == 0) /* all inside */ return(O_IN); if (vout < 8) /* some inside */ return(O_HIT); if (vloc) /* all to one side */ return(O_MISS); /* octree vertices in cube? */ for (j = 0; j < 3; j++) cumax[j] = (cumin[j] = cu->cuorg[j]) + cu->cusize; vloc = ABOVE | BELOW; for (i = 0; i < 8; i++) { for (j = 0; j < 3; j++) { v1[j] = cu1->cuorg[j]; if (i & 1<<j) v1[j] += cu1->cusize; } multp3(vert[i], v1, fxf->f.xfm); if ( (j = plocate(vert[i], cumin, cumax)) ) vloc &= j; else return(O_HIT); /* vertex inside */ } if (vloc) /* all to one side */ return(O_MISS); /* check edges */ for (i = 0; i < 4; i++) for (j = 0; j < 3; j++) { /* clip modifies vertices! */ VCOPY(v1, vert[vstart[i]]); VCOPY(v2, vert[vstart[i] ^ 1<<j]); if (clip(v1, v2, cumin, cumax)) return(O_HIT); /* edge inside */ } return(O_MISS); /* no intersection */ }
/* Sample BSDF direction based on the given random variable */ SDError SDsampBSDF(SDValue *sv, FVECT ioVec, double randX, int sflags, const SDData *sd) { SDError ec; FVECT inVec; int inFront; SDSpectralDF *rdf, *tdf; double rdiff; float coef[SDmaxCh]; int i, j, n, nr; SDComponent *sdc; const SDCDst **cdarr = NULL; /* check arguments */ if ((sv == NULL) | (ioVec == NULL) | (sd == NULL) | (randX < 0) | (randX >= 1.)) return SDEargument; /* whose side are we on? */ VCOPY(inVec, ioVec); inFront = (inVec[2] > 0); /* remember diffuse portions */ if (inFront) { *sv = sd->rLambFront; rdf = sd->rf; tdf = (sd->tf != NULL) ? sd->tf : sd->tb; } else /* !inFront */ { *sv = sd->rLambBack; rdf = sd->rb; tdf = (sd->tb != NULL) ? sd->tb : sd->tf; } if ((sflags & SDsampDf+SDsampR) != SDsampDf+SDsampR) sv->cieY = .0; rdiff = sv->cieY; if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) sv->cieY += sd->tLamb.cieY; /* gather non-diffuse components */ i = nr = (((sflags & SDsampSp+SDsampR) == SDsampSp+SDsampR) & (rdf != NULL)) ? rdf->ncomp : 0; j = (((sflags & SDsampSp+SDsampT) == SDsampSp+SDsampT) & (tdf != NULL)) ? tdf->ncomp : 0; n = i + j; if (n > 0 && (cdarr = (const SDCDst **)malloc(n*sizeof(SDCDst *))) == NULL) return SDEmemory; while (j-- > 0) { /* non-diffuse transmission */ cdarr[i+j] = (*tdf->comp[j].func->getCDist)(inVec, &tdf->comp[j]); if (cdarr[i+j] == NULL) { free(cdarr); return SDEmemory; } sv->cieY += cdarr[i+j]->cTotal; } while (i-- > 0) { /* non-diffuse reflection */ cdarr[i] = (*rdf->comp[i].func->getCDist)(inVec, &rdf->comp[i]); if (cdarr[i] == NULL) { free(cdarr); return SDEmemory; } sv->cieY += cdarr[i]->cTotal; } if (sv->cieY <= 1e-6) { /* anything to sample? */ sv->cieY = .0; memset(ioVec, 0, 3*sizeof(double)); return SDEnone; } /* scale random variable */ randX *= sv->cieY; /* diffuse reflection? */ if (randX < rdiff) { SDdiffuseSamp(ioVec, inFront, randX/rdiff); goto done; } randX -= rdiff; /* diffuse transmission? */ if ((sflags & SDsampDf+SDsampT) == SDsampDf+SDsampT) { if (randX < sd->tLamb.cieY) { sv->spec = sd->tLamb.spec; SDdiffuseSamp(ioVec, !inFront, randX/sd->tLamb.cieY); goto done; } randX -= sd->tLamb.cieY; } /* else one of cumulative dist. */ for (i = 0; i < n && randX < cdarr[i]->cTotal; i++) randX -= cdarr[i]->cTotal; if (i >= n) return SDEinternal; /* compute sample direction */ sdc = (i < nr) ? &rdf->comp[i] : &tdf->comp[i-nr]; ec = (*sdc->func->sampCDist)(ioVec, randX/cdarr[i]->cTotal, cdarr[i]); if (ec) return ec; /* compute color */ j = (*sdc->func->getBSDFs)(coef, ioVec, inVec, sdc); if (j <= 0) { sprintf(SDerrorDetail, "BSDF \"%s\" sampling value error", sd->name); return SDEinternal; } sv->spec = sdc->cspec[0]; rdiff = coef[0]; while (--j) { c_cmix(&sv->spec, rdiff, &sv->spec, coef[j], &sdc->cspec[j]); rdiff += coef[j]; } done: if (cdarr != NULL) free(cdarr); /* make sure everything is set */ c_ccvt(&sv->spec, C_CSXY+C_CSSPEC); return SDEnone; }
static void ambHessian( /* anisotropic radii & pos. gradient */ AMBHEMI *hp, FVECT uv[2], /* returned */ float ra[2], /* returned (optional) */ float pg[2] /* returned (optional) */ ) { static char memerrmsg[] = "out of memory in ambHessian()"; FVECT (*hessrow)[3] = NULL; FVECT *gradrow = NULL; FVECT hessian[3]; FVECT gradient; FFTRI fftr; int i, j; /* be sure to assign unit vectors */ VCOPY(uv[0], hp->ux); VCOPY(uv[1], hp->uy); /* clock-wise vertex traversal from sample POV */ if (ra != NULL) { /* initialize Hessian row buffer */ hessrow = (FVECT (*)[3])malloc(sizeof(FVECT)*3*(hp->ns-1)); if (hessrow == NULL) error(SYSTEM, memerrmsg); memset(hessian, 0, sizeof(hessian)); } else if (pg == NULL) /* bogus call? */ return; if (pg != NULL) { /* initialize form factor row buffer */ gradrow = (FVECT *)malloc(sizeof(FVECT)*(hp->ns-1)); if (gradrow == NULL) error(SYSTEM, memerrmsg); memset(gradient, 0, sizeof(gradient)); } /* compute first row of edges */ for (j = 0; j < hp->ns-1; j++) { comp_fftri(&fftr, hp, AI(hp,0,j), AI(hp,0,j+1)); if (hessrow != NULL) comp_hessian(hessrow[j], &fftr, hp->rp->ron); if (gradrow != NULL) comp_gradient(gradrow[j], &fftr, hp->rp->ron); } /* sum each row of triangles */ for (i = 0; i < hp->ns-1; i++) { FVECT hesscol[3]; /* compute first vertical edge */ FVECT gradcol; comp_fftri(&fftr, hp, AI(hp,i,0), AI(hp,i+1,0)); if (hessrow != NULL) comp_hessian(hesscol, &fftr, hp->rp->ron); if (gradrow != NULL) comp_gradient(gradcol, &fftr, hp->rp->ron); for (j = 0; j < hp->ns-1; j++) { FVECT hessdia[3]; /* compute triangle contributions */ FVECT graddia; double backg; backg = back_ambval(hp, AI(hp,i,j), AI(hp,i,j+1), AI(hp,i+1,j)); /* diagonal (inner) edge */ comp_fftri(&fftr, hp, AI(hp,i,j+1), AI(hp,i+1,j)); if (hessrow != NULL) { comp_hessian(hessdia, &fftr, hp->rp->ron); rev_hessian(hesscol); add2hessian(hessian, hessrow[j], hessdia, hesscol, backg); } if (gradrow != NULL) { comp_gradient(graddia, &fftr, hp->rp->ron); rev_gradient(gradcol); add2gradient(gradient, gradrow[j], graddia, gradcol, backg); } /* initialize edge in next row */ comp_fftri(&fftr, hp, AI(hp,i+1,j+1), AI(hp,i+1,j)); if (hessrow != NULL) comp_hessian(hessrow[j], &fftr, hp->rp->ron); if (gradrow != NULL) comp_gradient(gradrow[j], &fftr, hp->rp->ron); /* new column edge & paired triangle */ backg = back_ambval(hp, AI(hp,i+1,j+1), AI(hp,i+1,j), AI(hp,i,j+1)); comp_fftri(&fftr, hp, AI(hp,i,j+1), AI(hp,i+1,j+1)); if (hessrow != NULL) { comp_hessian(hesscol, &fftr, hp->rp->ron); rev_hessian(hessdia); add2hessian(hessian, hessrow[j], hessdia, hesscol, backg); if (i < hp->ns-2) rev_hessian(hessrow[j]); } if (gradrow != NULL) { comp_gradient(gradcol, &fftr, hp->rp->ron); rev_gradient(graddia); add2gradient(gradient, gradrow[j], graddia, gradcol, backg); if (i < hp->ns-2) rev_gradient(gradrow[j]); } } } /* release row buffers */ if (hessrow != NULL) free(hessrow); if (gradrow != NULL) free(gradrow); if (ra != NULL) /* extract eigenvectors & radii */ eigenvectors(uv, ra, hessian); if (pg != NULL) { /* tangential position gradient */ pg[0] = DOT(gradient, uv[0]); pg[1] = DOT(gradient, uv[1]); } }