Sphere.cc

Go to the documentation of this file.
00001 #include <esg/geometry/Sphere.h>
00002 #include <esg/mesh/SphereMesh.h>
00003 #include <esg/explorer/POGExplorer.h>
00004 #include <esg/explorer/RadiusExplorer.h>
00005 
00006 using namespace esg;
00007 
00008 Mesh* Sphere::_mesh(int density) const
00009 {
00010     Mesh*  pMesh = (SphereMesh*) new SphereMesh(density);
00011     if (!pMesh) return NULL;
00012     if (_radius != 1.0)
00013         pMesh->scale(_radius, _radius, _radius);
00014     if (_centre.x || _centre.y || _centre.z)
00015         pMesh->translate(_centre);
00016     return pMesh;
00017 }
00018 
00019 void Sphere::_duplicate_attributes(const Geometry& src)
00020 {
00021     _radius   = ((Sphere&)src)._radius;
00022     _radius2  = _radius * _radius;
00023     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00024     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00025     _centre.set(((Sphere&)src)._centre);
00026 }
00027 
00028 void Sphere::_rotateX(float a)
00029 {
00030     Matrix3 trMat;
00031     trMat.rotX(a);
00032     trMat.transform(_centre);
00033 }
00034 
00035 void Sphere::_rotateY(float a)
00036 {
00037     Matrix3 trMat;
00038     trMat.rotY(a);
00039     trMat.transform(_centre);
00040 }
00041 
00042 void Sphere::_rotateZ(float a)
00043 {
00044     Matrix3 trMat;
00045     trMat.rotZ(a);
00046     trMat.transform(_centre);
00047 }
00048 
00049 void Sphere::_rotate(float a, const Vector3& axis)
00050 {
00051     Matrix4 trMat;
00052     trMat.rotationGL(a, axis);
00053     trMat.transform(_centre);
00054 }
00055 
00056 void Sphere::_rotate(const Matrix3& rotMat)
00057 {
00058     rotMat.transform(_centre);
00059 }
00060 
00061 void Sphere::_translate(float x, float y, float z)
00062 {
00063     _centre.add(Vector3(x, y, z));
00064 }
00065 
00066 void Sphere::_transform(const Matrix4& trMat)
00067 {
00068     trMat.transform(_centre);
00069 }
00070 
00071 void Sphere::_scale(float s)
00072 {
00073     _centre.scale(s);
00074     _radius *= s;
00075     _radius2  = _radius * _radius;
00076     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00077     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00078 }
00079 
00080 
00081 //-------- public --------
00082 
00083 Sphere::Sphere()
00084 {
00085     _radius   = 1.;
00086     _radius2  = 1.;
00087     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00088     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00089     _centre.set(0., 0., 0.);
00090 }
00091 
00092 Sphere::Sphere(const Vector3& c, float r)
00093 {
00094     _radius   = r;
00095     _radius2  = r * r;
00096     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00097     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00098     _centre.set(c);
00099 }
00100 
00101 
00102 Sphere::Sphere(float x, float y, float z, float r)
00103 {
00104     _radius   = r;
00105     _radius2  = r * r;
00106     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00107     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00108     _centre.set(x,y,z);
00109 }
00110 
00111 Sphere::Sphere(const Geometry& g)
00112 {
00113     _centre.set(g.centroid());
00114     _radius   = g.radius(_centre);
00115     _radius2  = _radius * _radius;
00116     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00117     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00118 }
00119 
00120 Sphere::Sphere(const Geometry& g1, const Geometry& g2)
00121 {
00122     Vector3 c1(g1.centroid());
00123     Vector3 c2(g2.centroid());
00124     Vector3 v(c2 - c1);
00125     double  r1 = g1.radius(c1);
00126     double  r2 = g2.radius(c2);
00127     double  d  = v.length();
00128 
00129     if ((d + r2) <= r1) { // second inside first
00130         _centre.set(c1);
00131         _radius   = r1;
00132         _radius2  = r1 * r1;
00133     } else {
00134         if ((d + r1) <= r2) { // first inside second
00135             _centre.set(c2);
00136             _radius    = r2;
00137             _radius2   = r2 * r2;
00138         } else {
00139             c2.scale((1.0 + (r2 - r1) / d) / 2.0);
00140             _centre.set(c1);
00141             _centre.scaleAdd((1.0 + (r1 - r2) / d) / 2.0, c2);
00142             _radius    = (r1 + r2 + d) / 2.0;
00143             _radius2   = _radius * _radius;
00144         }
00145     }
00146 
00147     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00148     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00149 }
00150 
00151 Sphere::Sphere(SceneGraphObject& obj)
00152 {
00153     POGExplorer explorer;
00154     explorer.explore(obj);
00155     
00156     _centre.set(explorer.result());
00157     
00158     RadiusExplorer explorer2(_centre);
00159     explorer2.explore(obj);
00160     
00161     _radius   = explorer2.result();
00162     _radius2  = _radius * _radius;
00163     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00164     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00165 }
00166 
00167 Sphere::Sphere(List<SceneGraphObject>& list)
00168 {
00169     _centre.set(0,0,0);
00170     
00171     unsigned          num = 0;
00172     SceneGraphObject* pObject = list.firstItem();
00173     while (pObject) {
00174         POGExplorer pogExplorer;
00175         pogExplorer.explore(*pObject);
00176         _centre.add(pogExplorer.result());
00177         num++;
00178         pObject = list.nextItem();
00179     }
00180     
00181     if (num) _centre.scale(1.0/num);
00182 
00183     _radius = -MAXFLOAT;
00184 
00185     float r;
00186     pObject = list.firstItem();
00187     while (pObject) {
00188         RadiusExplorer radExplorer(_centre);
00189         radExplorer.explore(*pObject);
00190         r = radExplorer.result();
00191         if (r > _radius) _radius = r;
00192         pObject = list.nextItem();
00193     }
00194     _radius2  = _radius * _radius;
00195     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00196     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00197 }
00198 
00199 void Sphere::rayIntersection(PointEnv*      pPE,
00200                              int            mask,
00201                              const Vector3& origin,
00202                              const Vector3& direction,
00203                              float          maxDist)
00204 {
00205     if (!pPE) {
00206         fprintf(stderr,"Sphere error: No point environment structure defined\n");
00207         return;
00208     }
00209  
00210     pPE->mask = ENV_HAVE_NOTHING;
00211 
00212     Vector3 v(_centre - origin);
00213     double  v2 = v.dot(v);
00214     double& t0 = pPE->distance;
00215     
00216     t0 = v.dot(direction);
00217     if (v2 > _epsMinus) { // outside of or at the sphere
00218         if (t0 < Geometry::EPS) return; // sphere is 'behind' the ray
00219         register double td2 = _radius2 - v2 + (t0*t0);
00220         if (td2 < 0.0) return; // ray doesn't hit the sphere
00221         // if ray starts on the surface then get farter (inner) intersection
00222         t0 = (v2 < _epsPlus) ? t0 + sqrt(td2) : t0 - sqrt(td2);
00223     } else  // ray starts inside the sphere
00224         t0 += sqrt(_radius2 - v2 + (t0*t0));
00225 
00226     if (t0 > maxDist) return;
00227 
00228     if (mask & (ENV_WANT_INTERSECTION|ENV_WANT_UV_COORD|ENV_WANT_NORMAL)) {
00229         pPE->intersection.set(direction);
00230         pPE->intersection.scaleAdd(t0, origin);
00231         if (mask & ENV_WANT_UV_COORD) {
00232             Vector3 xyz(pPE->intersection);
00233             xyz.sub(_centre);
00234             double auxV = acos(xyz.z / _radius);
00235             if (xyz.y >= 0.) 
00236                 pPE->uvCoord.set(acos(xyz.x/(_radius*sin(auxV))) / (2*PI),
00237                                  auxV / PI);
00238             else
00239                 pPE->uvCoord.set((PI+acos(xyz.x/(_radius*sin(auxV)))) / (2*PI),
00240                                  auxV / PI);
00241             pPE->mask |=
00242                 ENV_HAVE_INTERFERENCE|ENV_HAVE_INTERSECTION|ENV_HAVE_DISTANCE|ENV_HAVE_UV_COORD;
00243         } else 
00244             pPE->mask |=
00245                 ENV_HAVE_INTERFERENCE|ENV_HAVE_INTERSECTION|ENV_HAVE_DISTANCE;
00246         if (mask & ENV_WANT_NORMAL) {
00247             pPE->normal.set(pPE->intersection - _centre);
00248             //pPE->normal.scale(1./_radius); // unsharp normalization 
00249             pPE->normal.normalize();
00250             pPE->normalOrientation = PointEnv::OUTWARDS_NORMAL;
00251             pPE->mask |= ENV_HAVE_NORMAL;
00252         }
00253     } else 
00254         pPE->mask |= ENV_HAVE_INTERFERENCE|ENV_HAVE_DISTANCE;
00255 }
00256 
00257 bool Sphere::mapToUV(const Vector3& v, Vector2& uv)
00258 {
00259     Vector3 xyz(v);
00260     xyz.sub(_centre);
00261     xyz.normalize();
00262 
00263     // re-arrange for pole distortion
00264     double aux = xyz.x; xyz.x = xyz.z; xyz.z = aux; 
00265 
00266     double r   = sqrt((xyz.x * xyz.x) + (xyz.y * xyz.y));
00267     double rho = sqrt((r * r) + (xyz.z * xyz.z));
00268     double theta, phi;
00269 
00270     if(r == 0.0) {
00271         theta = 0.0;
00272         phi   = 0.0;
00273     } else {
00274         phi   = (xyz.z == 0.0) ? PI / 2.0 : acos(xyz.z / rho);
00275         theta = (xyz.y == 0.0) ? PI / 2.0 : asin(xyz.y / r) + (PI / 2.0);
00276     }
00277 
00278     uv.set(1 - (phi / PI), 1 - (theta / PI));
00279 
00280     return true;
00281     
00282 #if 0 // funkci mapovani na dve polokoule
00283     Vector3 xyz(v);
00284     xyz.sub(_centre);
00285     xyz.normalize();
00286 
00287     // re-arrange for pole distortion
00288     double aux = xyz.x; xyz.x = xyz.z; xyz.z = aux; 
00289 
00290     double r   = sqrt((xyz.x * xyz.x) + (xyz.y * xyz.y));
00291     double rho = sqrt((r * r) + (xyz.z * xyz.z));
00292     double theta, phi;
00293 
00294     if(r == 0.0) {
00295         theta = 0.0;
00296         phi   = 0.0;
00297     } else {
00298         phi   = (xyz.z == 0.0) ? PI / 2.0 : acos(xyz.z / rho);
00299         theta = (xyz.y == 0.0) ? PI / 2.0 : asin(xyz.y / r) + (PI / 2.0);
00300     }
00301 
00302     uv.set(1 - (phi / PI), 1 - (theta / PI));
00303     return true;
00304 #endif
00305 }
00306 
00307 void Sphere::randomSample(int mask, PointEnv& pe, double* pdf) 
00308 {
00309     pe.mask = ENV_HAVE_NOTHING;
00310     
00311     if (mask & (ENV_WANT_SURFACE_POINT|ENV_WANT_NORMAL|ENV_WANT_UV_COORD)) {
00312         double d;
00313         do { // rejection sampling
00314             pe.intersection.set(2. * ESG_DBL_RAND - 1.,
00315                                 2. * ESG_DBL_RAND - 1.,
00316                                 2. * ESG_DBL_RAND - 1.);
00317             d = pe.intersection.lengthSquared();
00318         } while (d > 1.);
00319         pe.intersection.scale(_radius/sqrt(d));
00320         pe.intersection.add(_centre);
00321         pe.mask |= ENV_HAVE_SURFACE_POINT;
00322 
00323         if ((mask&ENV_WANT_UV_COORD) && mapToUV(pe.intersection, pe.uvCoord))
00324             pe.mask |= ENV_HAVE_UV_COORD;
00325         
00326         if (mask & ENV_WANT_NORMAL) {
00327             pe.normal.set(pe.intersection);
00328             pe.normal.normalize();
00329             pe.mask |= ENV_HAVE_NORMAL;
00330             pe.normalOrientation = PointEnv::OUTWARDS_NORMAL;
00331         }
00332     }
00333 
00334     if (pdf) *pdf = .25 / (M_PI * _radius2);
00335 }
00336 
00337 bool Sphere::randomDirection(const Vector3& pov, Vector3& dir, double* pdf) 
00338 {
00339     /*
00340      * Reference: 
00341      *    Arvo J. et al.:
00342      *    State of the Art in Monte Carlo Ray Tracing
00343      *    for Relistic Image Sythesis, 
00344      *    SIGGRAPH2001 Course 29, Aug 13, 2001. Page 33.
00345      */
00346 
00347     double  wLength;
00348     Vector3 u(0.0, 1.0, 0.0);
00349     Vector3 v;
00350     Vector3 w(_centre);
00351 
00352     w.sub(pov);
00353     wLength = w.length();
00354     w.scale(1./wLength);
00355 
00356     (fabs(w.x) > fabs(w.y)) ? u.set(0.0, 1.0, 0.0) : u.set(1.0, 0.0, 0.0);
00357     
00358     v.cross(w, u); v.normalize();
00359     u.cross(w, v); u.normalize();
00360 
00361     double rd       = _radius / wLength;
00362     double cosAlpha = ESG_DBL_RAND * (sqrt(1. - rd * rd) - 1.) + 1.;
00363     double phi      = ESG_DBL_RAND * 2 * M_PI;
00364     double alpha    = acos(cosAlpha);
00365     double sinAlpha = sin(alpha);
00366 
00367     Vector3 aux(cos(phi) * sinAlpha, sin(phi) * sinAlpha, cosAlpha);
00368     aux.normalize();
00369     
00370     dir.set(aux.x * u.x + aux.y * v.x + aux.z * w.x,
00371             aux.x * u.y + aux.y * v.y + aux.z * w.y,
00372             aux.x * u.z + aux.y * v.z + aux.z * w.z);
00373 
00374     // PDF: to do
00375     if (pdf)
00376         cerr << "Sphere::randomDirection(): PDF is not implemented" << endl;
00377     
00378     return true;
00379 }
00380 
00381 Interval Sphere::extent(const Vector3& direction) const
00382 {
00383   Interval retInt;
00384   float    hlp;
00385 
00386   hlp = _centre.dot(direction);
00387   retInt.min = hlp - _radius;
00388   retInt.max = hlp + _radius;
00389   return retInt;
00390 }
00391 
00392 Geometry* Sphere::clone(const Matrix4* pTrMat) const
00393 {
00394     Sphere* pRet = new Sphere();
00395     pRet->_duplicate_attributes(*this);
00396     if (pTrMat) pRet->_transform(*pTrMat);
00397     return pRet;
00398 }
00399 
00400 Vector3 Sphere::centroid() const
00401 {
00402     return _centre;
00403 }
00404 
00405 double Sphere::radius() const
00406 {
00407     return _radius;
00408 }
00409 
00410 double Sphere::radius(const Vector3& centroid) const
00411 {
00412     return (((Vector3)(centroid-_centre)).length() + _radius);
00413 }
00414 
00415 bool Sphere::separation(Geometry& geom, Vector3* pDir) 
00416 {
00417     register double rads = geom.radius() + _radius;
00418     Vector3 c(geom.centroid());
00419     c.sub(_centre);
00420     register double ns = c.normSquared();
00421     if (ns > rads * rads) {
00422         ns = sqrt(ns);
00423         if (pDir) pDir->set(c.x/ns, c.y/ns, c.z/ns); // normalize
00424         return true;
00425     }
00426     return false;
00427 }
00428 
00429 double Sphere::distance(const Geometry& geom, Vector3* pDir)
00430 {
00431     Vector3 c = geom.centroid();
00432     register double d = _radius + geom.radius(c);
00433     c.sub(_centre);
00434     register double d2 = c.length();
00435     if (pDir) pDir->set(c.x / d2, c.y / d2, c.z / d2);
00436     return (d2 - d);
00437 }
00438 
00439 void Sphere::dump(const char* intent, const char* tab)
00440 {
00441     printf("%s geometry Sphere {", intent);
00442     printf("%s %s centre %f %f %f\n", intent, tab, _centre.x, _centre.y, _centre.z);
00443     printf("%s %s radius %f\n", intent, tab, _radius);
00444     printf("%s }", intent);
00445 }
00446 
00447 
00448 void Sphere::setCentre(const Vector3& centre)
00449 {
00450     _centre.set(centre);
00451 }
00452 
00453 
00454 void Sphere::setCentre(float x, float y, float z)
00455 {
00456     _centre.set(x,y,z);
00457 }
00458 
00459 void Sphere::setRadius(float radius)
00460 {
00461     _radius   = radius;
00462     _radius2  = radius * radius;
00463     _epsPlus  = (_radius + Geometry::EPS) * (_radius + Geometry::EPS);
00464     _epsMinus = (_radius - Geometry::EPS) * (_radius - Geometry::EPS);
00465 }
00466 

Generated on Wed Jun 28 12:24:32 2006 for esg by  doxygen 1.4.6