/* * cylinder.c * * Copyright (C) 1989, 1991, Craig E. Kolb * All rights reserved. * * This software may be freely copied, modified, and redistributed * provided that this copyright notice is preserved on all copies. * * You may not distribute this software, in whole or in part, as part of * any commercial product without the express consent of the authors. * * There is no warranty or other guarantee of fitness of this software * for any purpose. It is provided solely "as is". * * $Id: cylinder.c,v 4.0 91/07/17 14:37:12 kolb Exp Locker: kolb $ * * $Log: cylinder.c,v $ * Revision 4.0 91/07/17 14:37:12 kolb * Initial version. * */ #include "geom.h" #include "cylinder.h" static Methods *iCylinderMethods = NULL; static char cylName[] = "cylinder"; unsigned long CylTests, CylHits; Cylinder * CylinderCreate(r, bot, top) Float r; Vector *bot, *top; { Cylinder *cyl; Float len; Vector axis; if (r <= 0.) { RLerror(RL_WARN, "Invalid cylinder radius.\n"); return (Cylinder*)NULL; } VecSub(*top, *bot, &axis); len = VecNormalize(&axis); if (len < EPSILON) { RLerror(RL_WARN, "Degenerate cylinder.\n"); return (Cylinder *)NULL; } cyl = (Cylinder *)share_malloc(sizeof(Cylinder)); CoordSysTransform(bot, &axis, r, len, &cyl->trans); return cyl; } Methods * CylinderMethods() { if (iCylinderMethods == (Methods *)NULL) { iCylinderMethods = MethodsCreate(); iCylinderMethods->name = CylinderName; iCylinderMethods->create = (GeomCreateFunc *)CylinderCreate; iCylinderMethods->methods = CylinderMethods; iCylinderMethods->intersect = CylinderIntersect; iCylinderMethods->normal = CylinderNormal; iCylinderMethods->uv = CylinderUV; iCylinderMethods->bounds = CylinderBounds; iCylinderMethods->stats = CylinderStats; iCylinderMethods->checkbounds = TRUE; iCylinderMethods->closed = FALSE; } return iCylinderMethods; } /* * Ray-cylinder intersection test. */ int CylinderIntersect(cyl, ray, mindist, maxdist) Cylinder *cyl; Ray *ray; Float mindist, *maxdist; { Float t1, t2, a, b, c, zpos1, zpos2, disc; Float distfact; Ray newray; Vector nray, npos; Float nmin; CylTests++; /* * Transform ray into canonical cylinder space. */ newray = *ray; distfact = RayTransform(&newray, &cyl->trans.itrans); nray = newray.dir; npos = newray.pos; nmin = mindist * distfact; a = nray.x * nray.x + nray.y * nray.y; if (a < EPSILON*EPSILON) /* |nray.z| == 1. */ return FALSE; b = nray.x * npos.x + nray.y * npos.y; c = npos.x*npos.x + npos.y*npos.y - 1; disc = b*b - a*c; if(disc < 0.) return FALSE; disc = sqrt(disc); t1 = (-b + disc) / a; t2 = (-b - disc) / a; if (t1 < nmin && t2 < nmin) return FALSE; zpos1 = npos.z + t1 * nray.z; zpos2 = npos.z + t2 * nray.z; if (t1 < nmin || zpos1 < 0. || zpos1 > 1.) { if (t2 < nmin || zpos2 < 0. || zpos2 > 1.) return FALSE; else t1 = t2 / distfact; } else { if (t2 < nmin || zpos2 < 0. || zpos2 > 1.) t1 /= distfact; else { t1 = min(t1, t2) / distfact; } } if (t1 < *maxdist) { *maxdist = t1; CylHits++; return TRUE; } return FALSE; } int CylinderNormal(cyl, pos, nrm, gnrm) Cylinder *cyl; Vector *pos, *nrm, *gnrm; { /* * Transform position into cylinder space. */ *nrm = *pos; PointTransform(nrm, &cyl->trans.itrans); /* * The normal is equal to the point of intersection in cylinder * space, but with Z = 0.; */ nrm->z = 0.; /* * Tranform normal back to world space. */ NormalTransform(nrm, &cyl->trans.itrans); *gnrm = *nrm; return FALSE; } void CylinderUV(cyl, pos, norm, uv, dpdu, dpdv) Cylinder *cyl; Vector *pos, *norm, *dpdu, *dpdv; Vec2d *uv; { Vector npos; npos = *pos; PointTransform(&npos, &cyl->trans.itrans); uv->v = npos.z; /* * Due to roundoff error, |npos.x| may be > 1. */ if (npos.x > 1.) uv->u = 0.; else if (npos.x < -1.) uv->u = 0.5; else uv->u = acos(npos.x) / TWOPI; if (npos.y < 0.) uv->u = 1. - uv->u; if (dpdu) { dpdv->x = dpdv->y = 0.; dpdv->z = 1.; dpdu->x = -npos.y; dpdu->y = npos.x; dpdu->z = 0.; VecTransform(dpdu, &cyl->trans.trans); VecTransform(dpdv, &cyl->trans.trans); (void)VecNormalize(dpdu); (void)VecNormalize(dpdv); } } void CylinderBounds(cyl, bounds) Cylinder *cyl; Float bounds[2][3]; { bounds[LOW][X] = bounds[LOW][Y] = -1; bounds[HIGH][X] = bounds[HIGH][Y] = 1; bounds[LOW][Z] = 0.; bounds[HIGH][Z] = 1; /* * Transform bounding box to world space. */ BoundsTransform(&cyl->trans.trans, bounds); } char * CylinderName() { return cylName; } void CylinderStats(tests, hits) unsigned long *tests, *hits; { *tests = CylTests; *hits = CylHits; } void CylinderMethodRegister(meth) UserMethodType meth; { if (iCylinderMethods) iCylinderMethods->user = meth; }