/* geom.c */ #include #include #include "geom.h" #include "bounds.h" #include "error.h" #include "mymath.h" #include "pools.h" #include "radiance.h" #ifdef NOPOOLS #define NEWGEOM() (GEOM *)Alloc(sizeof(GEOM)) #define DISPOSEGEOM(ptr) Free((char *)ptr, sizeof(GEOM)) #else static POOL *geomPool = (POOL *)NULL; #define NEWGEOM() (GEOM *)NewPoolCell(sizeof(GEOM), 0, "geoms", &geomPool) #define DISPOSEGEOM(ptr) Dispose((char *)ptr, &geomPool) #endif /* statistics */ int nrgeoms = 0, id = 0; static void BoundsEnlargeTinyBit(float *bounds) { float Dx = (bounds[MAX_X] - bounds[MIN_X]) * 1e-4; float Dy = (bounds[MAX_Y] - bounds[MIN_Y]) * 1e-4; float Dz = (bounds[MAX_Z] - bounds[MIN_Z]) * 1e-4; if (Dx < EPSILON) Dx = EPSILON; if (Dy < EPSILON) Dy = EPSILON; if (Dz < EPSILON) Dz = EPSILON; bounds[MIN_X] -= Dx; bounds[MAX_X] += Dx; bounds[MIN_Y] -= Dy; bounds[MAX_Y] += Dy; bounds[MIN_Z] -= Dz; bounds[MAX_Z] += Dz; } /* This function is used to create a new GEOMetry with given specific data and * methods. A pointer to the new GEOMetry is returned. */ GEOM *GeomCreate(void *obj, GEOM_METHODS *methods) { GEOM *p = (GEOM *)NULL; if (obj == NULL) return (GEOM *)NULL; p = NEWGEOM(); nrgeoms++; p->id = id++; p->obj = obj; p->methods = methods; if (methods->bounds) { methods->bounds(obj, p->bounds); /* enlarge bounding box a tiny bit for more conservative bounding box culling */ BoundsEnlargeTinyBit(p->bounds); p->bounded = TRUE; } else { BoundsInit(p->bounds); p->bounded = FALSE; } p->shaftcullgeom = FALSE; p->radiance_data = (void *)NULL; p->tmp.i = 0; p->omit = FALSE; p->dlistid = -1; return p; } /* This function prints the GEOMetry data to the file out */ void GeomPrint(FILE *out, GEOM *geom) { fprintf(out, "Geom %d, bounded = %s, shaftcullgeom = %s:\n", geom->id, geom->bounded ? "TRUE" : "FALSE", geom->shaftcullgeom ? "TRUE" : "FALSE"); geom->methods->print(out, geom->obj); } /* This function returns a bounding box for the GEOMetry */ float *GeomBounds(GEOM *geom) { return geom->bounds; } /* This function destroys the given GEOMetry */ void GeomDestroy(GEOM *geom) { geom->methods->destroy(geom->obj); DISPOSEGEOM(geom); nrgeoms--; } /* This function returns nonzero if the given GEOMetry is an aggregate. An * aggregate is a geometry that consists of simpler geometries. Currently, * there is only one type of aggregate geometry: the compound, which is basically * just a list of simpler geometries. Other aggregate geometries are also * possible, e.g. CSG objects. If the given GEOMetry is a primitive, zero is * returned. A primitive GEOMetry is a GEOMetry that does not consist of * simpler GEOMetries. */ int GeomIsAggregate(GEOM *geom) { return geom->methods->primlist != (GEOMLIST *(*)(void *))NULL; } /* Returns a linear list of the simpler GEOMEtries making up an aggregate GEOMetry. * A NULL pointer is returned if the GEOMetry is a primitive. */ GEOMLIST *GeomPrimList(GEOM *geom) { if (geom->methods->primlist) return geom->methods->primlist(geom->obj); else return (GEOMLIST *)NULL; } /* Returns a linear list of patches making up a primitive GEOMetry. A NULL * pointer is returned if the given GEOMetry is an aggregate. */ PATCHLIST *GeomPatchList(GEOM *geom) { if (geom->methods->patchlist) return geom->methods->patchlist(geom->obj); else return (PATCHLIST *)NULL; } /* This routine creates and returns a duplicate of the given geometry. Needed for * shaft culling. */ GEOM *GeomDuplicate(GEOM *geom) { GEOM *p = (GEOM *)NULL; if (!geom->methods->duplicate) { Error("GeomDuplicate", "geometry has no duplicate method"); return (GEOM *)NULL; } p = NEWGEOM(); nrgeoms++; *p = *geom; p->obj = geom->methods->duplicate(geom->obj); return p; } /* Will avoid intersection testing with geom1 and geom2 (possibly NULL * pointers). Can be used for avoiding immediate selfintersections. */ GEOM *excludedGeom1=(GEOM *)NULL, *excludedGeom2=(GEOM *)NULL; void GeomDontIntersect(GEOM *geom1, GEOM *geom2) { excludedGeom1 = geom1; excludedGeom2 = geom2; } #ifdef IDEBUG extern int idebug; #endif /* This routine returns NULL is the ray doesn't hit the discretisation of the * GEOMetry. If the ray hits the discretisation of the GEOM, a pointer to a * struct containing (among other information) the hit patch is returned. * The hitflags (defined in ray.h) determine whether the nearest intersection * is returned, or rather just any intersection (e.g. for shadow rays in * ray tracing or for form factor rays in radiosity), whether to consider * intersections with front/back facing patches and what other information * besides the hit patch (interpolated normal, intersection point, material * properties) to return. */ HITREC *GeomDiscretisationIntersect(GEOM *geom, RAY *ray, float mindist, float *maxdist, int hitflags, HITREC *hitstore) { VECTOR vtmp; float nmaxdist; #ifdef IDEBUG if (idebug) { fprintf(stderr, "====> %s %d: GeomDiscretisationIntersect\n", __FILE__, __LINE__); } #endif if (geom==excludedGeom1 || geom==excludedGeom2) { #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: excluded geometry -> no intersection\n", __FILE__, __LINE__); } #endif return (HITREC *)NULL; } if (geom->bounded) { /* Check ray/bounding volume intersection */ VECTORADDSCALED(ray->pos, mindist, ray->dir, vtmp); if (OutOfBounds(&vtmp, geom->bounds)) { nmaxdist = *maxdist; if (!BoundsIntersect(ray, geom->bounds, mindist, &nmaxdist)) { #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: bounding box test fails -> no intersection\n", __FILE__, __LINE__); } #endif return NULL; } } } #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: bounding box test succeeded, now calling discretisatoinintersect method ...\n", __FILE__, __LINE__); } #endif return geom->methods->discretisation_intersect(geom->obj, ray, mindist, maxdist, hitflags, hitstore); } HITLIST *GeomAllDiscretisationIntersections(HITLIST *hits, GEOM *geom, RAY *ray, float mindist, float maxdist, int hitflags) { VECTOR vtmp; float nmaxdist; if (geom==excludedGeom1 || geom==excludedGeom2) { return hits; } if (geom->bounded) { /* Check ray/bounding volume intersection */ VECTORADDSCALED(ray->pos, mindist, ray->dir, vtmp); if (OutOfBounds(&vtmp, geom->bounds)) { nmaxdist = maxdist; if (!BoundsIntersect(ray, geom->bounds, mindist, &nmaxdist)) { return hits; } } } return geom->methods->all_discretisation_intersections(hits, geom->obj, ray, mindist, maxdist, hitflags); }