src/Sphere.h

Go to the documentation of this file.
00001 #ifndef SPHERE_H
00002 #define SPHERE_H
00003 
00004 
00005 #include <cassert>
00006 
00007 #include "Primitive.h"
00008 
00009 
00015 class Sphere : public Primitive
00016 {
00017 public:
00023     Sphere(const Vec3f & center, float radius)
00024         : mCenter(center),
00025             mRadius(radius)
00026     {
00027         assert(radius > 0.0f);
00028         calcBounds();
00029     }
00030 
00031     virtual ~Sphere()
00032     {
00033     }
00034 
00038     bool intersect(Ray & ray) const
00039     {
00040         // mathematical derivation, numerically not very stable, but simple
00041 
00042         // --> find roots of f(t) = ((R+tD)-C)^2 - r^2
00043         // f(t) = (R-C)^2 + 2(R-C)(tD) + (tD)^2 -r^2
00044         // --> f(t) = [D^2] t^2 + [2D(R-C)] t + [(R-C)^2 - r^2]
00045         Vec3f diff = ray.org() - mCenter;
00046         float a = ray.dir().dot(ray.dir());
00047         float b = 2 * ray.dir().dot(diff);
00048         float c = diff.dot(diff) - mRadius * mRadius;
00049 
00050         // use 'abc'-formula for finding root t_1, 2 = (-b +/- sqrt(b^2-4ac))/(2a)
00051         float inRoot = b*b - 4*a*c;
00052         if (inRoot < 0)
00053             return false;
00054         float root = sqrtf(inRoot);
00055 
00056         float dist = (-b - root)/(2*a);
00057         if (dist > ray.t())
00058             return false;
00059 
00060         if (dist < EPSILON)
00061         {
00062             dist = (-b + root)/(2*a);
00063             if (dist < EPSILON || dist > ray.t())
00064                 return false;
00065         }
00066         ray.setHit(this, dist);
00067         // u and v correspond to theta and phi from spherical coordinates of the hitpoint with p set
00068         // to radius. range -PI .. PI is compressed into 0 .. 1
00069         // u goes arond the equator
00070         Vec3f hitP = ray.hitPoint(0.0f) - mCenter;
00071         float theta = 1.0f - (atan2f(hitP.y(), hitP.x()) + M_PI)*0.5f/M_PI;
00072         float phi   = atan2f(sqrtf(hitP.x()*hitP.x() + hitP.y()*hitP.y()), hitP.z())/M_PI;
00073         ray.setUV(theta, phi);
00074 //        LOG("theta / phi : " << theta << " " << phi);
00075 //        assert(0 <= theta && theta <= 1.0f + EPSILON);
00076 //        assert(0 <= phi && phi <= 1.0f + EPSILON);
00077         return true;
00078     }
00079 
00084     Vec3f normal(const Ray & ray) const
00085     {
00086         assert(ray.hit() == this);
00087         assert(ray.obj());
00088 
00089         // transform spherical coordinates of the hit point to cartesian with p = 1
00090         float theta = (1.0f - ray.u())*2*M_PI - M_PI;
00091         float phi   = ray.v()*M_PI;
00092         Vec3f norm(sinf(phi)*cosf(theta), sinf(phi)*sinf(theta), cosf(phi));
00093         ray.obj()->toGlobalCoordinates(norm);
00094         return norm.normal();
00095     }
00096 
00099     virtual TexCoordinate texCoord(const Ray & ray) const
00100     {
00101         return TexCoordinate(ray.u(), ray.v());
00102     }
00103 
00107     void axes(const Ray & ray, Vec3f & x, Vec3f & y) const
00108     {
00109         assert(ray.hit() == this);
00110         assert(ray.obj());
00111 
00112         Vec3f n = normal(ray);
00113         Vec3f fn = n.fabs();
00114         fn.setX(1 - fn.x());
00115         if (fn.x() <= EPSILON && fn.y() <= EPSILON && fn.z() <= EPSILON)
00116         {
00117             x = Vec3f(0, 1, 0);
00118             y = Vec3f(0, 0, 1);
00119             return;
00120         }
00121         x = - n.cross(Vec3f(1, 0, 0)).normal();
00122         y = n.cross(x).normal();
00123 
00124         // take x and y coordinate axes rotated with phi and theta
00125 //        float theta = (1.0f - ray.u())*2*M_PI - M_PI;
00126 //        float phi   = ray.v()*M_PI;
00127 //        x = Vec3f(cosf(phi)*cosf(theta), cosf(phi)*sinf(theta), -sinf(phi));
00128 //        y = Vec3f(-cosf(phi)*sinf(theta), cosf(theta), sinf(phi)*sinf(theta));
00129     }
00130 
00131 protected:
00133     Vec3f   mCenter;
00134 
00135     // Sphere radius
00136     float   mRadius;
00137 
00138 
00141     void calcBounds()
00142     {
00143         // sphere's bounding box is computed based on the radius of the sphere
00144         mBounds.extend(mCenter + Vec3f(-mRadius));
00145         mBounds.extend(mCenter + Vec3f(mRadius));
00146     }
00147 };
00148 
00149 
00150 #endif
00151 

Generated on Fri Feb 1 00:01:42 2008 for Grayfall by  doxygen 1.5.1