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

physics/Capsule.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)2004 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: Capsule.cpp 1031 2004-02-11 20:46:36Z jungd $
00019  
00020 ****************************************************************************/
00021 
00022 #include <physics/Capsule>
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/LOD>
00033 #include <osg/ShapeDrawable>
00034 
00035 using physics::Capsule;
00036 
00037 using base::Transform;
00038 using base::dom::DOMNode;
00039 using base::dom::DOMElement;
00040 
00041 using gfx::Segment3;
00042 using gfx::VisualTriangles;
00043 
00044 using physics::MassProperties;
00045 using physics::CollisionModel;
00046 using physics::OBBCollisionModel;
00047 using physics::GJKCollisionModel;
00048 
00049 
00050 using osg::Node;
00051 using osg::Group;
00052 using osg::Geode;
00053 using osg::LOD;
00054 using osg::Vec3;
00055 using osg::Vec2;
00056 
00057 
00058 Capsule::Capsule() 
00059   : _height(1.0), _radius(1.0), massPropertiesCached(false)
00060 {
00061 }
00062 
00063 Capsule::Capsule(Real height, Real radius) 
00064   : _height(height), _radius(radius), massPropertiesCached(false)
00065 {
00066   if (_radius<0) _radius=consts::epsilon;
00067 }
00068 
00069 Capsule::Capsule(const Capsule& c) 
00070   : _height(c._height), _radius(c._radius), massPropertiesCached(false)
00071 {
00072 }
00073 
00074 Capsule::~Capsule()
00075 {
00076 }
00077  
00078 
00079 /// \todo this is calculated for a cylinder, correct it for capsule
00080 const MassProperties& Capsule::getMassProperties(ref<const Material> material) const 
00081 {
00082   if (massPropertiesCached && (density == material->density()))
00083     return massProperties;
00084 
00085   density = material->density();
00086   Real volume = consts::Pi*Math::sqr(_radius)*_height;
00087   massProperties.mass = volume*density;
00088 
00089   const Real m = massProperties.mass;
00090   Matrix3 Ibody;
00091   Ibody.e(1,1) = m*Math::sqr(_radius)/4.0 + m*Math::sqr(_height)/12.0;
00092   Ibody.e(2,2) = Ibody.e(1,1);
00093   Ibody.e(3,3) = m*Math::sqr(_radius)/2.0;
00094 
00095   massProperties.setIbody(Ibody);
00096   massProperties.centerOfMass = Point3(0.0,0.0,0.0);
00097 
00098   massPropertiesCached = true;
00099   return massProperties;
00100 }
00101 
00102 
00103 
00104 Segment3 Capsule::shortestSegmentBetween(const base::Transform& t, const Point3& p) const
00105 {
00106   Unimplemented;
00107 }
00108 
00109 
00110 Segment3 Capsule::shortestSegmentBetween(const base::Transform& t, const gfx::Segment3& s) const
00111 {
00112   // find the point on the axis segment closest to s
00113   Point3 axisstart(0,0,-height()/2.0); axisstart = t.transform(axisstart);
00114   Point3 axisend(0,0,height()/2.0); axisend = t.transform(axisend);
00115   Segment3 axisseg( axisstart, axisend);
00116   Segment3 toaxis = s.shortestSegmentBetween(axisseg);
00117 
00118   // now, shortest segment is identical to that from a sphere centered at
00119   //  toaxis.e and s
00120   ref<Sphere> sphere( NewObj Sphere(radius()) );
00121   Transform center(toaxis.e);
00122   return sphere->shortestSegmentBetween(center, s); 
00123 }
00124 
00125 
00126 Segment3 Capsule::shortestSegmentBetween(const base::Transform& t, const gfx::Triangle3& tri) const
00127 {
00128   Unimplemented;
00129 }
00130 
00131 
00132 Segment3 Capsule::shortestSegmentBetween(const base::Transform& t, const gfx::Quad3& q) const
00133 {
00134   Unimplemented;
00135 }
00136 
00137 
00138 Segment3 Capsule::shortestSegmentBetween(const base::Transform& t1, ref<const Shape> s, const base::Transform& t2) const
00139 {
00140   Unimplemented;
00141 }
00142 
00143 
00144 
00145 /// \todo use Geometry & add normals
00146 osg::Node* Capsule::createOSGCapsule(Visual::Attributes visualAttributes,
00147                                      Int slices, Int stacks) const
00148 {
00149   
00150   bool onlyVerts = ((visualAttributes & Visual::VerticesOnly) != 0);
00151 
00152   // oriented along the z-axis 
00153   osg::ref_ptr<osg::ShapeDrawable> shapeDrawable = new osg::ShapeDrawable(new osg::Capsule(osg::Vec3(0.0f,0.0f,0.0f),radius(),height()));
00154   osg::ref_ptr<osg::TessellationHints> tessHints = new osg::TessellationHints();
00155   tessHints->setTargetNumFaces(slices*stacks);
00156   tessHints->setTessellationMode(osg::TessellationHints::USE_TARGET_NUM_FACES);
00157   tessHints->setCreateNormals(!onlyVerts);
00158   tessHints->setCreateTextureCoords(!onlyVerts);
00159   shapeDrawable->setTessellationHints(&(*tessHints));
00160 
00161   osg::Geode* geode = new osg::Geode();
00162   geode->addDrawable(&(*shapeDrawable));
00163 /*
00164   osg::TriangleMesh* mesh = new osg::TriangleMesh();
00165   array<Vec3>& verts = *new array<Vec3>(0,128);
00166   array<Int>& indices = *new array<Int>(0,128);
00167   if (((slices%4) != 0)) slices += (4-slices%4); // make multiple of 4
00168   const Int n = slices; 
00169   Real l = height()/2.0;
00170   Real r = radius();
00171 
00172   Real a = (consts::Pi*2.0)/Real(n);
00173   Real sa = Math::sin(a);
00174   Real ca = Math::cos(a);
00175   Real tmp;
00176   
00177   // cylinder body
00178   Real ny=1, nz=0;
00179   Int v=0;
00180   for(Int i=0; i<=n; i++) {
00181     verts.push_back(Vec3(ny*r,nz*r,l));
00182     if (v>1) {
00183       indices.push_back(v-2);
00184       indices.push_back(v-1);
00185       indices.push_back(v);
00186     }
00187     ++v;
00188 
00189     verts.push_back(Vec3(ny*r,nz*r,-l));
00190     if (v>1) {
00191       indices.push_back(v);
00192       indices.push_back(v-1);
00193       indices.push_back(v-2);
00194     }
00195     ++v;
00196 
00197     // rotate ny,nz
00198     tmp = ca*ny - sa*nz;
00199     nz = sa*ny + ca*nz;
00200     ny = tmp;
00201   }
00202   
00203   
00204   // first cap
00205   Real start_nx = 0, start_ny = 1;
00206   for(Int j=0; j<(n/4); j++) {
00207     // get start_n2 = rotated start_n
00208     Real start_nx2 =  ca*start_nx + sa*start_ny;
00209     Real start_ny2 = -sa*start_nx + ca*start_ny;
00210     // get n=start_n and n2=start_n2
00211     Real nx = start_nx; ny = start_ny; nz = 0;
00212     Real nx2 = start_nx2, ny2 = start_ny2, nz2 = 0;
00213 
00214     Int iv = v;
00215     for (Int i=0; i<=n; i++) {
00216 
00217       verts.push_back(Vec3(ny2*r,nz2*r,l+nx2*r));
00218       if ((v-iv)>1) {
00219         indices.push_back(v-2);
00220         indices.push_back(v-1);
00221         indices.push_back(v);
00222       }
00223       ++v;
00224 
00225       verts.push_back(Vec3(ny*r,nz*r,l+nx*r));
00226       if ((v-iv)>1) {
00227         indices.push_back(v);
00228         indices.push_back(v-1);
00229         indices.push_back(v-2);
00230       }
00231       ++v;
00232       
00233       // rotate n,n2
00234       tmp = ca*ny - sa*nz;
00235       nz = sa*ny + ca*nz;
00236       ny = tmp;
00237       tmp = ca*ny2- sa*nz2;
00238       nz2 = sa*ny2 + ca*nz2;
00239       ny2 = tmp;
00240     }
00241 
00242     start_nx = start_nx2;
00243     start_ny = start_ny2;
00244     
00245   }
00246   
00247   
00248   // second cap
00249   start_nx = 0;
00250   start_ny = 1;
00251   for (Int j=0; j<(n/4); j++) {
00252     // get start_n2 = rotated start_n
00253     Real start_nx2 = ca*start_nx - sa*start_ny;
00254     Real start_ny2 = sa*start_nx + ca*start_ny;
00255     // get n=start_n and n2=start_n2
00256     Real nx = start_nx; ny = start_ny; nz = 0;
00257     Real nx2 = start_nx2, ny2 = start_ny2, nz2 = 0;
00258 
00259     Int iv = v;
00260     for (Int i=0; i<=n; i++) {
00261 
00262       verts.push_back(Vec3(ny*r,nz*r,-l+nx*r));
00263       if ((v-iv)>1) {
00264         indices.push_back(v-2);
00265         indices.push_back(v-1);
00266         indices.push_back(v);
00267       }
00268       ++v;
00269 
00270       verts.push_back(Vec3(ny2*r,nz2*r,-l+nx2*r));
00271       if ((v-iv)>1) {
00272         indices.push_back(v);
00273         indices.push_back(v-1);
00274         indices.push_back(v-2);
00275       }
00276       ++v;
00277       
00278       // rotate n,n2
00279       tmp = ca*ny - sa*nz;
00280       nz = sa*ny + ca*nz;
00281       ny = tmp;
00282       tmp = ca*ny2- sa*nz2;
00283       nz2 = sa*ny2 + ca*nz2;
00284       ny2 = tmp;
00285     }
00286 
00287     start_nx = start_nx2;
00288     start_ny = start_ny2;
00289   }
00290   
00291   
00292   
00293   
00294   // convert to OSG arrays
00295   osg::Vec3Array* va = new osg::Vec3Array(verts.size());
00296   for(Int i=0; i<verts.size(); i++)
00297     (*va)[i] = verts[i];
00298   mesh->setVertices(va);
00299 
00300   osg::IntArray* ia = new osg::IntArray(indices.size());
00301   for(Int i=0; i<indices.size(); i++) {
00302     (*ia)[i] = indices[i];
00303   }
00304   mesh->setIndices(ia);
00305 
00306   osg::Geode* geode = new osg::Geode();
00307   geode->setName("Capsule");
00308   geode->addDrawable(new osg::ShapeDrawable(mesh));
00309   */ 
00310   return geode;
00311 }
00312 
00313 
00314 
00315 osg::Node* Capsule::createOSGVisual(Visual::Attributes visualAttributes) const
00316 {
00317   if ((node!=0) && (attributes==visualAttributes))
00318     return &(*node);
00319 
00320   Real r = Math::maximum(0.5,radius());
00321   Real h = Math::maximum(1.0,height());
00322 
00323 //  osg::Node* node0 = createOSGCapsule(visualAttributes, Int(52*r), (h<40)?Int(12*h):2000);
00324 //  osg::Node* node1 = createOSGCapsule(visualAttributes, Int(40*r), (h<40)?Int(8*h):1300);
00325 //  osg::Node* node2 = createOSGCapsule(visualAttributes, Int(20*r), (h<40)?Int(4*h):700);
00326 //  osg::Node* node3 = createOSGCapsule(visualAttributes, 8, 1);
00327 
00328 //replace above after normals added (i.e. switched from mesh to geometry)
00329   osg::Node* node0 = createOSGCapsule(visualAttributes, Int(104*r), (h<40)?Int(12*h):2000);
00330   osg::Node* node1 = createOSGCapsule(visualAttributes, Int(80*r), (h<40)?Int(8*h):1300);
00331   osg::Node* node2 = createOSGCapsule(visualAttributes, Int(40*r), (h<40)?Int(4*h):700);
00332   osg::Node* node3 = createOSGCapsule(visualAttributes, 16, 1);
00333 
00334   
00335   osg::LOD* lod = new osg::LOD();
00336   lod->setName("Capsule");
00337   lod->addChild(node0);
00338   lod->addChild(node1);
00339   lod->addChild(node2);
00340   lod->addChild(node3);
00341 
00342   lod->setRange(0,0,2.0);
00343   lod->setRange(1,2.0,16.0);
00344   lod->setRange(2,16.0,120.0*Math::maximum(r,h));
00345   lod->setRange(3,120.0*Math::maximum(r,h),consts::Infinity);
00346 
00347   if (!(visualAttributes & Visual::ShowAxes))
00348     node = lod;
00349   else {
00350     Group* group = new Group();
00351     group->addChild( lod );
00352     group->addChild( createOSGAxes(base::Dimension3(2.0*radius(),2.0*radius(),height())) );
00353     node = group;
00354   }
00355 
00356   attributes = visualAttributes;
00357   return &(*node);
00358 }
00359 
00360 
00361 
00362 base::ref<CollisionModel> Capsule::getCollisionModel(CollisionModel::CollisionModelType modelType) const
00363 {
00364   if ((collisionModel!=0) && 
00365       ((this->modelType==modelType) || (modelType==CollisionModel::AnyModel)))
00366     return collisionModel;
00367   
00368   collisionModel = Shape::getCollisionModel(modelType);
00369   this->modelType=modelType;
00370 
00371   return collisionModel;
00372 }
00373 
00374 
00375 void Capsule::serialize(base::Serializer& s)
00376 {
00377   s(_height)(_radius);
00378 
00379   if (s.isInput()) {
00380     massPropertiesCached = false;
00381     node = 0;
00382     collisionModel = ref<CollisionModel>(0);
00383   }
00384 }
00385 
00386 
00387 
00388 bool Capsule::formatSupported(String format, Real version, ExternalizationType type) const
00389 { 
00390   return ( (format=="xml") && (version==1.0) ); 
00391 }
00392 
00393 
00394 void Capsule::externalize(base::Externalizer& e, String format, Real version)
00395 {
00396   if (format == "") format = "xml";
00397                                                                                                                                                                                                     
00398   if (!formatSupported(format,version,e.ioType()))
00399     throw std::invalid_argument(Exception(String("format ")+format+" v"+base::realToString(version)+" unsupported"));
00400                                                                                                                                                                                                     
00401   if (e.isOutput()) {
00402     
00403     DOMElement*  capsuleElem = e.createElement("capsule");
00404     e.setElementAttribute(capsuleElem,"height",base::realToString(_height));
00405     e.setElementAttribute(capsuleElem,"radius",base::realToString(_radius));
00406     
00407     e.appendElement(capsuleElem);
00408   }
00409   else { // input
00410     
00411     massPropertiesCached = false;
00412     node = 0;
00413     collisionModel = ref<CollisionModel>(0);
00414 
00415     DOMNode* context = e.context();
00416     
00417     DOMElement* capsuleElem = e.getFirstElement(context, "capsule");
00418         
00419     _height = e.toReal( e.getDefaultedElementAttribute(capsuleElem, "height", "1") );
00420     _radius = e.toReal( e.getDefaultedElementAttribute(capsuleElem, "radius", "1") );
00421         
00422     e.removeElement(capsuleElem);
00423     
00424   }
00425 }
00426 
00427 

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