diff --git a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt
index cc503bf09d6..edc3176316a 100644
--- a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt
+++ b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt
@@ -6,6 +6,7 @@ set(SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR "src/sofa/component/coll
set(HEADER_FILES
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/config.h.in
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/init.h
+ ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/AbstractSubCollisionPipeline.h
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BVHNarrowPhase.h
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceBroadPhase.h
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceDetection.h
@@ -17,8 +18,10 @@ set(HEADER_FILES
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/EndPoint.h
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/IncrSAP.h
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/MirrorIntersector.h
+ ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/MultiCollisionPipeline.h
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/RayTraceDetection.h
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/RayTraceNarrowPhase.h
+ ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/SubCollisionPipeline.h
)
set(SOURCE_FILES
@@ -31,8 +34,10 @@ set(SOURCE_FILES
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/DirectSAP.cpp
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/DirectSAPNarrowPhase.cpp
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/IncrSAP.cpp
+ ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/MultiCollisionPipeline.cpp
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/RayTraceDetection.cpp
${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/RayTraceNarrowPhase.cpp
+ ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/SubCollisionPipeline.cpp
)
sofa_find_package(Sofa.Simulation.Core REQUIRED)
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.h
new file mode 100644
index 00000000000..ac9979213e9
--- /dev/null
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.h
@@ -0,0 +1,123 @@
+/******************************************************************************
+* SOFA, Simulation Open-Framework Architecture *
+* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: The SOFA Team and external contributors (see Authors.txt) *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#pragma once
+#include
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+
+namespace sofa::component::collision::detection::algorithm
+{
+
+class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipeline : public sofa::core::objectmodel::BaseObject
+{
+public:
+ SOFA_ABSTRACT_CLASS(AbstractSubCollisionPipeline, sofa::core::objectmodel::BaseObject);
+
+ virtual void computeCollisionReset() = 0;
+ virtual void computeCollisionDetection() = 0;
+ virtual void computeCollisionResponse() = 0;
+ virtual void doInit() = 0;
+ virtual void doBwdInit() {}
+ virtual void doHandleEvent(sofa::core::objectmodel::Event* e) = 0;
+
+ virtual void doDraw(const core::visual::VisualParams*) {}
+
+ AbstractSubCollisionPipeline()
+ : sofa::core::objectmodel::BaseObject()
+ , l_collisionModels(initLink("collisionModels", "List of collision models to consider in this pipeline"))
+ , l_intersectionMethod(initLink("intersectionMethod", "Intersection method to use in this pipeline"))
+ , l_contactManager(initLink("contactManager", "Contact manager to use in this pipeline"))
+ {
+
+ }
+
+ void init() override final
+ {
+ bool validity = true;
+
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid);
+
+ //Check given parameters
+ if (l_collisionModels.size() == 0)
+ {
+ msg_warning() << "At least one CollisionModel is required to compute collision detection.";
+ validity = false;
+ }
+
+ if (!l_intersectionMethod)
+ {
+ msg_warning() << "An Intersection detection component is required to compute collision detection.";
+ validity = false;
+ }
+
+ if (!l_contactManager)
+ {
+ msg_warning() << "A contact manager component is required to compute collision detection.";
+ validity = false;
+ }
+
+ if (validity)
+ {
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid);
+ }
+
+ doInit();
+ }
+
+ static std::set< std::string > getResponseList()
+ {
+ std::set< std::string > listResponse;
+ core::collision::Contact::Factory::iterator it;
+ for (it = core::collision::Contact::Factory::getInstance()->begin(); it != core::collision::Contact::Factory::getInstance()->end(); ++it)
+ {
+ listResponse.insert(it->first);
+ }
+ return listResponse;
+ }
+
+ void draw(const core::visual::VisualParams* vparams) override final
+ {
+ const auto stateLifeCycle = vparams->drawTool()->makeStateLifeCycle();
+
+ doDraw(vparams);
+ }
+
+ void handleEvent(sofa::core::objectmodel::Event* e) override final
+ {
+ doHandleEvent(e);
+ }
+
+ sofa::MultiLink < AbstractSubCollisionPipeline, sofa::core::CollisionModel, sofa::BaseLink::FLAG_DUPLICATE > l_collisionModels;
+ sofa::SingleLink< AbstractSubCollisionPipeline, sofa::core::collision::Intersection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_intersectionMethod;
+ sofa::SingleLink< AbstractSubCollisionPipeline, sofa::core::collision::ContactManager, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_contactManager;
+};
+
+} // namespace sofa::component::collision::detection::algorithm
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.cpp
index 4af2b8fb932..00d6e6ce740 100644
--- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.cpp
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.cpp
@@ -66,31 +66,44 @@ CollisionPipeline::CollisionPipeline()
, d_depth(initData(&d_depth, defaultDepthValue, "depth",
("Max depth of bounding trees. (default=" + std::to_string(defaultDepthValue) + ", min=?, max=?)").c_str()))
{
+
+ m_subCollisionPipeline = sofa::core::objectmodel::New();
+ m_subCollisionPipeline->d_depth.setParent(&this->d_depth);
+ m_subCollisionPipeline->l_broadPhaseDetection.set(this->broadPhaseDetection);
+ m_subCollisionPipeline->l_narrowPhaseDetection.set(this->narrowPhaseDetection);
+ m_multiCollisionPipeline = sofa::core::objectmodel::New();
+ m_multiCollisionPipeline->l_subCollisionPipelines.add(m_subCollisionPipeline.get());
+
+ this->addSlave(m_subCollisionPipeline);
+ this->addSlave(m_multiCollisionPipeline);
}
-#ifdef SOFA_DUMP_VISITOR_INFO
-typedef simulation::Visitor::ctime_t ctime_t;
-#endif
-
void CollisionPipeline::init()
{
Inherit1::init();
-
- if (broadPhaseDetection == nullptr)
- {
- msg_warning() << "A BroadPhase component is required to compute collision detection and was not found in the current scene";
- }
-
- if (narrowPhaseDetection == nullptr)
- {
- msg_warning() << "A NarrowPhase component is required to compute collision detection and was not found in the current scene";
- }
-
- if (contactManager == nullptr)
- {
- msg_warning() << "A ContactManager component is required to compute collision response and was not found in the current scene";
- }
-
+
+ msg_info() << "CollisionPipeline is now a wrapper to MultiCollisionPipeline with a single SubCollisionPipeline.";
+ msg_info() << "If you want more flexibility, use directly the components MultiCollisionPipeline and SubCollisionPipeline, with their respective Data.";
+
+ auto context = this->getContext();
+ // set the whole collision models list to the sub collision pipeline
+ sofa::type::vector collisionModels;
+ context->get>(&collisionModels, BaseContext::SearchRoot);
+
+ for(auto collisionModel : collisionModels)
+ {
+ m_subCollisionPipeline->l_collisionModels.add(collisionModel.get());
+ }
+
+ // set the other component to the sub collision pipeline (which is implcitely searched/set by PipelineImpl)
+ m_subCollisionPipeline->l_intersectionMethod.set(this->intersectionMethod);
+ m_subCollisionPipeline->l_broadPhaseDetection.set(this->broadPhaseDetection);
+ m_subCollisionPipeline->l_narrowPhaseDetection.set(this->narrowPhaseDetection);
+ m_subCollisionPipeline->l_contactManager.set(this->contactManager);
+
+ m_subCollisionPipeline->init();
+ m_multiCollisionPipeline->init();
+
/// Insure that all the value provided by the user are valid and report message if it is not.
checkDataValues() ;
}
@@ -107,220 +120,22 @@ void CollisionPipeline::checkDataValues()
void CollisionPipeline::doCollisionReset()
{
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "CollisionPipeline::doCollisionReset" ;
-
- // clear all contacts
- if (contactManager != nullptr)
- {
- const type::vector& contacts = contactManager->getContacts();
- for (const auto& contact : contacts)
- {
- if (contact != nullptr)
- {
- contact->removeResponse();
- }
- }
- }
-
- // clear all collision groups
- if (groupManager != nullptr)
- {
- core::objectmodel::BaseContext* scene = getContext();
- groupManager->clearGroups(scene);
- }
+ m_multiCollisionPipeline->doCollisionReset();
}
void CollisionPipeline::doCollisionDetection(const type::vector& collisionModels)
{
- SCOPED_TIMER_VARNAME(docollisiontimer, "doCollisionDetection");
-
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "doCollisionDetection, compute Bounding Trees" ;
-
- // First, we compute a bounding volume for the collision model (for example bounding sphere)
- // or we have loaded a collision model that knows its other model
-
- type::vector vectBoundingVolume;
- {
- SCOPED_TIMER_VARNAME(bboxtimer, "ComputeBoundingTree");
-
-#ifdef SOFA_DUMP_VISITOR_INFO
- simulation::Visitor::printNode("ComputeBoundingTree");
-#endif
- const bool continuous = intersectionMethod->useContinuous();
- const auto continuousIntersectionType = intersectionMethod->continuousIntersectionType();
- const SReal dt = getContext()->getDt();
-
- type::vector::const_iterator it;
- const type::vector::const_iterator itEnd = collisionModels.end();
- int nActive = 0;
-
- const int used_depth = (
- (broadPhaseDetection && broadPhaseDetection->needsDeepBoundingTree()) ||
- (narrowPhaseDetection && narrowPhaseDetection->needsDeepBoundingTree())
- ) ? d_depth.getValue() : 0;
-
- for (it = collisionModels.begin(); it != itEnd; ++it)
- {
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "doCollisionDetection, consider model" ;
-
- if (!(*it)->isActive()) continue;
-
- if (continuous)
- {
- const std::string msg = "Compute Continuous BoundingTree: " + (*it)->getName();
- ScopedAdvancedTimer continuousBoundingTreeTimer(msg.c_str());
- (*it)->computeContinuousBoundingTree(dt, continuousIntersectionType, used_depth);
- }
- else
- {
- std::string msg = "Compute BoundingTree: " + (*it)->getName();
- ScopedAdvancedTimer boundingTreeTimer(msg.c_str());
- (*it)->computeBoundingTree(used_depth);
- }
-
- vectBoundingVolume.push_back ((*it)->getFirst());
- ++nActive;
- }
-
-#ifdef SOFA_DUMP_VISITOR_INFO
- simulation::Visitor::printCloseNode("ComputeBoundingTree");
-#endif
-
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "doCollisionDetection, Computed "<getName();
-
-#ifdef SOFA_DUMP_VISITOR_INFO
- simulation::Visitor::printNode("BroadPhase");
-#endif
- {
- SCOPED_TIMER_VARNAME(broadphase, "BroadPhase");
- intersectionMethod->beginBroadPhase();
- broadPhaseDetection->beginBroadPhase();
- broadPhaseDetection->addCollisionModels(vectBoundingVolume); // detection is done there
- broadPhaseDetection->endBroadPhase();
- intersectionMethod->endBroadPhase();
- }
-#ifdef SOFA_DUMP_VISITOR_INFO
- simulation::Visitor::printCloseNode("BroadPhase");
-#endif
-
- // then we start the narrow phase
- if (narrowPhaseDetection == nullptr)
- {
- return; // can't go further
- }
-
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "doCollisionDetection, NarrowPhaseDetection "<getName();
-
-#ifdef SOFA_DUMP_VISITOR_INFO
- simulation::Visitor::printNode("NarrowPhase");
-#endif
- {
- SCOPED_TIMER_VARNAME(narrowphase, "NarrowPhase");
- intersectionMethod->beginNarrowPhase();
- narrowPhaseDetection->beginNarrowPhase();
- const type::vector >& vectCMPair = broadPhaseDetection->getCollisionModelPairs();
-
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "doCollisionDetection, "<< vectCMPair.size()<<" colliding model pairs" ;
-
- narrowPhaseDetection->addCollisionPairs(vectCMPair);
- narrowPhaseDetection->endNarrowPhase();
- intersectionMethod->endNarrowPhase();
- }
-#ifdef SOFA_DUMP_VISITOR_INFO
- simulation::Visitor::printCloseNode("NarrowPhase");
-#endif
-
+ m_multiCollisionPipeline->doCollisionDetection(collisionModels);
}
void CollisionPipeline::doCollisionResponse()
{
- core::objectmodel::BaseContext* scene = getContext();
- // then we start the creation of contacts
- if (narrowPhaseDetection == nullptr || contactManager == nullptr)
- {
- return; // can't go further
- }
-
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "Create Contacts " << contactManager->getName() ;
-
- {
- SCOPED_TIMER_VARNAME(createContactsTimer, "CreateContacts");
- contactManager->createContacts(narrowPhaseDetection->getDetectionOutputs());
- }
-
- // finally we start the creation of collisionGroup
-
- const type::vector& contacts = contactManager->getContacts();
-
- // First we remove all contacts with non-simulated objects and directly add them
- type::vector notStaticContacts;
-
- {
- SCOPED_TIMER_VARNAME(createStaticObjectsResponseTimer, "CreateStaticObjectsResponse");
- for (const auto& contact : contacts)
- {
- const auto collisionModels = contact->getCollisionModels();
- if (collisionModels.first != nullptr && !collisionModels.first->isSimulated())
- {
- contact->createResponse(collisionModels.second->getContext());
- }
- else if (collisionModels.second != nullptr && !collisionModels.second->isSimulated())
- {
- contact->createResponse(collisionModels.first->getContext());
- }
- else
- {
- notStaticContacts.push_back(contact);
- }
- }
- }
-
- if (groupManager == nullptr)
- {
- SCOPED_TIMER_VARNAME(createResponseTimer, "CreateMovingObjectsResponse");
-
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "Linking all contacts to Scene" ;
-
- for (const auto& contact : notStaticContacts)
- {
- contact->createResponse(scene);
- }
- }
- else
- {
- msg_info_when(d_doPrintInfoMessage.getValue())
- << "Create Groups "<getName();
-
- groupManager->createGroups(scene, notStaticContacts);
- }
+ m_multiCollisionPipeline->doCollisionResponse();
}
std::set< std::string > CollisionPipeline::getResponseList() const
{
- std::set< std::string > listResponse;
- core::collision::Contact::Factory::iterator it;
- for (it=core::collision::Contact::Factory::getInstance()->begin(); it!=core::collision::Contact::Factory::getInstance()->end(); ++it)
- {
- listResponse.insert(it->first);
- }
- return listResponse;
+ return m_multiCollisionPipeline->getResponseList();
}
} // namespace sofa::component::collision::detection::algorithm
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.h
index 8d7a2233213..f24f9a1828f 100644
--- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.h
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.h
@@ -23,6 +23,8 @@
#include
#include
+#include
+#include
namespace sofa::component::collision::detection::algorithm
{
@@ -35,6 +37,7 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CollisionPipeline : publi
Data d_doPrintInfoMessage;
Data d_doDebugDraw;
Data d_depth;
+
protected:
CollisionPipeline();
public:
@@ -52,6 +55,9 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CollisionPipeline : publi
void doCollisionResponse() override;
virtual void checkDataValues() ;
+
+ MultiCollisionPipeline::SPtr m_multiCollisionPipeline;
+ SubCollisionPipeline::SPtr m_subCollisionPipeline;
public:
static const int defaultDepthValue;
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp
new file mode 100644
index 00000000000..ef1a0238cb4
--- /dev/null
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp
@@ -0,0 +1,245 @@
+/******************************************************************************
+* SOFA, Simulation Open-Framework Architecture *
+* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: The SOFA Team and external contributors (see Authors.txt) *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+using sofa::helper::ScopedAdvancedTimer ;
+
+#include
+
+
+namespace sofa::component::collision::detection::algorithm
+{
+
+using namespace sofa;
+using namespace sofa::core;
+using namespace sofa::core::collision;
+
+void registerMultiCollisionPipeline(sofa::core::ObjectFactory* factory)
+{
+ factory->registerObjects(core::ObjectRegistrationData("Multiple collision pipelines in one.")
+ .add< MultiCollisionPipeline >());
+}
+
+MultiCollisionPipeline::MultiCollisionPipeline()
+ : d_parallelDetection(initData(&d_parallelDetection, false, "parallelDetection", "Parallelize collision detection."))
+ , d_parallelResponse(initData(&d_parallelResponse, false, "parallelResponse", "(DISABLED) Parallelize collision response."))
+ , l_subCollisionPipelines(initLink("subCollisionPipelines", "List of sub collision pipelines to handle."))
+{
+}
+
+void MultiCollisionPipeline::init()
+{
+ Inherit1::init();
+
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid);
+
+ if(l_subCollisionPipelines.size() == 0)
+ {
+ msg_warning() << "No SubCollisionPipeline defined in MultiCollisionPipeline. Nothing will be done." ;
+
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid);
+ return;
+ }
+
+ if(d_parallelDetection.getValue() || d_parallelResponse.getValue())
+ {
+ m_taskScheduler = sofa::simulation::MainTaskSchedulerFactory::createInRegistry();
+ assert(m_taskScheduler);
+
+ m_taskScheduler->init();
+ }
+
+ // UX: warn if there is any CollisionModel not handled by any SubCollisionPipeline
+ simulation::Node* root = dynamic_cast(getContext()->getRootContext());
+ std::vector sceneCollisionModels;
+ root->getTreeObjects(&sceneCollisionModels);
+
+ std::set pipelineCollisionModels;
+ for(auto* subPipeline : l_subCollisionPipelines)
+ {
+ if(!subPipeline)
+ {
+ msg_error() << "One of the subCollisionPipeline is incorrect (nullptr or invalid) ";
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid);
+ return;
+ }
+
+ for (auto cm : subPipeline->l_collisionModels)
+ {
+ pipelineCollisionModels.insert(cm);
+ }
+ }
+
+ for (const auto& cm : sceneCollisionModels)
+ {
+ if (pipelineCollisionModels.find(cm) == pipelineCollisionModels.end())
+ {
+ msg_warning() << "CollisionModel " << cm->getName() << " is not handled by any SubCollisionPipeline.";
+ }
+ }
+
+}
+
+void MultiCollisionPipeline::bwdInit()
+{
+ for(const auto& subPipeline : l_subCollisionPipelines)
+ {
+ subPipeline->doBwdInit();
+ }
+}
+
+void MultiCollisionPipeline::reset()
+{
+
+}
+
+void MultiCollisionPipeline::doCollisionReset()
+{
+ msg_info() << "MultiCollisionPipeline::doCollisionReset" ;
+
+ for(const auto& subPipeline : l_subCollisionPipelines)
+ {
+ subPipeline->computeCollisionReset();
+ }
+
+ // re-order pipelines by order of distance
+ m_subCollisionPipelines.clear();
+ for(auto* subPipeline : l_subCollisionPipelines)
+ {
+ const auto alarmDistance = subPipeline->l_intersectionMethod->getAlarmDistance();
+ auto subPipelineIt = m_subCollisionPipelines.begin();
+
+ if(subPipelineIt == m_subCollisionPipelines.end())
+ {
+ m_subCollisionPipelines.push_back(subPipeline);
+ }
+ else
+ {
+ while(subPipelineIt != m_subCollisionPipelines.end() && alarmDistance > (*subPipelineIt)->l_intersectionMethod->getAlarmDistance())
+ {
+ subPipelineIt++;
+ }
+ m_subCollisionPipelines.insert(subPipelineIt, subPipeline);
+ }
+ }
+}
+
+void MultiCollisionPipeline::doCollisionDetection(const type::vector& collisionModels)
+{
+ SOFA_UNUSED(collisionModels);
+
+ SCOPED_TIMER_VARNAME(docollisiontimer, "doCollisionDetection");
+
+ msg_info()
+ << "doCollisionDetection, compute Bounding Trees" ;
+
+ const sofa::simulation::ForEachExecutionPolicy execution = m_taskScheduler != nullptr && d_parallelDetection.getValue() ?
+ sofa::simulation::ForEachExecutionPolicy::PARALLEL :
+ sofa::simulation::ForEachExecutionPolicy::SEQUENTIAL;
+
+ auto computeCollisionDetection = [&](const auto& range)
+ {
+ for (auto it = range.start; it != range.end; ++it)
+ {
+ (*it)->computeCollisionDetection();
+ }
+ };
+
+ sofa::simulation::forEachRange(execution, *m_taskScheduler, m_subCollisionPipelines.begin(), m_subCollisionPipelines.end(), computeCollisionDetection);
+}
+
+void MultiCollisionPipeline::doCollisionResponse()
+{
+ // disable parallel execution, as there is a potential race condition on Node
+ // It arises when while cleaning inactive contact, BaryCcontactMapper will detach the node, which clears _descendency set
+ // if two contact responses do the same in the same time, it will do a race condition on this particular node.
+// const sofa::simulation::ForEachExecutionPolicy execution = m_taskScheduler != nullptr && d_parallelResponse.getValue() ?
+// sofa::simulation::ForEachExecutionPolicy::PARALLEL :
+// sofa::simulation::ForEachExecutionPolicy::SEQUENTIAL;
+ const sofa::simulation::ForEachExecutionPolicy execution = sofa::simulation::ForEachExecutionPolicy::SEQUENTIAL;
+
+ auto computeCollisionResponse = [&](const auto& range)
+ {
+ for (auto it = range.start; it != range.end; ++it)
+ {
+ (*it)->computeCollisionResponse();
+ }
+ };
+
+ sofa::simulation::forEachRange(execution, *m_taskScheduler, l_subCollisionPipelines.begin(), l_subCollisionPipelines.end(), computeCollisionResponse);
+}
+
+std::set< std::string > MultiCollisionPipeline::getResponseList() const
+{
+ return AbstractSubCollisionPipeline::getResponseList();
+}
+
+void MultiCollisionPipeline::computeCollisionReset()
+{
+ if(!this->isComponentStateValid())
+ return;
+
+ doCollisionReset();
+}
+
+void MultiCollisionPipeline::computeCollisionDetection()
+{
+ if(!this->isComponentStateValid())
+ return;
+
+ //useless
+ std::vector collisionModels;
+
+ doCollisionDetection(collisionModels);
+}
+
+void MultiCollisionPipeline::computeCollisionResponse()
+{
+ if(!this->isComponentStateValid())
+ return;
+
+ doCollisionResponse();
+}
+
+
+void MultiCollisionPipeline::draw(const core::visual::VisualParams* vparams)
+{
+ for (const auto& subPipeline : m_subCollisionPipelines)
+ {
+ subPipeline->draw(vparams);
+ }
+}
+
+} // namespace sofa::component::collision::detection::algorithm
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h
new file mode 100644
index 00000000000..dbe49a1a8f0
--- /dev/null
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+* SOFA, Simulation Open-Framework Architecture *
+* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: The SOFA Team and external contributors (see Authors.txt) *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#pragma once
+#include
+
+#include
+
+namespace sofa::simulation
+{
+class TaskScheduler;
+}
+
+namespace sofa::component::collision::detection::algorithm
+{
+
+class AbstractSubCollisionPipeline;
+
+class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : public sofa::core::collision::Pipeline
+{
+public:
+ SOFA_CLASS(MultiCollisionPipeline, sofa::core::collision::Pipeline);
+
+ sofa::Data d_depth;
+protected:
+ MultiCollisionPipeline();
+public:
+ void init() override;
+ void bwdInit() override;
+
+ /// get the set of response available with the current collision pipeline
+ std::set< std::string > getResponseList() const override;
+protected:
+ // -- Pipeline interface
+ /// Remove collision response from last step
+ void doCollisionReset() override;
+ /// Detect new collisions. Note that this step must not modify the simulation graph
+ void doCollisionDetection(const sofa::type::vector& collisionModels) override;
+ /// Add collision response in the simulation graph
+ void doCollisionResponse() override;
+
+ void reset() override;
+
+ void draw(const core::visual::VisualParams* vparams) override;
+
+ /// Remove collision response from last step
+ virtual void computeCollisionReset() override final;
+ /// Detect new collisions. Note that this step must not modify the simulation graph
+ virtual void computeCollisionDetection() override final;
+ /// Add collision response in the simulation graph
+ virtual void computeCollisionResponse() override final;
+
+ sofa::simulation::TaskScheduler* m_taskScheduler{nullptr};
+
+ std::vector m_subCollisionPipelines;
+
+public:
+ sofa::Data d_parallelDetection;
+ sofa::Data d_parallelResponse;
+ sofa::MultiLink < MultiCollisionPipeline, AbstractSubCollisionPipeline, sofa::BaseLink::FLAG_DUPLICATE > l_subCollisionPipelines;
+
+
+ friend class CollisionPipeline; // to be able to call do*()
+};
+
+} // namespace sofa::component::collision::detection::algorithm
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.cpp
new file mode 100644
index 00000000000..1cdbb8cff5f
--- /dev/null
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.cpp
@@ -0,0 +1,244 @@
+/******************************************************************************
+* SOFA, Simulation Open-Framework Architecture *
+* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: The SOFA Team and external contributors (see Authors.txt) *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#include
+
+#include
+
+#include
+
+#include
+
+#include
+using sofa::helper::ScopedAdvancedTimer ;
+
+#include
+
+
+namespace sofa::component::collision::detection::algorithm
+{
+
+using namespace sofa;
+using namespace sofa::core;
+using namespace sofa::core::collision;
+
+void registerSubCollisionPipeline(sofa::core::ObjectFactory* factory)
+{
+ factory->registerObjects(core::ObjectRegistrationData("Collision pipeline to be used with MultiCollisionPipeline.")
+ .add< SubCollisionPipeline >());
+}
+
+SubCollisionPipeline::SubCollisionPipeline()
+ : Inherited()
+ , d_depth(initData(&d_depth, s_defaultDepthValue, "depth", +("Max depth of bounding trees. (default=" + std::to_string(s_defaultDepthValue) + ", min=?, max=?)").c_str()))
+ , l_broadPhaseDetection(initLink("broadPhaseDetection", "Broad phase detection to use in this pipeline"))
+ , l_narrowPhaseDetection(initLink("narrowPhaseDetection", "Narrow phase detection to use in this pipeline"))
+{
+}
+
+void SubCollisionPipeline::doInit()
+{
+ bool validity = true;
+
+ if (!l_broadPhaseDetection)
+ {
+ msg_warning() << "A BroadPhase component is required to compute collision detection.";
+ validity = false;
+ }
+ if (!l_narrowPhaseDetection)
+ {
+ msg_warning() << "A NarrowPhase component is required to compute collision detection.";
+ validity = false;
+ }
+
+ if (!validity)
+ {
+ this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid);
+ }
+
+}
+
+void SubCollisionPipeline::computeCollisionReset()
+{
+ if (!this->isComponentStateValid())
+ return;
+
+ msg_info() << "SubCollisionPipeline::doCollisionReset";
+
+ l_broadPhaseDetection->setIntersectionMethod(l_intersectionMethod.get());
+ l_narrowPhaseDetection->setIntersectionMethod(l_intersectionMethod.get());
+ l_contactManager->setIntersectionMethod(l_intersectionMethod.get());
+
+ // clear all contacts
+ const type::vector& contacts = l_contactManager->getContacts();
+ for (const auto& contact : contacts)
+ {
+ if (contact != nullptr)
+ {
+ contact->removeResponse();
+ }
+ }
+}
+
+void SubCollisionPipeline::computeCollisionDetection()
+{
+ SCOPED_TIMER_VARNAME(docollisiontimer, "doCollisionDetection");
+
+ if (!this->isComponentStateValid())
+ return;
+
+ msg_info()
+ << "doCollisionDetection, compute Bounding Trees" ;
+
+ // First, we compute a bounding volume for the collision model (for example bounding sphere)
+ // or we have loaded a collision model that knows its other model
+
+
+
+ type::vector vectBoundingVolume;
+ {
+ SCOPED_TIMER_VARNAME(bboxtimer, "ComputeBoundingTree");
+
+ const bool continuous = l_intersectionMethod->useContinuous();
+ const auto continuousIntersectionType = l_intersectionMethod->continuousIntersectionType();
+ const SReal dt = getContext()->getDt();
+
+ int nActive = 0;
+
+ const int used_depth = (
+ (l_broadPhaseDetection->needsDeepBoundingTree()) ||
+ (l_narrowPhaseDetection->needsDeepBoundingTree())
+ ) ? d_depth.getValue() : 0;
+
+ for (auto it = l_collisionModels.begin(); it != l_collisionModels.end(); ++it)
+ {
+ msg_info()
+ << "doCollisionDetection, consider model" ;
+
+ if (!(*it)->isActive()) continue;
+
+ if (continuous)
+ {
+ const std::string msg = "Compute Continuous BoundingTree: " + (*it)->getName();
+ ScopedAdvancedTimer continuousBoundingTreeTimer(msg.c_str());
+ (*it)->computeContinuousBoundingTree(dt, continuousIntersectionType, used_depth);
+ }
+ else
+ {
+ std::string msg = "Compute BoundingTree: " + (*it)->getName();
+ ScopedAdvancedTimer boundingTreeTimer(msg.c_str());
+ (*it)->computeBoundingTree(used_depth);
+ }
+
+ vectBoundingVolume.push_back ((*it)->getFirst());
+ ++nActive;
+ }
+
+
+ msg_info()
+ << "doCollisionDetection, Computed "<getName();
+
+ {
+ SCOPED_TIMER_VARNAME(broadphase, "BroadPhase");
+ l_intersectionMethod->beginBroadPhase();
+ l_broadPhaseDetection->beginBroadPhase();
+ l_broadPhaseDetection->addCollisionModels(vectBoundingVolume); // detection is done there
+ l_broadPhaseDetection->endBroadPhase();
+ l_intersectionMethod->endBroadPhase();
+ }
+
+ msg_info()
+ << "doCollisionDetection, NarrowPhaseDetection "<< l_narrowPhaseDetection->getName();
+
+ {
+ SCOPED_TIMER_VARNAME(narrowphase, "NarrowPhase");
+ l_intersectionMethod->beginNarrowPhase();
+ l_narrowPhaseDetection->beginNarrowPhase();
+ const type::vector >& vectCMPair = l_broadPhaseDetection->getCollisionModelPairs();
+
+ msg_info()
+ << "doCollisionDetection, "<< vectCMPair.size()<<" colliding model pairs" ;
+
+ l_narrowPhaseDetection->addCollisionPairs(vectCMPair);
+ l_narrowPhaseDetection->endNarrowPhase();
+ l_intersectionMethod->endNarrowPhase();
+ }
+
+}
+
+void SubCollisionPipeline::computeCollisionResponse()
+{
+ if (!this->isComponentStateValid())
+ return;
+
+ core::objectmodel::BaseContext* scene = getContext();
+
+ msg_info()
+ << "Create Contacts " << l_contactManager->getName() ;
+
+ {
+ SCOPED_TIMER_VARNAME(createContactsTimer, "CreateContacts");
+ l_contactManager->createContacts(l_narrowPhaseDetection->getDetectionOutputs());
+ }
+
+ // finally we start the creation of collisionGroup
+
+ const type::vector& contacts = l_contactManager->getContacts();
+
+ // First we remove all contacts with non-simulated objects and directly add them
+ type::vector notStaticContacts;
+
+ {
+ SCOPED_TIMER_VARNAME(createStaticObjectsResponseTimer, "CreateStaticObjectsResponse");
+ for (const auto& contact : contacts)
+ {
+ const auto collisionModels = contact->getCollisionModels();
+ if (collisionModels.first != nullptr && !collisionModels.first->isSimulated())
+ {
+ contact->createResponse(collisionModels.second->getContext());
+ }
+ else if (collisionModels.second != nullptr && !collisionModels.second->isSimulated())
+ {
+ contact->createResponse(collisionModels.first->getContext());
+ }
+ else
+ {
+ notStaticContacts.push_back(contact);
+ }
+ }
+ }
+
+ SCOPED_TIMER_VARNAME(createResponseTimer, "CreateMovingObjectsResponse");
+
+ msg_info()
+ << "Linking all contacts to Scene" ;
+
+ for (const auto& contact : notStaticContacts)
+ {
+ contact->createResponse(scene);
+ }
+}
+
+} // namespace sofa::component::collision::detection::algorithm
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.h
new file mode 100644
index 00000000000..d4f27dd86a9
--- /dev/null
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+* SOFA, Simulation Open-Framework Architecture *
+* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: The SOFA Team and external contributors (see Authors.txt) *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#pragma once
+#include
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+namespace sofa::component::collision::detection::algorithm
+{
+
+class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API SubCollisionPipeline : public AbstractSubCollisionPipeline
+{
+public:
+ using Inherited = AbstractSubCollisionPipeline;
+ SOFA_CLASS(SubCollisionPipeline, AbstractSubCollisionPipeline);
+protected:
+ SubCollisionPipeline();
+public:
+ virtual ~SubCollisionPipeline() override = default;
+ void doInit() override;
+ void doHandleEvent(sofa::core::objectmodel::Event*) override {}
+
+ void computeCollisionReset() override;
+ void computeCollisionDetection() override;
+ void computeCollisionResponse() override;
+
+ sofa::Data d_depth;
+ sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::BroadPhaseDetection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_broadPhaseDetection;
+ sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::NarrowPhaseDetection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_narrowPhaseDetection;
+
+ static inline constexpr unsigned int s_defaultDepthValue = 6;
+};
+
+} // namespace sofa::component::collision::detection::algorithm
diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/init.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/init.cpp
index d6e879397e0..2541b470943 100644
--- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/init.cpp
+++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/init.cpp
@@ -29,6 +29,8 @@ namespace sofa::component::collision::detection::algorithm
extern void registerBruteForceBroadPhase(sofa::core::ObjectFactory* factory);
extern void registerBruteForceDetection(sofa::core::ObjectFactory* factory);
extern void registerBVHNarrowPhase(sofa::core::ObjectFactory* factory);
+extern void registerMultiCollisionPipeline(sofa::core::ObjectFactory* factory);
+extern void registerSubCollisionPipeline(sofa::core::ObjectFactory* factory);
extern void registerCollisionPipeline(sofa::core::ObjectFactory* factory);
extern void registerDirectSAP(sofa::core::ObjectFactory* factory);
extern void registerDirectSAPNarrowPhase(sofa::core::ObjectFactory* factory);
@@ -64,6 +66,8 @@ void registerObjects(sofa::core::ObjectFactory* factory)
registerBruteForceBroadPhase(factory);
registerBruteForceDetection(factory);
registerBVHNarrowPhase(factory);
+ registerMultiCollisionPipeline(factory);
+ registerSubCollisionPipeline(factory);
registerCollisionPipeline(factory);
registerDirectSAP(factory);
registerDirectSAPNarrowPhase(factory);
diff --git a/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp b/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp
index 3078fc26c56..f9dc70e5871 100644
--- a/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp
+++ b/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp
@@ -72,12 +72,14 @@ class TestCollisionPipeline : public BaseSimulationTest {
void checkCollisionPipelineWithMissingBroadPhase();
void checkCollisionPipelineWithMissingNarrowPhase();
void checkCollisionPipelineWithMissingContactManager();
+ void checkCollisionPipelineWithMissingCollisionModel();
int checkCollisionPipelineWithMonkeyValueForDepth(int value);
void doSetUp() override
{
this->loadPlugins({
Sofa.Component.StateContainer,
+ Sofa.Component.Collision.Geometry,
Sofa.Component.Collision.Detection.Algorithm,
Sofa.Component.Collision.Detection.Intersection,
Sofa.Component.Collision.Response.Contact
@@ -104,6 +106,10 @@ void TestCollisionPipeline::checkCollisionPipelineWithNoAttributes()
" \n"
" \n"
" \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
" \n" ;
root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str());
@@ -127,6 +133,10 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingIntersection()
" \n"
" \n"
" \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
" \n" ;
root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str());
@@ -149,6 +159,10 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingBroadPhase()
" \n"
" \n"
" \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
" \n" ;
root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str());
@@ -170,8 +184,12 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingNarrowPhase()
" \n"
" \n"
" \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
" \n" ;
-
+
root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str());
ASSERT_NE(root.get(), nullptr) ;
root->init(sofa::core::execparams::defaultInstance()) ;
@@ -191,6 +209,34 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingContactManager()
" \n"
" \n"
" \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n" ;
+
+ root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str());
+ ASSERT_NE(root.get(), nullptr) ;
+ root->init(sofa::core::execparams::defaultInstance()) ;
+
+ BaseObject* clp = root->getObject("pipeline") ;
+ ASSERT_NE(clp, nullptr) ;
+
+}
+
+void TestCollisionPipeline::checkCollisionPipelineWithMissingCollisionModel()
+{
+ EXPECT_MSG_EMIT(Warning) ;
+ EXPECT_MSG_NOEMIT(Error) ;
+
+ std::stringstream scene ;
+ scene << " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
" \n" ;
root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str());
@@ -212,6 +258,10 @@ int TestCollisionPipeline::checkCollisionPipelineWithMonkeyValueForDepth(int dva
" \n"
" \n"
" \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
" \n" ;
root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str());
@@ -252,6 +302,12 @@ TEST_F(TestCollisionPipeline, checkCollisionPipelineWithMissingContactManager)
this->checkCollisionPipelineWithMissingContactManager();
}
+TEST_F(TestCollisionPipeline, checkCollisionPipelineWithMissingCollisionModel)
+{
+ this->checkCollisionPipelineWithMissingCollisionModel();
+}
+
+
TEST_F(TestCollisionPipeline, checkCollisionPipelineWithMonkeyValueForDepth_OpenIssue)
{
const std::vector> testvalues = {
diff --git a/applications/projects/SceneChecking/CMakeLists.txt b/applications/projects/SceneChecking/CMakeLists.txt
index e0832b0ad65..f4ce6b70cab 100644
--- a/applications/projects/SceneChecking/CMakeLists.txt
+++ b/applications/projects/SceneChecking/CMakeLists.txt
@@ -12,6 +12,7 @@ set(HEADER_FILES
${SCENECHECK_SRC_DIR}/config.h.in
${SCENECHECK_SRC_DIR}/init.h
${SCENECHECK_SRC_DIR}/SceneCheckAPIChange.h
+ ${SCENECHECK_SRC_DIR}/SceneCheckCollisionPipelineAndModels.h
${SCENECHECK_SRC_DIR}/SceneCheckCollisionResponse.h
${SCENECHECK_SRC_DIR}/SceneCheckDeprecatedComponents.h
${SCENECHECK_SRC_DIR}/SceneCheckDuplicatedName.h
@@ -26,6 +27,7 @@ set(HEADER_FILES
set(SOURCE_FILES
${SCENECHECK_SRC_DIR}/init.cpp
${SCENECHECK_SRC_DIR}/SceneCheckAPIChange.cpp
+ ${SCENECHECK_SRC_DIR}/SceneCheckCollisionPipelineAndModels.cpp
${SCENECHECK_SRC_DIR}/SceneCheckCollisionResponse.cpp
${SCENECHECK_SRC_DIR}/SceneCheckDeprecatedComponents.cpp
${SCENECHECK_SRC_DIR}/SceneCheckDuplicatedName.cpp
diff --git a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.cpp b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.cpp
new file mode 100644
index 00000000000..80019e7099b
--- /dev/null
+++ b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.cpp
@@ -0,0 +1,104 @@
+/******************************************************************************
+* SOFA, Simulation Open-Framework Architecture *
+* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: The SOFA Team and external contributors (see Authors.txt) *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#include "SceneCheckCollisionPipelineAndModels.h"
+
+#include
+#include
+#include
+#include
+
+namespace sofa::_scenechecking_
+{
+
+const bool SceneCheckCollisionPipelineAndModelsRegistered = sofa::simulation::SceneCheckMainRegistry::addToRegistry(SceneCheckCollisionPipelineAndModels::newSPtr());
+
+using sofa::simulation::Node;
+
+const std::string SceneCheckCollisionPipelineAndModels::getName()
+{
+ return "SceneCheckCollisionPipelineAndModels";
+}
+
+const std::string SceneCheckCollisionPipelineAndModels::getDesc()
+{
+ return "Ensure the consistency of the existence of a collision pipeline, and collision models in the scene.";
+}
+
+void SceneCheckCollisionPipelineAndModels::doInit(Node* node)
+{
+ SOFA_UNUSED(node);
+}
+
+void SceneCheckCollisionPipelineAndModels::doCheckOn(Node* node)
+{
+ const sofa::core::objectmodel::BaseContext* root = node->getContext()->getRootContext();
+
+ if(!root)
+ {
+ return;
+ }
+
+ sofa::core::collision::Pipeline::SPtr anyPipeline{};
+ root->get(anyPipeline, sofa::core::objectmodel::BaseContext::SearchDirection::SearchDown);
+ sofa::core::CollisionModel::SPtr anyCollisionModel{};
+ root->get(anyCollisionModel, sofa::core::objectmodel::BaseContext::SearchDirection::SearchDown);
+
+ if(anyPipeline)
+ {
+ if(anyCollisionModel)
+ {
+ // there is a collision pipeline and (at least one) collision model(s), carry on.
+ }
+ else
+ {
+ // there is a collision pipeline but no collision model.
+ // Either the collision pipeline is superfluous;
+ // or the collision model(s) has been forgotten.
+ m_message = "There is no collision model in this scene, but there is a collision pipeline. Either add one collision model or remove the collision pipeline.";
+ }
+ }
+ else
+ {
+ if(anyCollisionModel)
+ {
+ // At least one collision model has been detected but without any pipeline.
+ // Either the collision pipeline has been forgotten;
+ // or the collision model(s) is useless.
+ m_message = "At least one collision model has been found, but there is no collision pipeline. You may add a collision pipeline (or remove the collision model if it is not used).";
+ }
+ else
+ {
+ // there is no collision pipeline and no collision model, the scene certainly does not involve any collision feature.
+ }
+ }
+}
+
+void SceneCheckCollisionPipelineAndModels::doPrintSummary()
+{
+ if(!m_message.empty())
+ {
+ msg_warning(this->getName()) << m_message;
+ }
+}
+
+
+} // namespace sofa::_scenechecking_
diff --git a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.h b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.h
new file mode 100644
index 00000000000..0ce57d79d77
--- /dev/null
+++ b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+* SOFA, Simulation Open-Framework Architecture *
+* (c) 2006 INRIA, USTL, UJF, CNRS, MGH *
+* *
+* This program is free software; you can redistribute it and/or modify it *
+* under the terms of the GNU Lesser General Public License as published by *
+* the Free Software Foundation; either version 2.1 of the License, or (at *
+* your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, but WITHOUT *
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
+* for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this program. If not, see . *
+*******************************************************************************
+* Authors: The SOFA Team and external contributors (see Authors.txt) *
+* *
+* Contact information: contact@sofa-framework.org *
+******************************************************************************/
+#pragma once
+
+#include
+#include
+
+#include