Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

physics/Sphere.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)1996 David Jung <opensim@pobox.com>
00003 
00004   This program/file is free software; you can redistribute it and/or modify
00005   it under the terms of the GNU General Public License as published by
00006   the Free Software Foundation; either version 2 of the License, or
00007   (at your option) any later version.
00008   
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012   GNU General Public License for more details. (http://www.gnu.org)
00013   
00014   You should have received a copy of the GNU General Public License
00015   along with this program; if not, write to the Free Software
00016   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017   
00018   $Id: Sphere.cpp 1031 2004-02-11 20:46:36Z jungd $
00019  
00020 ****************************************************************************/
00021 
00022 #include <physics/Sphere>
00023 
00024 #include <base/Externalizer>
00025 #include <gfx/VisualTriangles>
00026 #include <physics/Material>
00027 #include <physics/OBBCollisionModel>
00028 #include <physics/GJKCollisionModel>
00029 
00030 #include <osg/Group>
00031 #include <osg/Geode>
00032 #include <osg/ShapeDrawable>
00033 #include <osg/LOD>
00034 
00035 using base::dom::DOMNode;
00036 using base::dom::DOMElement;
00037 using gfx::Point2;
00038 using gfx::Point3;
00039 using gfx::Vector3;
00040 using gfx::Segment3;
00041 using gfx::VisualTriangles;
00042 using physics::Sphere;
00043 using physics::MassProperties;
00044 using physics::CollisionModel;
00045 using physics::OBBCollisionModel;
00046 using physics::GJKCollisionModel;
00047 
00048 
00049 using osg::Node;
00050 using osg::Group;
00051 using osg::Geode;
00052 using osg::LOD;
00053 using osg::Vec3;
00054 using osg::Vec2;
00055 
00056 
00057 Sphere::Sphere() 
00058   : _radius(1.0), massPropertiesCached(false)
00059 {
00060 }
00061 
00062 Sphere::Sphere(Real radius) 
00063   : _radius(radius), massPropertiesCached(false)
00064 {
00065 }
00066 
00067 Sphere::Sphere(const Sphere& s)
00068   : _radius(s._radius), massPropertiesCached(false)
00069 {
00070 }
00071 
00072 Sphere::~Sphere() 
00073 {
00074 }
00075 
00076 
00077 const MassProperties& Sphere::getMassProperties(ref<const Material> material) const 
00078 {
00079   if (massPropertiesCached && (density == material->density()))
00080     return massProperties;
00081  
00082   density = material->density();
00083   Real volume = 4.0*(consts::Pi*Math::cube(radius()))/3.0;
00084   massProperties.mass = volume*density;
00085   
00086   Matrix3 Ibody; 
00087   Ibody(1,1) = Ibody(2,2) = Ibody(3,3) = (2.0*massProperties.mass*Math::sqr(radius()))/5.0;
00088   massProperties.setIbody(Ibody);
00089   
00090   massProperties.centerOfMass = Point3(0.0,0.0,0.0);
00091   
00092   massPropertiesCached = true;
00093   return massProperties;
00094 }
00095 
00096 
00097 Segment3 Sphere::shortestSegmentBetween(const base::Transform& t, const Point3& p) const
00098 {
00099   Point3 c(t.getTranslation());
00100   Vector3 diff( p - c );
00101   Real diffLen = diff.length();
00102   Segment3 seg;
00103   seg.e = p;
00104   
00105   if (diffLen > consts::epsilon)
00106     seg.s = c + radius()*diff.normalize();
00107   else
00108     seg.s = c + radius()*Vector3(1,0,0);  
00109 
00110   return seg;
00111 }
00112 
00113 
00114 
00115 Segment3 Sphere::shortestSegmentBetween(const base::Transform& t, const gfx::Segment3& s) const
00116 {
00117   // algorithm from XEngine by Martin Ecker ( http://xengine.sourceforge.net )
00118   Point3 c(t.getTranslation());
00119   Vector3 segDir( s.e - s.s );
00120   Real distance = 0;
00121   Point3 p1,p2;
00122   Real DlengthSquared = s.norm();
00123   Real u = 0;
00124   if (DlengthSquared >= consts::epsilon)                // avoid division by 0
00125     u = dot(segDir, c - s.s) / DlengthSquared;
00126 
00127   Vector3 R;
00128   if (u <= 0)
00129     R = s.s;
00130   else if (u >= 1)
00131     R = s.e;
00132   else
00133     R = s.s + u * segDir;
00134 
00135   Vector3 E = R - c;
00136   Real Elength = E.length();
00137 
00138   if (Elength > radius())               // segment does not intersect the sphere
00139   {
00140     p1 = R;
00141     p2 = c + (radius() / Elength) * E;          // note that E cannot have zero length
00142     distance = Elength - radius();
00143   }
00144   else                                                                                          // segment intersects the sphere or is completely contained
00145   {
00146     Vector3 centerToStart = s.s  - c;
00147     Vector3 centerToEnd = s.e - c;
00148     Real centerToStartLength = centerToStart.length();
00149     Real centerToEndLength = centerToEnd.length();
00150     
00151     if (centerToStartLength < radius() && centerToEndLength < radius()) {
00152       // the segment is completely contained within the sphere
00153       if (centerToStartLength < radius()) {             // then the end point must be the closest to the sphere's shell
00154         p1 = s.e;
00155         p2 = c + radius() * centerToEnd.normalize();
00156         distance = radius() - centerToEndLength;
00157       }
00158       else {                                    // the start point is closest to the sphere's shell
00159         p1 = s.s;
00160         p2 = c + radius() * centerToStart.normalize();
00161         distance = radius() - centerToStartLength;
00162       }
00163     }
00164     else {      // the segment intersects the sphere
00165       Vector3 projCenter = s.s - u * segDir;
00166 
00167       Vector3 intersection = projCenter;
00168       if (DlengthSquared >= consts::epsilon)            // to avoid division by 0
00169       {
00170         // take abs of radius^2 - (projCenter-c).norm() in case it's negative (DJ)
00171         Real s = Math::sqrt(Math::abs( Math::sqr(radius()) - (projCenter - c).norm() ));
00172         // if the end point of the segment is inside the sphere, the direction vector points from outside of 
00173         // the sphere to the inside; therefore we have to swap the direction vector which we do by inverting s
00174         if (centerToEndLength < radius())
00175           s = -s;
00176         intersection += (s / Math::sqrt(DlengthSquared)) * segDir;
00177       }
00178       
00179       p1 = intersection;
00180       p2 = intersection;
00181       distance = 0;
00182     }
00183   }
00184 
00185   return Segment3(p2,p1);
00186 }
00187 
00188 
00189 Segment3 Sphere::shortestSegmentBetween(const base::Transform& t, const gfx::Triangle3& tri) const
00190 {
00191   Unimplemented;
00192 }
00193 
00194 
00195 Segment3 Sphere::shortestSegmentBetween(const base::Transform& t, const gfx::Quad3& q) const
00196 {
00197   Unimplemented;
00198 }
00199 
00200 
00201 Segment3 Sphere::shortestSegmentBetween(const base::Transform& t1, ref<const Shape> s, const base::Transform& t2) const
00202 {
00203   if (instanceof(*s, const Sphere)) {
00204 
00205     ref<const Sphere> sphere1(this);
00206     ref<const Sphere> sphere2(narrow_ref<const Sphere>(s));
00207     Point3 c1(t1.getTranslation());
00208     Point3 c2(t2.getTranslation());
00209     Real r1( sphere1->radius() );
00210     Real r2( sphere2->radius() );
00211 
00212     Point3 p1,p2;
00213     Vector3 D = c2 - c1;
00214     Real Dlength = D.length();
00215     
00216     // normalize D
00217     if (Dlength < consts::epsilon)
00218       D.setZero();
00219     else
00220       D /= Dlength;
00221     
00222     if (Dlength > r1 + r2) {            // spheres do not intersect
00223       p1 = c1 + r1 * D;
00224       p2 = c2 - r2 * D;
00225     }
00226     else if (Dlength < Math::abs(r1 - r2))              // one sphere is contained in the other
00227     {
00228       p1 = c1 + r1 * D;
00229       p2 = c2 + r2 * D;
00230     }
00231     else if (Dlength < consts::epsilon)         // |D| == 0, the spheres are equal
00232     {
00233       Vector3 intersection = c1;
00234       intersection.x += r1;
00235       
00236       p1 = intersection;
00237       p2 = intersection;
00238     }
00239     else {              // the spheres intersect
00240       Real t = 0.5 * (1 + (Math::sqr(r1) - Math::sqr(r2)) / Math::sqr(Dlength));
00241       Real r = Math::sqrt(Math::sqr(r1) - Math::sqr(t * Dlength));
00242       Vector3 C = c1 + t * D;
00243       
00244       Matrix3 basis;
00245       basis.setOrthonormalBasisOf(D);// get the coordinate system of the circle, D is the plane normal and therefore the z vector of the basis
00246       Vector3 intersection = C + r * basis.row(1);
00247       
00248       p1 = intersection;
00249       p2 = intersection;
00250     }
00251 
00252     return Segment3(p1,p2);
00253     
00254   }
00255   else if (instanceof(*s, const Box)) {
00256     
00257     return s->shortestSegmentBetween(t2,ref<const Shape>(this), t1).swapEnds(); 
00258     
00259   }
00260   else {
00261     Unimplemented;
00262   }
00263 }
00264 
00265 
00266 
00267 
00268 
00269 
00270 gfx::Point3 Sphere::support(const base::Vector3& v) const
00271 {
00272   Real s = v.length();
00273   if (s > consts::epsilon) 
00274     return v*(_radius/s);
00275   else
00276     return base::Point3(0,0,0);
00277 }
00278 
00279 
00280 osg::Node* Sphere::createOSGSphere(Visual::Attributes visualAttributes,
00281                                    Int slices) const
00282 {
00283   /*
00284   bool onlyVerts = (visualAttributes & VerticesOnly);
00285 
00286   Real rho, drho, theta, dtheta;
00287   Real x, y, z;
00288   Real s, t, ds, dt;
00289   Int index;
00290   SInt i,j;
00291   
00292   drho = consts::Pi / Real(slices);
00293   dtheta = 2.0*consts::Pi / Real(slices);
00294   
00295   // texturing: s goes from 0.0/0.25/0.5/0.75/1.0 at +y/+x/-y/-x/+y axis 
00296   // t goes from -1.0/+1.0 at z = -radius/+radius (linear along longitudes) 
00297   
00298   ds = 1.0 / Real(slices);
00299   dt = 1.0 / Real(slices);
00300   t = 1.0;
00301   
00302   // draw stacks as quad strips 
00303   array<Vec3>& coords = *new array<Vec3>(slices*(slices*2+2));
00304   array<Vec3>& normals = *new array<Vec3>(slices*(slices*2+2));
00305   array<Vec2>& texCoords = *new array<Vec2>(slices*(slices*2+2));
00306   array<int>& lengths = *new array<int>(slices);
00307   for(Int i=0; i<slices; i++)
00308     lengths[i] = slices*2+2;
00309   index=0;
00310   
00311   for (i = 0; i < (SInt)slices; i++) {
00312     rho = i * drho;
00313     s = 0.0;
00314     for (j = 0; j <= (SInt)slices; j++) {
00315       theta = (j == (SInt)slices) ? 0.0 : j * dtheta;
00316       x = -Math::sin(theta) * Math::sin(rho);
00317       y = Math::cos(theta) * Math::sin(rho);
00318       z = Math::cos(rho);
00319 
00320       normals[index] = Vec3(x,y,z);
00321       if (!onlyVerts)
00322         texCoords[index] = Vec2(s,t);
00323       coords[index] = Vec3(x*radius(),y*radius(),z*radius());
00324       index++;
00325       
00326       x = -Math::sin(theta) * Math::sin(rho + drho);
00327       y = Math::cos(theta) * Math::sin(rho + drho);
00328       z = Math::cos(rho + drho);
00329       
00330       normals[index] = Vec3(x,y,z);
00331       if (!onlyVerts)
00332         texCoords[index] = Vec2(s,t-dt);
00333       s += ds;
00334       coords[index] = Vec3(x*radius(),y*radius(),z*radius());
00335       index++;
00336     }
00337     t -= dt;
00338   }
00339 
00340   GeoSet* sphere = new GeoSet();
00341   sphere->setPrimType(GeoSet::QUAD_STRIP);
00342   sphere->setNumPrims(slices);
00343   sphere->setCoords(coords.c_array());
00344   sphere->setPrimLengths(lengths.c_array());
00345   if (!onlyVerts) {
00346     sphere->setNormals(normals.c_array());
00347     sphere->setNormalBinding(osg::GeoSet::BIND_PERVERTEX);
00348     sphere->setTextureCoords(texCoords.c_array());
00349     sphere->setTextureBinding(osg::GeoSet::BIND_PERVERTEX);
00350   }
00351 */
00352   osg::Geode* geode = new osg::Geode();
00353 //  geode->addDrawable(sphere);
00354   geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0,0,0),radius())));
00355 
00356   return geode;
00357 }
00358 
00359 
00360 osg::Node* Sphere::createOSGVisual(Visual::Attributes visualAttributes) const
00361 {
00362   if ((node!=0) && (attributes==visualAttributes))
00363     return &(*node);
00364 
00365   Real d = Math::maximum(radius()*2.0,1.0);
00366 
00367   osg::Node* node0 = createOSGSphere(visualAttributes, Int(22*d));
00368   osg::Node* node1 = createOSGSphere(visualAttributes, Int(16*d));
00369   osg::Node* node2 = createOSGSphere(visualAttributes, Int(8*d));
00370   osg::Node* node3 = createOSGSphere(visualAttributes, 8);
00371   
00372   osg::LOD* lod = new osg::LOD();
00373   lod->setName("Sphere");
00374   lod->addChild(node0);
00375   lod->addChild(node1);
00376   lod->addChild(node2);
00377   lod->addChild(node3);
00378 
00379   lod->setRange(0,0,2.0);
00380   lod->setRange(1,2.0,16.0);
00381   lod->setRange(2,16.0,120.0*d);
00382   lod->setRange(3,120.0*d,consts::Infinity);
00383 
00384   if (!(visualAttributes & Visual::ShowAxes))
00385     node = lod;
00386   else {
00387     Group* group = new Group();
00388     group->addChild( lod );
00389     group->addChild( createOSGAxes(base::Dimension3(radius(),radius(),radius())*2.0) );
00390     node = group;
00391   }
00392 
00393   attributes = visualAttributes;
00394   return &(*node);
00395 }
00396 
00397 
00398 base::ref<CollisionModel> Sphere::getCollisionModel(CollisionModel::CollisionModelType modelType) const
00399 {
00400   if ((collisionModel!=0) && 
00401       ((this->modelType==modelType) || (modelType==CollisionModel::AnyModel)))
00402     return collisionModel;
00403   
00404   collisionModel = Shape::getCollisionModel(modelType);
00405   this->modelType=modelType;
00406 
00407   return collisionModel;
00408 }
00409 
00410 
00411 void Sphere::serialize(base::Serializer& s)
00412 {
00413   s(_radius);
00414 
00415   if (s.isInput()) {
00416     massPropertiesCached = false;
00417     node = 0;
00418     collisionModel = ref<CollisionModel>(0);
00419   }
00420 }
00421 
00422 
00423 
00424 bool Sphere::formatSupported(String format, Real version, ExternalizationType type) const
00425 { 
00426   return ( (format=="xml") && (version==1.0) ); 
00427 }
00428 
00429 
00430 void Sphere::externalize(base::Externalizer& e, String format, Real version)
00431 {
00432   if (format == "") format = "xml";
00433                                                                                                                                                                                                     
00434   if (!formatSupported(format,version,e.ioType()))
00435     throw std::invalid_argument(Exception(String("format ")+format+" v"+base::realToString(version)+" unsupported"));
00436                                                                                                                                                                                                     
00437   if (e.isOutput()) {
00438     
00439     DOMElement*  sphereElem = e.createElement("sphere");
00440     e.setElementAttribute(sphereElem,"radius",base::realToString(_radius));
00441     
00442     e.appendElement(sphereElem);
00443   }
00444   else { // input
00445 
00446     massPropertiesCached = false;
00447     node = 0;
00448     collisionModel = ref<CollisionModel>(0);
00449     
00450     DOMNode* context = e.context();
00451     
00452     DOMElement* sphereElem = e.getFirstElement(context, "sphere");
00453         
00454     _radius = e.toReal( e.getDefaultedElementAttribute(sphereElem, "radius", "1") );
00455 
00456     e.removeElement(sphereElem);
00457          
00458   }
00459 }
00460 
00461 

Generated on Thu Jul 29 15:56:30 2004 for OpenSim by doxygen 1.3.6