RayPickInteractor.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002 *       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 3      *
00003 *                (c) 2006-2008 MGH, INRIA, USTL, UJF, CNRS                    *
00004 *                                                                             *
00005 * This library is free software; you can redistribute it and/or modify it     *
00006 * under the terms of the GNU Lesser General Public License as published by    *
00007 * the Free Software Foundation; either version 2.1 of the License, or (at     *
00008 * your option) any later version.                                             *
00009 *                                                                             *
00010 * This library is distributed in the hope that it will be useful, but WITHOUT *
00011 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
00012 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
00013 * for more details.                                                           *
00014 *                                                                             *
00015 * You should have received a copy of the GNU Lesser General Public License    *
00016 * along with this library; if not, write to the Free Software Foundation,     *
00017 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
00018 *******************************************************************************
00019 *                               SOFA :: Modules                               *
00020 *                                                                             *
00021 * Authors: The SOFA Team and external contributors (see Authors.txt)          *
00022 *                                                                             *
00023 * Contact information: contact@sofa-framework.org                             *
00024 ******************************************************************************/
00025 #include <sofa/component/collision/RayPickInteractor.inl>
00026 #include <sofa/component/collision/RayContact.h>
00027 #include <sofa/component/collision/SphereModel.h>
00028 #include <sofa/component/collision/TriangleModel.h>
00029 #include <sofa/component/collision/TetrahedronModel.h>
00030 #include <sofa/component/collision/SphereTreeModel.h>
00031 #include <sofa/component/collision/DistanceGridCollisionModel.h>
00032 #include <sofa/component/mapping/BarycentricMapping.h>
00033 #include <sofa/component/mapping/RigidMapping.h>
00034 #include <sofa/component/mapping/SkinningMapping.h>
00035 #include <sofa/component/MechanicalObject.h>
00036 #include <sofa/core/componentmodel/behavior/MechanicalState.h>
00037 #include <sofa/helper/Factory.inl>
00038 #include <sofa/helper/system/gl.h>
00039 
00040 
00041 namespace sofa
00042 {
00043 
00044 namespace component
00045 {
00046 
00047 namespace collision
00048 {
00049 
00050 using namespace sofa::defaulttype;
00051 
00052 template<>
00053 void DefaultPickingManager<defaulttype::Vec3Types,forcefield::VectorSpringForceField<defaulttype::Vec3Types> >::addContact(forcefield::VectorSpringForceField<defaulttype::Vec3Types>* ff, int index1, int index2, double stiffness, double mu_v, double /*length*/, const Vector3& p1, const Vector3& p2)
00054 {
00055     ff->addSpring(index1, index2, stiffness, mu_v, (p2-p1));
00056 }
00057 
00058 template<>
00059 void DefaultPickingManager<defaulttype::Vec3Types,forcefield::StiffSpringForceField<defaulttype::Vec3Types> >::addContact(forcefield::StiffSpringForceField<defaulttype::Vec3Types>* ff, int index1, int index2, double stiffness, double mu_v, double length, const Vector3& /*p1*/, const Vector3& /*p2*/)
00060 {
00061     ff->addSpring(index1, index2, stiffness, mu_v, length);
00062 }
00063     
00064 template class DefaultPickingManager< Vec3Types, forcefield::VectorSpringForceField<Vec3Types> >;
00065 
00066 helper::Creator<BasePickingManager::PickingManagerFactory, DefaultPickingManager< Vec3Types, forcefield::VectorSpringForceField<Vec3Types> > > PickingVectorSpringVec3Class ("VectorSpringVec3d",true);
00067 
00068 
00069 BasePickingManager* BasePickingManager::Create(std::string name, core::objectmodel::BaseContext* context)
00070 {
00071     BasePickingManager* o = PickingManagerFactory::CreateObject(name, context);
00072     if (o != NULL)
00073     {
00074     o->setName(name);
00075     if (context != NULL)
00076         context->addObject(o);
00077     }
00078     return o;
00079 }
00080 
00081 int BasePickingManager::CreateAll(std::vector<BasePickingManager*>& instances, core::objectmodel::BaseContext* context)
00082 {
00083     int n = 0;
00084     for (PickingManagerFactory::iterator it = PickingManagerFactory::getInstance()->begin(), itend = PickingManagerFactory::getInstance()->end(); it != itend; ++it)
00085     {
00086     BasePickingManager* o = it->second->createInstance(context);
00087     if (o != NULL)
00088     {
00089         o->setName(it->first);
00090         if (context != NULL)
00091         context->addObject(o);
00092         instances.push_back(o);
00093         ++n;
00094     }
00095     }
00096     return n;
00097 }
00098 
00099 
00100 BasePickingManager::~BasePickingManager()
00101 {
00102 }
00103 
00104 
00105 
00106 RayPickInteractor::RayPickInteractor()
00107 :  state(DISABLED), button(0)
00108 {
00109     transform.identity();
00110     this->contactStiffness.setValue(100);
00111 }
00112 
00113 RayPickInteractor::~RayPickInteractor()
00114 {
00115 }
00116 
00117 void RayPickInteractor::init()
00118 {
00119     this->RayModel::init();
00120     this->getContext()->get<BasePickingManager,helper::vector<BasePickingManager*> >(&pickManagers,core::objectmodel::BaseContext::SearchRoot);
00121     if (pickManagers.empty())
00122     {
00123     std::cout << "RayPickInteractor: creating default pick managers." << std::endl;
00124     BasePickingManager::CreateAll(pickManagers, this->getContext());
00125     }
00126 }
00127 
00128 
00129 bool RayPickInteractor::isActive()
00130 {
00131     return state==ACTIVE || state==PRESENT || state == FIRST_INPUT || state == IS_CUT; // state FIRST_INPUT controls the first input point for incision in a triangular mesh
00132 }
00133 
00134 bool RayPickInteractor::isAttached()
00135 {
00136     for (unsigned int i=0; i<pickManagers.size(); i++)
00137     if (pickManagers[i]->isAttached())
00138         return true;
00139     return false;
00140 }
00141 
00142 void RayPickInteractor::pickBody()
00143 {
00144     const int oldState = state;
00145 
00146     // We find the first contact for each ray
00147     for (int r=0; r<getNbRay(); r++)
00148     {
00149         Ray ray = getRay(r);
00150         double dist = 0;
00151         core::componentmodel::collision::DetectionOutput* collision = findFirstCollision(ray,&dist);
00152         if (collision==NULL)
00153             continue;
00154         core::CollisionElementIterator elem1 = collision->elem.first;
00155         core::CollisionElementIterator elem2 = collision->elem.second;
00156         Vector3 p1 = collision->point[0];
00157         Vector3 p2 = collision->point[1];
00158         if (elem2.getCollisionModel() == this)
00159         { // swap elements
00160             elem2 = collision->elem.first;
00161             elem1 = collision->elem.second;
00162             p2 = collision->point[0];
00163             p1 = collision->point[1];
00164         }
00165         bool processed = false;
00166         for (unsigned int i=0;!processed && i<pickManagers.size();++i)
00167         {
00168             if (pickManagers[i]->attach(button, elem1, elem2, dist, p1, p2))
00169             processed = true;
00170         }
00171         if (!processed) // default cases not yet handled by external managers
00172         switch(button)
00173         {
00174         case 0: // attach body
00175         break;
00176 
00177         case 1: // element removal
00178             {
00179 
00180                 sofa::core::componentmodel::topology::TopologyModifier* topoMod; 
00181                 elem2.getCollisionModel()->getContext()->get(topoMod);  
00182 
00183                 if(topoMod){ // dynamic topology
00184                     // Handle Removing of topological element (from any type of topology)
00185                     topo_changeManager.removeItemsFromCollisionModel(elem2);
00186                 }
00187             }
00188             break;
00189 
00190         case 2: // incision
00191             {
00192                 sofa::core::componentmodel::topology::TopologyModifier* topoMod; 
00193                 elem2.getCollisionModel()->getContext()->get(topoMod);  
00194 
00195                 if(topoMod){ // dynamic topology
00196                     // Handle Cutting, using global variables to register the two last input points
00197                     bool attach = topo_changeManager.incisionCollisionModel(elem2, p2, state == FIRST_INPUT, state == IS_CUT);
00198                     if(attach)
00199                         state = ATTACHED;
00200                 }
00201             }
00202             break;
00203         }
00204     }
00205 
00206     if(oldState == FIRST_INPUT || oldState == IS_CUT)
00207     {
00208         state = SECOND_INPUT; // force the state SECOND_INPUT
00209     }
00210     else if (isAttached())
00211     {
00212         state = ATTACHED;
00213     }
00214 
00215     if (oldState != state)
00216     {
00217         std::cout << "Interactor State: ";
00218         switch (state)
00219         {
00220         case DISABLED:
00221             std::cout << "DISABLED";
00222             break;
00223         case PRESENT:
00224             std::cout << "PRESENT";
00225             break;
00226         case ACTIVE:
00227             std::cout << "ACTIVE";
00228             break;
00229         case ATTACHED:
00230             std::cout << "ATTACHED";
00231             break;
00232         case FIRST_INPUT:
00233             std::cout << "FIRST_INPUT"; // first input point for incision in a triangular mesh
00234             break;
00235         case SECOND_INPUT:
00236             std::cout << "SECOND_INPUT"; // second input point for incision in a triangular mesh
00237             break;
00238         case IS_CUT:
00239             std::cout << "IS_CUT"; 
00240             break;
00241         }
00242         std::cout << std::endl;
00243     }
00244 }
00245 
00246 void RayPickInteractor::releaseBody()
00247 {
00248     for (unsigned int i=0; i<pickManagers.size(); i++)
00249     if (pickManagers[i]->isAttached())
00250         pickManagers[i]->release();
00251 }
00252 
00254 void RayPickInteractor::updatePosition(double /*dt*/)
00255 {
00256     if (state == DISABLED && getNbRay()>0)
00257     { // we need to disable our interactor
00258         resize(0);
00259     }
00260     else if (state != DISABLED && getNbRay()==0)
00261     { // we need to enable our interactor
00262         std::cout << "ADD RAY"<<std::endl;
00263         setNbRay(1);
00264         getRay(0).l() = 10000;
00265     }
00266 
00267     if ((state == ACTIVE || state == FIRST_INPUT || state == IS_CUT) && !contacts.empty()) // state FIRST_INPUT controls the first input point for incision in a triangular mesh
00268     { 
00269         pickBody();
00270     } 
00271     else if (state != ATTACHED && isAttached())
00272     { 
00273         releaseBody();
00274     }
00275 
00276     // update the ray position
00277     if (getNbRay()>=1)
00278     { // update current ray position
00279         Ray ray = getRay(0);
00280         ray.origin() = transform*Vec4d(0,0,0,1);
00281         ray.direction() = transform*Vec4d(0,0,1,0);
00282         ray.direction().normalize();
00283     }
00284 
00285     for (unsigned int i=0; i<pickManagers.size(); i++)
00286     pickManagers[i]->update();
00287 }
00288 
00290 void RayPickInteractor::newPosition(const Vector3& /*translation*/, const Quat& /*rotation*/, const Mat4x4d& transformation)
00291 {
00292     transform = transformation;
00293 }
00294 
00295 void RayPickInteractor::newEvent(const std::string& command)
00296 {
00297     const int oldState = state;
00298 
00299     if (command == "hide")
00300     {
00301         state = DISABLED;
00302     }
00303     else
00304     {
00305         switch(oldState)
00306         {
00307         case DISABLED:
00308             if (command == "show")
00309             {
00310                 state = PRESENT;
00311             }
00312         case PRESENT:
00313             if (command == "pick" || command == "pick1") // detection of Left-click 
00314             {
00315                 state = ACTIVE;
00316                 button = 0;
00317             }
00318             else if (command == "pick2") // detection of Right-click
00319             {
00320                 state = ACTIVE;
00321                 button = 1;
00322             }
00323             break;
00324         case ACTIVE:
00325         case ATTACHED:
00326             if (command == "release")
00327                 state = PRESENT;
00328             break;
00329         case FIRST_INPUT:
00330             // this state changes into SECOND_INPUT after a position update
00331             break;
00332         case IS_CUT:
00333             // this state changes into SECOND_INPUT after a position update
00334             break;
00335         case SECOND_INPUT:
00336             if (command == "pick3") // detection of Mid-click
00337             {
00338                 state = IS_CUT;
00339                 button = 2;
00340             }
00341             break;
00342         }
00343     }
00344 
00345     if (command == "pick3") // detection of Mid-click
00346     {
00347         if (oldState!=SECOND_INPUT)
00348         {
00349             state = FIRST_INPUT;
00350             button = 2;
00351         }
00352     }
00353 
00354     if (oldState != state)
00355     {
00356         std::cout << "Interactor State: ";
00357         switch (state)
00358         {
00359         case DISABLED:
00360             std::cout << "DISABLED";
00361             break;
00362         case PRESENT:
00363             std::cout << "PRESENT";
00364             break;
00365         case ACTIVE:
00366             std::cout << "ACTIVE, button = "<<button;
00367             break;
00368         case ATTACHED:
00369             std::cout << "ATTACHED";
00370             break;
00371         case FIRST_INPUT:
00372             std::cout << "FIRST_INPUT, button = "<<button; // First input point for incision
00373             break;
00374         case SECOND_INPUT:
00375             std::cout << "SECOND_INPUT, button = "<<button; // Second input point for incision
00376             break;
00377         case IS_CUT:
00378             std::cout << "IS_CUT";
00379             break;
00380         }
00381         std::cout << std::endl;
00382     }
00383 }
00384 
00385 core::componentmodel::collision::DetectionOutput* RayPickInteractor::findFirstCollision(const Ray& ray, double* dist)
00386 {
00387     core::componentmodel::collision::DetectionOutput* result = NULL;
00388     const Vector3& origin = ray.origin();
00389     const Vector3& direction = ray.direction();
00390     double l = ray.l();
00391     double mindist = 0;
00392     for (std::set<BaseRayContact*>::iterator it = contacts.begin(); it!=contacts.end(); ++it)
00393     {
00394         const sofa::helper::vector<core::componentmodel::collision::DetectionOutput*>& collisions = (*it)->getDetectionOutputs();
00395         for (unsigned int i=0;i<collisions.size();i++)
00396         {
00397             core::componentmodel::collision::DetectionOutput* collision = collisions[i];
00398             if (collision->elem.second == ray)
00399             {
00400                 if (!collision->elem.first.getCollisionModel()->isSimulated())
00401                     continue;
00402                 double d = (collision->point[1] - origin)*direction;
00403                 if (d<0.0 || d>l)
00404                     continue;
00405                 if (result==NULL || d<mindist)
00406                 {
00407                     result = collision;
00408                     mindist = d;
00409                 }
00410             }
00411             else
00412                 if (collision->elem.first == ray)
00413                 {
00414                     if (!collision->elem.second.getCollisionModel()->isSimulated())
00415                         continue;
00416                     double d = (collision->point[0] - origin)*direction;
00417                     if (d<0.0 || d>l)
00418                         continue;
00419                     if (result==NULL || d<mindist)
00420                     {
00421                         result = collision;
00422                         mindist = d;
00423                     }
00424                 }
00425         }
00426     }
00427     if (dist!=NULL && result!=NULL)
00428         *dist = mindist;
00429     return result;
00430 }
00431 
00432 void RayPickInteractor::draw()
00433 {
00434     if (getNbRay()>=1)
00435         this->RayModel::draw();
00436     if (state==PRESENT)
00437         glColor4f(0,1,0,1);
00438     else if (state==ATTACHED)
00439         glColor4f(1,1,1,1);
00440     else
00441         return;
00442     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
00443     glLineWidth(3);
00444     for (int r = 0; r < getNbRay(); r++)
00445     {
00446         Ray ray = getRay(r);
00447         double dist = 0;
00448         core::componentmodel::collision::DetectionOutput* collision = findFirstCollision(ray,&dist);
00449         if (collision==NULL)
00450             continue;
00451         core::CollisionElementIterator elem = collision->elem.first;
00452         if (elem.getIndex() < elem.getCollisionModel()->getSize())
00453             elem.draw();
00454     }
00455     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
00456     glColor4f(1,1,1,1);
00457     glLineWidth(1);
00458 }
00459 
00460 } // namespace collision
00461 
00462 } // namespace component
00463 
00464 } // namespace sofa

Generated on Sun Nov 23 02:34:46 2008 for Sofa by  doxygen 1.5.7.1