/* globallines.c: generic global line tracing engine */ #include "globallines.h" #include "scene.h" #include "mymath.h" #include "render.h" #include "QMC/nied31.h" #include "error.h" /* World bounding sphere, available after InitGlobalLines() */ VECTOR glin_Center; float glin_Radius; /* Generate a bounding sphere */ static void GenerateBoundingSphere(void) { float *bbx = WorldGrid->bounds; VECTOR diagonal; VECTORSET(glin_Center, (bbx[MIN_X] + bbx[MAX_X])/2, (bbx[MIN_Y] + bbx[MAX_Y])/2, (bbx[MIN_Z] + bbx[MAX_Z])/2); VECTORSET(diagonal, bbx[MAX_X] - bbx[MIN_X], bbx[MAX_Y] - bbx[MIN_Y], bbx[MAX_Z] - bbx[MIN_Z] ); glin_Radius = VECTORNORM(diagonal)/2; } static double *DefSample4D(int n) { unsigned int *zeta = Nied31(n); static double xi[4]; xi[0] = zeta[0] * RECIP; xi[1] = zeta[1] * RECIP; xi[2] = zeta[2] * RECIP; xi[3] = zeta[3] * RECIP; return xi; } static double* (*Sample4D)(int n) = DefSample4D; /* Generates a line with two random points on bounding sphere. * Ray direction is not normalized: t values lay between 0 and 1 * for points inside the scenes bounding sphere. */ static RAY GenerateGlobalLine(int n) { RAY ray; VECTOR p1, p2; double alea; double direc1,direc2; double *xi; xi = Sample4D(n); alea = 1.0-2.0*xi[0]; direc1 = 2.0*M_PI*xi[1]; direc2 = acos(alea); VECTORSET(p1,cos(direc1)*sin(direc2), sin(direc1)*sin(direc2), cos(direc2)); VECTORSCALE(glin_Radius, p1,p1); VECTORADD(glin_Center,p1,p1); alea = 1.0-2.0*xi[2]; direc1 = 2.0*M_PI*xi[3]; direc2 = acos(alea); VECTORSET(p2,cos(direc1)*sin(direc2), sin(direc1)*sin(direc2), cos(direc2)); VECTORSCALE(glin_Radius, p2,p2); VECTORADD(glin_Center,p2,p2); ray.pos = p1; VECTORSUBTRACT(p2, p1, ray.dir); /* ray direction is not normalized!!! */ /* RenderSetColor(&Yellow); RenderLine(&p1, &p2); */ return ray; } static HITLIST *TraceGlobalLine(RAY *ray) { return AllGridIntersections((HITLIST *)NULL, WorldGrid, ray, -EPSILON, 1.+EPSILON, HIT_FRONT|HIT_BACK|HIT_PATCH|HIT_POINT); /* HIT_POINT is only needed to draw the points in ShowHit() */ } /* "returns" TRUE if the hit record describes a frontal hit and "FALSE" if * it is a hit from the back. */ #define IsFrontalHit(hit) (hit->flags & HIT_FRONT) /* Returns -1 if hit1 comes before hit2, +1 if hit1 should come after * hit2 and 0 if both hits are coincident are are both frontal hits * or hits from the back (a situation that occurs e.g. for coplanar * polygons) */ int DefCompareHits(HITREC *hit1, HITREC *hit2) { int code=0; if (hit1->dist < hit2->dist-EPSILON) code = -1; else if (hit1->dist > hit2->dist+EPSILON) code = +1; else { /* coincident hitpoints */ if (IsFrontalHit(hit1) && !IsFrontalHit(hit2)) code = -1; /* frontal hits come before hits from the back */ else if (!IsFrontalHit(hit1) && IsFrontalHit(hit2)) code = +1; else code = 0; /* both hits are frontal or from back */ } return code; } static int (*CompareHits)(HITREC*, HITREC*) = DefCompareHits; /* Determines "spans" connecting a hit from the back with the next frontal * hit. Calls do_span for each found span. Requires that the hitlist has * been sorted using the default hitlist sorting routine. * Returns number of spans processed. */ int ProcessSpans(HITLIST *hits, void (*do_span)(HITREC *start, HITREC *end)) { int nrspans = 0; HITREC *start = NULL; ForAllHits(hit, hits) { if (IsFrontalHit(hit)) { if (start) { do_span(start, hit); nrspans++; start = (HITREC *)NULL; } /* else (frontal hit without preceeding hit from the back) skip it */ } else start = hit; } EndForAll; return nrspans; } /* example of a span processing routine */ void PrintSpan(HITREC *start, HITREC *end) { fprintf(stderr, "start = "); PrintHit(stderr, start); fprintf(stderr, "end = "); PrintHit(stderr, end); } /* default hit processing routine: calles ProcessSpans() with * PrintSpan as a span processing routine. */ void DefProcessHits(HITLIST *hits) { ProcessSpans(hits, PrintSpan); } static void (*ProcessHits)(HITLIST *hits) = DefProcessHits; static int inited = FALSE; /* Initialises global line tracing. * - sample4d is a routine returning a 4D sample vector with given * index, used to generate a global line. This parameter can be * NULL, in which case 4D Niederreiter sampling is used. * - compare_hits is a routine that compares to hit records. Use * 'DefCompareHits' in order to sort global line hit points in such * a way that ProcessSpans() can be used for processing the hits. * Also this parameter can be NULL, in which case the hits are * not sorted. * - process_hits is a pointer to a routine for processing the * sorted global line hits. If sorted using DefCompareHits, process_hits * can call ProcessSpans with a routine for handling spans of mutually * visible points. */ void InitGlobalLines(double* (*sample4d)(int index), int (*compare_hits)(HITREC *hit1, HITREC *hit2), void (*process_hits)(HITLIST *hits)) { if (!World) { Error("InitGlobalLines", "No world!"); inited = FALSE; return; } inited = TRUE; GenerateBoundingSphere(); Sample4D = sample4d ? sample4d : DefSample4D; CompareHits = compare_hits; ProcessHits = process_hits; } /* Generates and traces a global line with given index number using * the two-points-on-a-bounding-sphere method, using sample4d() * for sampling the points on the sphere. If the global line * intersects surfaces in the scene, the hit points are sorted using * compare_hits(). Eventually, process_hits() is called for processing * the sorted hit points. * Returns FALSE is the generated global line did not hit the scene and * returns TRUE if it did. */ int DoGlobalLine(int index) { RAY ray; HITLIST *hits; if (!inited) { Fatal(-1, "DoGlobalLine", "Call InitGlobalLines() first before using DoGlobalLine()"); } ray = GenerateGlobalLine(index); hits = TraceGlobalLine(&ray); if (!hits) return FALSE; if (CompareHits) { hits = HitListSort(hits, CompareHits); } if (ProcessHits) { ProcessHits(hits); } DestroyHitlist(hits); return TRUE; }