From 17cceeaafbaa1131253c5e5f430614553117ba02 Mon Sep 17 00:00:00 2001 From: Frederick ROY Date: Tue, 9 Dec 2025 11:10:21 +0100 Subject: [PATCH 01/28] initial commit to bring back multi-staged collision pipeline --- .../Detection/Algorithm/CMakeLists.txt | 5 + .../algorithm/AbstractSubCollisionPipeline.h | 123 +++++++ .../algorithm/MultiCollisionPipeline.cpp | 314 ++++++++++++++++++ .../algorithm/MultiCollisionPipeline.h | 81 +++++ .../algorithm/SubCollisionPipeline.cpp | 243 ++++++++++++++ .../algorithm/SubCollisionPipeline.h | 60 ++++ 6 files changed, 826 insertions(+) create mode 100644 Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.h create mode 100644 Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp create mode 100644 Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h create mode 100644 Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.cpp create mode 100644 Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.h 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..c6d87fa5598 --- /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(); + } + + std::set< std::string > 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; + } + + 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/MultiCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp new file mode 100644 index 00000000000..e4c9de522b4 --- /dev/null +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp @@ -0,0 +1,314 @@ +/****************************************************************************** +* 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); + } + } +// +// for(const auto& subPipeline : m_subCollisionPipelines) +// { +// std::cout << subPipeline->l_intersectionMethod->getAlarmDistance() << " "; +// } +// std::cout << std::endl; + +} + +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() +{ + // before doing collision response, filter identical contacts + std::map mapIdDistances; + std::map mapToRemove; + + +// for(auto& subCollisionPipeline : m_subCollisionPipelines) +// { +// const auto& subDetectionMap = subCollisionPipeline->l_narrowPhaseDetection->getDetectionOutputs(); +// for(const auto& [pairCM, outputsT] : subDetectionMap) +// { +// const auto& outputs = outputsT->getDetectionOutput(); +// +// for(const auto& output: outputs) +// { +// const auto currentAlarmDistance = subCollisionPipeline->l_intersectionMethod->getAlarmDistance(); +// +// if(mapIdDistances.find(output.id) == mapIdDistances.end()) +// { +// mapIdDistances[output.id] = subCollisionPipeline; +// } +// else +// { +// auto* previousSubCollisionPipeline = mapIdDistances[output.id]; +// const auto previousAlarmDistance = previousSubCollisionPipeline->l_intersectionMethod->getAlarmDistance(); +// +// if(currentAlarmDistance < previousAlarmDistance) +// { +// std::cout << "Find duplicate with a lower alarm" << std::endl; +// // remove the contact in the other subCollisionPipeline +// mapToRemove[output.id] = previousSubCollisionPipeline; +// } +// else +// { +// std::cout << "Find duplicate with a upper alarm" << std::endl; +// // remove the contact in the current subCollisionPipeline +// mapToRemove[output.id] = subCollisionPipeline; +// } +// } +// } +// } +// } + +// // remove effectively the contacts +// for(auto& [id, subCollisionPipeline] : mapToRemove) +// { +// auto& subDetectionMap = subCollisionPipeline->l_narrowPhaseDetection->getDetectionOutputs(); +// for(const auto& [pairCM, outputsT] : subDetectionMap) +// { +// outputsT->removeDetectionOutputFromID(id); +// std::cout << "Removed " << id << " from " << subCollisionPipeline->getName() << std::endl; +// } +// } + + // 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 +{ + std::set< std::string > listResponse; + core::collision::Contact::Factory::iterator it; + + for (const auto& subPipeline : m_subCollisionPipelines) + { + std::set< std::string > subListResponse = subPipeline->getResponseList(); + listResponse.insert(subListResponse.begin(), subListResponse.end()); + } + + return listResponse; +} + +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..7bcd485d08c --- /dev/null +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h @@ -0,0 +1,81 @@ +/****************************************************************************** +* 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; + /// Detect new collisions. Note that this step must not modify the simulation graph + virtual void computeCollisionDetection() override; + /// Add collision response in the simulation graph + virtual void computeCollisionResponse() override; + + 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; +}; + +} // 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..2bf62953511 --- /dev/null +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/SubCollisionPipeline.cpp @@ -0,0 +1,243 @@ +/****************************************************************************** +* 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 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, 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 From e2bb40507d1f334ef98f3340184a8c5d5a42f348 Mon Sep 17 00:00:00 2001 From: Frederick ROY Date: Tue, 9 Dec 2025 11:56:37 +0100 Subject: [PATCH 02/28] clean --- .../algorithm/MultiCollisionPipeline.cpp | 60 ------------------- 1 file changed, 60 deletions(-) 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 index e4c9de522b4..1b6fe76ea80 100644 --- 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 @@ -154,13 +154,6 @@ void MultiCollisionPipeline::doCollisionReset() m_subCollisionPipelines.insert(subPipelineIt, subPipeline); } } -// -// for(const auto& subPipeline : m_subCollisionPipelines) -// { -// std::cout << subPipeline->l_intersectionMethod->getAlarmDistance() << " "; -// } -// std::cout << std::endl; - } void MultiCollisionPipeline::doCollisionDetection(const type::vector& collisionModels) @@ -189,59 +182,6 @@ void MultiCollisionPipeline::doCollisionDetection(const type::vector mapIdDistances; - std::map mapToRemove; - - -// for(auto& subCollisionPipeline : m_subCollisionPipelines) -// { -// const auto& subDetectionMap = subCollisionPipeline->l_narrowPhaseDetection->getDetectionOutputs(); -// for(const auto& [pairCM, outputsT] : subDetectionMap) -// { -// const auto& outputs = outputsT->getDetectionOutput(); -// -// for(const auto& output: outputs) -// { -// const auto currentAlarmDistance = subCollisionPipeline->l_intersectionMethod->getAlarmDistance(); -// -// if(mapIdDistances.find(output.id) == mapIdDistances.end()) -// { -// mapIdDistances[output.id] = subCollisionPipeline; -// } -// else -// { -// auto* previousSubCollisionPipeline = mapIdDistances[output.id]; -// const auto previousAlarmDistance = previousSubCollisionPipeline->l_intersectionMethod->getAlarmDistance(); -// -// if(currentAlarmDistance < previousAlarmDistance) -// { -// std::cout << "Find duplicate with a lower alarm" << std::endl; -// // remove the contact in the other subCollisionPipeline -// mapToRemove[output.id] = previousSubCollisionPipeline; -// } -// else -// { -// std::cout << "Find duplicate with a upper alarm" << std::endl; -// // remove the contact in the current subCollisionPipeline -// mapToRemove[output.id] = subCollisionPipeline; -// } -// } -// } -// } -// } - -// // remove effectively the contacts -// for(auto& [id, subCollisionPipeline] : mapToRemove) -// { -// auto& subDetectionMap = subCollisionPipeline->l_narrowPhaseDetection->getDetectionOutputs(); -// for(const auto& [pairCM, outputsT] : subDetectionMap) -// { -// outputsT->removeDetectionOutputFromID(id); -// std::cout << "Removed " << id << " from " << subCollisionPipeline->getName() << std::endl; -// } -// } - // 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. From cfa5f555ee67ff92a38a045871bf6ca3f7ec6d6c Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 22 Dec 2025 15:08:31 +0900 Subject: [PATCH 03/28] wip --- .../detection/algorithm/CollisionPipeline.cpp | 238 ++---------------- .../detection/algorithm/CollisionPipeline.h | 6 + .../algorithm/MultiCollisionPipeline.h | 3 + .../algorithm/SubCollisionPipeline.cpp | 5 +- 4 files changed, 29 insertions(+), 223 deletions(-) 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..2ff7584609d 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 @@ -68,28 +68,22 @@ CollisionPipeline::CollisionPipeline() { } -#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."; + + 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); /// Insure that all the value provided by the user are valid and report message if it is not. checkDataValues() ; @@ -107,220 +101,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.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h index 7bcd485d08c..24129e4daf4 100644 --- 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 @@ -76,6 +76,9 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : 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 index 2bf62953511..1cdbb8cff5f 100644 --- 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 @@ -118,6 +118,7 @@ void SubCollisionPipeline::computeCollisionDetection() 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; @@ -138,7 +139,7 @@ void SubCollisionPipeline::computeCollisionDetection() { const std::string msg = "Compute Continuous BoundingTree: " + (*it)->getName(); ScopedAdvancedTimer continuousBoundingTreeTimer(msg.c_str()); - (*it)->computeContinuousBoundingTree(dt, used_depth); + (*it)->computeContinuousBoundingTree(dt, continuousIntersectionType, used_depth); } else { @@ -153,7 +154,7 @@ void SubCollisionPipeline::computeCollisionDetection() msg_info() - << "doCollisionDetection, Computed "< Date: Mon, 15 Dec 2025 09:23:16 +0900 Subject: [PATCH 04/28] set links --- .../detection/algorithm/CollisionPipeline.cpp | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) 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 2ff7584609d..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,6 +66,16 @@ 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); } void CollisionPipeline::init() @@ -74,17 +84,26 @@ void CollisionPipeline::init() 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()); + } - m_subCollisionPipeline = sofa::core::objectmodel::New(); - m_subCollisionPipeline->d_depth.setParent(&this->d_depth); + // 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_multiCollisionPipeline = sofa::core::objectmodel::New(); - m_multiCollisionPipeline->l_subCollisionPipelines.add(m_subCollisionPipeline.get()); + m_subCollisionPipeline->l_contactManager.set(this->contactManager); + + m_subCollisionPipeline->init(); + m_multiCollisionPipeline->init(); - this->addSlave(m_subCollisionPipeline); - this->addSlave(m_multiCollisionPipeline); - /// Insure that all the value provided by the user are valid and report message if it is not. checkDataValues() ; } From 85002b225644dacee867c7ab6f2347c603ca681f Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 15 Dec 2025 09:37:22 +0900 Subject: [PATCH 05/28] set getresponselist to a static function --- .../algorithm/AbstractSubCollisionPipeline.h | 2 +- .../detection/algorithm/MultiCollisionPipeline.cpp | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) 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 index c6d87fa5598..ac9979213e9 100644 --- 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 @@ -92,7 +92,7 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipel doInit(); } - std::set< std::string > getResponseList() const + static std::set< std::string > getResponseList() { std::set< std::string > listResponse; core::collision::Contact::Factory::iterator it; 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 index 1b6fe76ea80..ef1a0238cb4 100644 --- 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 @@ -203,16 +203,7 @@ void MultiCollisionPipeline::doCollisionResponse() std::set< std::string > MultiCollisionPipeline::getResponseList() const { - std::set< std::string > listResponse; - core::collision::Contact::Factory::iterator it; - - for (const auto& subPipeline : m_subCollisionPipelines) - { - std::set< std::string > subListResponse = subPipeline->getResponseList(); - listResponse.insert(subListResponse.begin(), subListResponse.end()); - } - - return listResponse; + return AbstractSubCollisionPipeline::getResponseList(); } void MultiCollisionPipeline::computeCollisionReset() From e48c24788d25b8ed7c1c19d9d2a13e1a4faef6e3 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 23 Dec 2025 14:13:33 +0900 Subject: [PATCH 06/28] add one unit test on warning with no model and update other tests --- .../tests/CollisionPipeline_test.cpp | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp b/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp index ae151f8efd5..9bb60fc3a8d 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 = { From 357ec2b3938b51827c993097df198e08bf9836aa Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 24 Dec 2025 15:40:28 +0900 Subject: [PATCH 07/28] add into registry --- .../src/sofa/component/collision/detection/algorithm/init.cpp | 4 ++++ 1 file changed, 4 insertions(+) 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); From 81753014c853616635c0836c1166d64c0284e04f Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 7 Jan 2026 18:14:59 +0900 Subject: [PATCH 08/28] Update Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h Co-authored-by: Paul Baksic <30337881+bakpaul@users.noreply.github.com> --- .../collision/detection/algorithm/MultiCollisionPipeline.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 index 24129e4daf4..dbe49a1a8f0 100644 --- 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 @@ -62,11 +62,11 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : void draw(const core::visual::VisualParams* vparams) override; /// Remove collision response from last step - virtual void computeCollisionReset() override; + virtual void computeCollisionReset() override final; /// Detect new collisions. Note that this step must not modify the simulation graph - virtual void computeCollisionDetection() override; + virtual void computeCollisionDetection() override final; /// Add collision response in the simulation graph - virtual void computeCollisionResponse() override; + virtual void computeCollisionResponse() override final; sofa::simulation::TaskScheduler* m_taskScheduler{nullptr}; From 12b0bb505ebf01cf549868903251142c4bc08aaf Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 10:50:39 +0900 Subject: [PATCH 09/28] use rangeloop with structured bindings in getResponseList --- .../detection/algorithm/AbstractSubCollisionPipeline.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 index ac9979213e9..907de73de31 100644 --- 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 @@ -95,10 +95,9 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipel 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) + for (const auto& [key, creatorPtr] : *core::collision::Contact::Factory::getInstance()) { - listResponse.insert(it->first); + listResponse.insert(key); } return listResponse; } From fc71ec8082bedfbeb6758108024d269b93cfc058 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 10:55:43 +0900 Subject: [PATCH 10/28] set do*() and ctor protected in AbstractSubCollisionPipeline --- .../algorithm/AbstractSubCollisionPipeline.h | 20 ++++++++++--------- .../algorithm/MultiCollisionPipeline.cpp | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) 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 index 907de73de31..782f8891512 100644 --- 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 @@ -41,15 +41,7 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipel 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*) {} - +protected: AbstractSubCollisionPipeline() : sofa::core::objectmodel::BaseObject() , l_collisionModels(initLink("collisionModels", "List of collision models to consider in this pipeline")) @@ -59,6 +51,16 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipel } + virtual void doInit() = 0; + virtual void doBwdInit() {} + virtual void doHandleEvent(sofa::core::objectmodel::Event* e) = 0; + virtual void doDraw(const core::visual::VisualParams*) {} + +public: + virtual void computeCollisionReset() = 0; + virtual void computeCollisionDetection() = 0; + virtual void computeCollisionResponse() = 0; + void init() override final { bool validity = true; 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 index ef1a0238cb4..e25671d5678 100644 --- 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 @@ -116,7 +116,7 @@ void MultiCollisionPipeline::bwdInit() { for(const auto& subPipeline : l_subCollisionPipelines) { - subPipeline->doBwdInit(); + subPipeline->bwdInit(); } } From 9375d49ca008be118cf4a33bd136b861661c1a23 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 11:04:48 +0900 Subject: [PATCH 11/28] put implementations into a cpp for AbstractSubCollisionPipeline --- .../Detection/Algorithm/CMakeLists.txt | 1 + .../AbstractSubCollisionPipeline.cpp | 105 ++++++++++++++++++ .../algorithm/AbstractSubCollisionPipeline.h | 72 ++---------- 3 files changed, 113 insertions(+), 65 deletions(-) create mode 100644 Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.cpp diff --git a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt index edc3176316a..74039c8d276 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt +++ b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt @@ -26,6 +26,7 @@ set(HEADER_FILES set(SOURCE_FILES ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/init.cpp + ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/AbstractSubCollisionPipeline.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BVHNarrowPhase.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceBroadPhase.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceDetection.cpp diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.cpp new file mode 100644 index 00000000000..13110b12892 --- /dev/null +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** +* 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::component::collision::detection::algorithm +{ + +AbstractSubCollisionPipeline::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 AbstractSubCollisionPipeline::doBwdInit() +{ + +} + +void AbstractSubCollisionPipeline::doDraw(const core::visual::VisualParams* vparams) +{ + SOFA_UNUSED(vparams); + +} + +void AbstractSubCollisionPipeline::init() +{ + 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(); +} + +std::set< std::string > AbstractSubCollisionPipeline::getResponseList() +{ + std::set< std::string > listResponse; + for (const auto& [key, creatorPtr] : *core::collision::Contact::Factory::getInstance()) + { + listResponse.insert(key); + } + return listResponse; +} + +void AbstractSubCollisionPipeline::draw(const core::visual::VisualParams* vparams) +{ + const auto stateLifeCycle = vparams->drawTool()->makeStateLifeCycle(); + + doDraw(vparams); +} + +void AbstractSubCollisionPipeline::handleEvent(sofa::core::objectmodel::Event* e) +{ + doHandleEvent(e); +} + +} // namespace sofa::component::collision::detection::algorithm 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 index 782f8891512..d16673f4d4e 100644 --- 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 @@ -28,8 +28,6 @@ #include #include -#include - #include #include @@ -42,79 +40,23 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipel SOFA_ABSTRACT_CLASS(AbstractSubCollisionPipeline, sofa::core::objectmodel::BaseObject); protected: - 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")) - { - - } + AbstractSubCollisionPipeline(); virtual void doInit() = 0; - virtual void doBwdInit() {} + virtual void doBwdInit(); virtual void doHandleEvent(sofa::core::objectmodel::Event* e) = 0; - virtual void doDraw(const core::visual::VisualParams*) {} + virtual void doDraw(const core::visual::VisualParams* vparams); public: virtual void computeCollisionReset() = 0; virtual void computeCollisionDetection() = 0; virtual void computeCollisionResponse() = 0; - 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; - for (const auto& [key, creatorPtr] : *core::collision::Contact::Factory::getInstance()) - { - listResponse.insert(key); - } - return listResponse; - } - - void draw(const core::visual::VisualParams* vparams) override final - { - const auto stateLifeCycle = vparams->drawTool()->makeStateLifeCycle(); - - doDraw(vparams); - } + void init() override final; + void draw(const core::visual::VisualParams* vparams) override final; + void handleEvent(sofa::core::objectmodel::Event* e) override final; - void handleEvent(sofa::core::objectmodel::Event* e) override final - { - doHandleEvent(e); - } + static std::set< std::string > getResponseList(); 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; From c30920d17cc190af05b83a26fa1939025701ea24 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 11:06:46 +0900 Subject: [PATCH 12/28] Update Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.cpp - add assertion Co-authored-by: Alex Bilger --- .../collision/detection/algorithm/CollisionPipeline.cpp | 1 + 1 file changed, 1 insertion(+) 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 00d6e6ce740..82e27c6559b 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 @@ -86,6 +86,7 @@ void CollisionPipeline::init() msg_info() << "If you want more flexibility, use directly the components MultiCollisionPipeline and SubCollisionPipeline, with their respective Data."; auto context = this->getContext(); + assert(context); // set the whole collision models list to the sub collision pipeline sofa::type::vector collisionModels; context->get>(&collisionModels, BaseContext::SearchRoot); From 862cf0c2f66bb03821a93cc89561e86b568110e9 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 11:31:45 +0900 Subject: [PATCH 13/28] multipipeline inherits TaskSchedulerUser --- .../algorithm/AbstractSubCollisionPipeline.h | 3 +-- .../detection/algorithm/MultiCollisionPipeline.cpp | 5 +---- .../detection/algorithm/MultiCollisionPipeline.h | 13 ++++--------- 3 files changed, 6 insertions(+), 15 deletions(-) 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 index d16673f4d4e..1355a266cac 100644 --- 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 @@ -22,9 +22,8 @@ #pragma once #include -#include - #include +#include #include #include 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 index e25671d5678..7c7ada59bc6 100644 --- 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 @@ -75,10 +75,7 @@ void MultiCollisionPipeline::init() if(d_parallelDetection.getValue() || d_parallelResponse.getValue()) { - m_taskScheduler = sofa::simulation::MainTaskSchedulerFactory::createInRegistry(); - assert(m_taskScheduler); - - m_taskScheduler->init(); + this->initTaskScheduler(); } // UX: warn if there is any CollisionModel not handled by any SubCollisionPipeline 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 index dbe49a1a8f0..69ae08edae0 100644 --- 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 @@ -24,20 +24,17 @@ #include -namespace sofa::simulation -{ -class TaskScheduler; -} +#include namespace sofa::component::collision::detection::algorithm { class AbstractSubCollisionPipeline; -class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : public sofa::core::collision::Pipeline +class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : public sofa::core::collision::Pipeline, public sofa::simulation::TaskSchedulerUser { public: - SOFA_CLASS(MultiCollisionPipeline, sofa::core::collision::Pipeline); + SOFA_CLASS2(MultiCollisionPipeline, sofa::core::collision::Pipeline, sofa::simulation::TaskSchedulerUser); sofa::Data d_depth; protected: @@ -67,9 +64,7 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : 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: From e47c62b94db2135d46a056214ad7b59789bdf031 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 12:31:10 +0900 Subject: [PATCH 14/28] rename AbstractSubCollisionPipeline to BaseSubCollisionPipeline --- .../Collision/Detection/Algorithm/CMakeLists.txt | 4 ++-- ...Pipeline.cpp => BaseSubCollisionPipeline.cpp} | 16 ++++++++-------- ...sionPipeline.h => BaseSubCollisionPipeline.h} | 12 ++++++------ .../algorithm/MultiCollisionPipeline.cpp | 4 ++-- .../detection/algorithm/MultiCollisionPipeline.h | 6 +++--- .../detection/algorithm/SubCollisionPipeline.h | 8 ++++---- 6 files changed, 25 insertions(+), 25 deletions(-) rename Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/{AbstractSubCollisionPipeline.cpp => BaseSubCollisionPipeline.cpp} (86%) rename Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/{AbstractSubCollisionPipeline.h => BaseSubCollisionPipeline.h} (78%) diff --git a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt index 74039c8d276..7f2f8268bc9 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt +++ b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt @@ -6,7 +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}/BaseSubCollisionPipeline.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BVHNarrowPhase.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceBroadPhase.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceDetection.h @@ -26,7 +26,7 @@ set(HEADER_FILES set(SOURCE_FILES ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/init.cpp - ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/AbstractSubCollisionPipeline.cpp + ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BaseSubCollisionPipeline.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BVHNarrowPhase.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceBroadPhase.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceDetection.cpp diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp similarity index 86% rename from Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.cpp rename to Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp index 13110b12892..4abd536c0c3 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp @@ -20,14 +20,14 @@ * Contact information: contact@sofa-framework.org * ******************************************************************************/ #pragma once -#include +#include #include namespace sofa::component::collision::detection::algorithm { -AbstractSubCollisionPipeline::AbstractSubCollisionPipeline() +BaseSubCollisionPipeline::BaseSubCollisionPipeline() : 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")) @@ -36,18 +36,18 @@ AbstractSubCollisionPipeline::AbstractSubCollisionPipeline() } -void AbstractSubCollisionPipeline::doBwdInit() +void BaseSubCollisionPipeline::doBwdInit() { } -void AbstractSubCollisionPipeline::doDraw(const core::visual::VisualParams* vparams) +void BaseSubCollisionPipeline::doDraw(const core::visual::VisualParams* vparams) { SOFA_UNUSED(vparams); } -void AbstractSubCollisionPipeline::init() +void BaseSubCollisionPipeline::init() { bool validity = true; @@ -80,7 +80,7 @@ void AbstractSubCollisionPipeline::init() doInit(); } -std::set< std::string > AbstractSubCollisionPipeline::getResponseList() +std::set< std::string > BaseSubCollisionPipeline::getResponseList() { std::set< std::string > listResponse; for (const auto& [key, creatorPtr] : *core::collision::Contact::Factory::getInstance()) @@ -90,14 +90,14 @@ std::set< std::string > AbstractSubCollisionPipeline::getResponseList() return listResponse; } -void AbstractSubCollisionPipeline::draw(const core::visual::VisualParams* vparams) +void BaseSubCollisionPipeline::draw(const core::visual::VisualParams* vparams) { const auto stateLifeCycle = vparams->drawTool()->makeStateLifeCycle(); doDraw(vparams); } -void AbstractSubCollisionPipeline::handleEvent(sofa::core::objectmodel::Event* e) +void BaseSubCollisionPipeline::handleEvent(sofa::core::objectmodel::Event* e) { doHandleEvent(e); } 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/BaseSubCollisionPipeline.h similarity index 78% rename from Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/AbstractSubCollisionPipeline.h rename to Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h index 1355a266cac..e5a8d26e1cd 100644 --- 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/BaseSubCollisionPipeline.h @@ -33,13 +33,13 @@ namespace sofa::component::collision::detection::algorithm { -class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipeline : public sofa::core::objectmodel::BaseObject +class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API BaseSubCollisionPipeline : public sofa::core::objectmodel::BaseObject { public: - SOFA_ABSTRACT_CLASS(AbstractSubCollisionPipeline, sofa::core::objectmodel::BaseObject); + SOFA_ABSTRACT_CLASS(BaseSubCollisionPipeline, sofa::core::objectmodel::BaseObject); protected: - AbstractSubCollisionPipeline(); + BaseSubCollisionPipeline(); virtual void doInit() = 0; virtual void doBwdInit(); @@ -57,9 +57,9 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API AbstractSubCollisionPipel static std::set< std::string > getResponseList(); - 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; + sofa::MultiLink < BaseSubCollisionPipeline, sofa::core::CollisionModel, sofa::BaseLink::FLAG_DUPLICATE > l_collisionModels; + sofa::SingleLink< BaseSubCollisionPipeline, sofa::core::collision::Intersection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_intersectionMethod; + sofa::SingleLink< BaseSubCollisionPipeline, 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/MultiCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp index 7c7ada59bc6..93fa9208520 100644 --- 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 @@ -21,7 +21,7 @@ ******************************************************************************/ #include -#include +#include #include #include @@ -200,7 +200,7 @@ void MultiCollisionPipeline::doCollisionResponse() std::set< std::string > MultiCollisionPipeline::getResponseList() const { - return AbstractSubCollisionPipeline::getResponseList(); + return BaseSubCollisionPipeline::getResponseList(); } void MultiCollisionPipeline::computeCollisionReset() 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 index 69ae08edae0..314536c8392 100644 --- 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 @@ -29,7 +29,7 @@ namespace sofa::component::collision::detection::algorithm { -class AbstractSubCollisionPipeline; +class BaseSubCollisionPipeline; class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : public sofa::core::collision::Pipeline, public sofa::simulation::TaskSchedulerUser { @@ -65,12 +65,12 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : /// Add collision response in the simulation graph virtual void computeCollisionResponse() override final; - std::vector m_subCollisionPipelines; + std::vector m_subCollisionPipelines; public: sofa::Data d_parallelDetection; sofa::Data d_parallelResponse; - sofa::MultiLink < MultiCollisionPipeline, AbstractSubCollisionPipeline, sofa::BaseLink::FLAG_DUPLICATE > l_subCollisionPipelines; + sofa::MultiLink < MultiCollisionPipeline, BaseSubCollisionPipeline, sofa::BaseLink::FLAG_DUPLICATE > l_subCollisionPipelines; friend class CollisionPipeline; // to be able to call do*() 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 index d4f27dd86a9..e091c2e25ae 100644 --- 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 @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include @@ -34,11 +34,11 @@ namespace sofa::component::collision::detection::algorithm { -class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API SubCollisionPipeline : public AbstractSubCollisionPipeline +class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API SubCollisionPipeline : public BaseSubCollisionPipeline { public: - using Inherited = AbstractSubCollisionPipeline; - SOFA_CLASS(SubCollisionPipeline, AbstractSubCollisionPipeline); + using Inherited = BaseSubCollisionPipeline; + SOFA_CLASS(SubCollisionPipeline, Inherited); protected: SubCollisionPipeline(); public: From 96aa02498171d7529ac21218dfa4fd6f1d204b0c Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 12:50:01 +0900 Subject: [PATCH 15/28] remove sorting the sub pipelines --- .../algorithm/MultiCollisionPipeline.cpp | 21 ------------------- 1 file changed, 21 deletions(-) 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 index 93fa9208520..d6a3f98fdf3 100644 --- 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 @@ -130,27 +130,6 @@ void MultiCollisionPipeline::doCollisionReset() { 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) From 346af123e9a0262c88aaba0b6cf7caff5511bcf0 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 12:53:15 +0900 Subject: [PATCH 16/28] remove useless code (about parallel computeResponse) --- .../algorithm/MultiCollisionPipeline.cpp | 22 ++++--------------- .../algorithm/MultiCollisionPipeline.h | 1 - 2 files changed, 4 insertions(+), 19 deletions(-) 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 index d6a3f98fdf3..e34d20297c3 100644 --- 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 @@ -54,7 +54,6 @@ void registerMultiCollisionPipeline(sofa::core::ObjectFactory* factory) 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.")) { } @@ -73,7 +72,7 @@ void MultiCollisionPipeline::init() return; } - if(d_parallelDetection.getValue() || d_parallelResponse.getValue()) + if(d_parallelDetection.getValue()) { this->initTaskScheduler(); } @@ -158,23 +157,10 @@ void MultiCollisionPipeline::doCollisionDetection(const type::vectorcomputeCollisionResponse(); - } - }; - - sofa::simulation::forEachRange(execution, *m_taskScheduler, l_subCollisionPipelines.begin(), l_subCollisionPipelines.end(), computeCollisionResponse); + subPipeline->computeCollisionResponse(); + } } std::set< std::string > MultiCollisionPipeline::getResponseList() const 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 index 314536c8392..cd14338b489 100644 --- 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 @@ -69,7 +69,6 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : public: sofa::Data d_parallelDetection; - sofa::Data d_parallelResponse; sofa::MultiLink < MultiCollisionPipeline, BaseSubCollisionPipeline, sofa::BaseLink::FLAG_DUPLICATE > l_subCollisionPipelines; From 3217113db856829cfcf667724e0c68f4a682be2a Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 12:56:35 +0900 Subject: [PATCH 17/28] remove possible dereference to null --- .../algorithm/MultiCollisionPipeline.cpp | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) 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 index e34d20297c3..b6457901fe7 100644 --- 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 @@ -137,22 +137,25 @@ void MultiCollisionPipeline::doCollisionDetection(const type::vectorcomputeCollisionDetection(); + for (auto it = range.start; it != range.end; ++it) + { + (*it)->computeCollisionDetection(); + } + }; + + sofa::simulation::forEachRange(sofa::simulation::ForEachExecutionPolicy::PARALLEL, *m_taskScheduler, m_subCollisionPipelines.begin(), m_subCollisionPipelines.end(), computeCollisionDetection); + } + else + { + for (const auto& subPipeline : m_subCollisionPipelines) + { + subPipeline->computeCollisionDetection(); } - }; - - sofa::simulation::forEachRange(execution, *m_taskScheduler, m_subCollisionPipelines.begin(), m_subCollisionPipelines.end(), computeCollisionDetection); + } } void MultiCollisionPipeline::doCollisionResponse() From 907b2be12b4e1ea2f766d3ca8f9369f822a34614 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 13:02:36 +0900 Subject: [PATCH 18/28] rename MultiCollisionPipeline to CompositeCollisionPipeline --- .../Detection/Algorithm/CMakeLists.txt | 4 +-- .../detection/algorithm/CollisionPipeline.cpp | 6 ++-- .../detection/algorithm/CollisionPipeline.h | 4 +-- ...ine.cpp => CompositeCollisionPipeline.cpp} | 34 +++++++++---------- ...ipeline.h => CompositeCollisionPipeline.h} | 8 ++--- .../algorithm/SubCollisionPipeline.cpp | 2 +- .../collision/detection/algorithm/init.cpp | 4 +-- 7 files changed, 31 insertions(+), 31 deletions(-) rename Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/{MultiCollisionPipeline.cpp => CompositeCollisionPipeline.cpp} (84%) rename Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/{MultiCollisionPipeline.h => CompositeCollisionPipeline.h} (88%) diff --git a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt index 7f2f8268bc9..b59238f8a65 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt +++ b/Sofa/Component/Collision/Detection/Algorithm/CMakeLists.txt @@ -11,6 +11,7 @@ set(HEADER_FILES ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceBroadPhase.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceDetection.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/CollisionPM.h + ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/CompositeCollisionPipeline.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/DSAPBox.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/CollisionPipeline.h ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/DirectSAP.h @@ -18,7 +19,6 @@ 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 @@ -32,10 +32,10 @@ set(SOURCE_FILES ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/BruteForceDetection.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/DSAPBox.cpp ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/CollisionPipeline.cpp + ${SOFACOMPONENTCOLLISIONDETECTIONALGORITHM_SOURCE_DIR}/CompositeCollisionPipeline.cpp ${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 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 82e27c6559b..95ee9f10593 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 @@ -71,7 +71,7 @@ CollisionPipeline::CollisionPipeline() 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 = sofa::core::objectmodel::New(); m_multiCollisionPipeline->l_subCollisionPipelines.add(m_subCollisionPipeline.get()); this->addSlave(m_subCollisionPipeline); @@ -82,8 +82,8 @@ void CollisionPipeline::init() { Inherit1::init(); - 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."; + msg_info() << "CollisionPipeline is now a wrapper to CompositeCollisionPipeline with a single SubCollisionPipeline."; + msg_info() << "If you want more flexibility, use directly the components CompositeCollisionPipeline and SubCollisionPipeline, with their respective Data."; auto context = this->getContext(); assert(context); 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 f24f9a1828f..7ea3e7add9d 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,7 +23,7 @@ #include #include -#include +#include #include namespace sofa::component::collision::detection::algorithm @@ -56,7 +56,7 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CollisionPipeline : publi virtual void checkDataValues() ; - MultiCollisionPipeline::SPtr m_multiCollisionPipeline; + CompositeCollisionPipeline::SPtr m_multiCollisionPipeline; SubCollisionPipeline::SPtr m_subCollisionPipeline; public: 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/CompositeCollisionPipeline.cpp similarity index 84% rename from Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.cpp rename to Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp index b6457901fe7..9ba35ff316d 100644 --- 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/CompositeCollisionPipeline.cpp @@ -19,7 +19,7 @@ * * * Contact information: contact@sofa-framework.org * ******************************************************************************/ -#include +#include #include @@ -46,19 +46,19 @@ using namespace sofa; using namespace sofa::core; using namespace sofa::core::collision; -void registerMultiCollisionPipeline(sofa::core::ObjectFactory* factory) +void registerCompositeCollisionPipeline(sofa::core::ObjectFactory* factory) { factory->registerObjects(core::ObjectRegistrationData("Multiple collision pipelines in one.") - .add< MultiCollisionPipeline >()); + .add< CompositeCollisionPipeline >()); } -MultiCollisionPipeline::MultiCollisionPipeline() +CompositeCollisionPipeline::CompositeCollisionPipeline() : d_parallelDetection(initData(&d_parallelDetection, false, "parallelDetection", "Parallelize collision detection.")) , l_subCollisionPipelines(initLink("subCollisionPipelines", "List of sub collision pipelines to handle.")) { } -void MultiCollisionPipeline::init() +void CompositeCollisionPipeline::init() { Inherit1::init(); @@ -66,7 +66,7 @@ void MultiCollisionPipeline::init() if(l_subCollisionPipelines.size() == 0) { - msg_warning() << "No SubCollisionPipeline defined in MultiCollisionPipeline. Nothing will be done." ; + msg_warning() << "No SubCollisionPipeline defined in CompositeCollisionPipeline. Nothing will be done." ; this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; @@ -108,7 +108,7 @@ void MultiCollisionPipeline::init() } -void MultiCollisionPipeline::bwdInit() +void CompositeCollisionPipeline::bwdInit() { for(const auto& subPipeline : l_subCollisionPipelines) { @@ -116,14 +116,14 @@ void MultiCollisionPipeline::bwdInit() } } -void MultiCollisionPipeline::reset() +void CompositeCollisionPipeline::reset() { } -void MultiCollisionPipeline::doCollisionReset() +void CompositeCollisionPipeline::doCollisionReset() { - msg_info() << "MultiCollisionPipeline::doCollisionReset" ; + msg_info() << "CompositeCollisionPipeline::doCollisionReset" ; for(const auto& subPipeline : l_subCollisionPipelines) { @@ -131,7 +131,7 @@ void MultiCollisionPipeline::doCollisionReset() } } -void MultiCollisionPipeline::doCollisionDetection(const type::vector& collisionModels) +void CompositeCollisionPipeline::doCollisionDetection(const type::vector& collisionModels) { SOFA_UNUSED(collisionModels); @@ -158,7 +158,7 @@ void MultiCollisionPipeline::doCollisionDetection(const type::vector MultiCollisionPipeline::getResponseList() const +std::set< std::string > CompositeCollisionPipeline::getResponseList() const { return BaseSubCollisionPipeline::getResponseList(); } -void MultiCollisionPipeline::computeCollisionReset() +void CompositeCollisionPipeline::computeCollisionReset() { if(!this->isComponentStateValid()) return; @@ -179,7 +179,7 @@ void MultiCollisionPipeline::computeCollisionReset() doCollisionReset(); } -void MultiCollisionPipeline::computeCollisionDetection() +void CompositeCollisionPipeline::computeCollisionDetection() { if(!this->isComponentStateValid()) return; @@ -190,7 +190,7 @@ void MultiCollisionPipeline::computeCollisionDetection() doCollisionDetection(collisionModels); } -void MultiCollisionPipeline::computeCollisionResponse() +void CompositeCollisionPipeline::computeCollisionResponse() { if(!this->isComponentStateValid()) return; @@ -199,7 +199,7 @@ void MultiCollisionPipeline::computeCollisionResponse() } -void MultiCollisionPipeline::draw(const core::visual::VisualParams* vparams) +void CompositeCollisionPipeline::draw(const core::visual::VisualParams* vparams) { for (const auto& subPipeline : m_subCollisionPipelines) { 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/CompositeCollisionPipeline.h similarity index 88% rename from Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/MultiCollisionPipeline.h rename to Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h index cd14338b489..87350713118 100644 --- 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/CompositeCollisionPipeline.h @@ -31,14 +31,14 @@ namespace sofa::component::collision::detection::algorithm class BaseSubCollisionPipeline; -class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : public sofa::core::collision::Pipeline, public sofa::simulation::TaskSchedulerUser +class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CompositeCollisionPipeline : public sofa::core::collision::Pipeline, public sofa::simulation::TaskSchedulerUser { public: - SOFA_CLASS2(MultiCollisionPipeline, sofa::core::collision::Pipeline, sofa::simulation::TaskSchedulerUser); + SOFA_CLASS2(CompositeCollisionPipeline, sofa::core::collision::Pipeline, sofa::simulation::TaskSchedulerUser); sofa::Data d_depth; protected: - MultiCollisionPipeline(); + CompositeCollisionPipeline(); public: void init() override; void bwdInit() override; @@ -69,7 +69,7 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API MultiCollisionPipeline : public: sofa::Data d_parallelDetection; - sofa::MultiLink < MultiCollisionPipeline, BaseSubCollisionPipeline, sofa::BaseLink::FLAG_DUPLICATE > l_subCollisionPipelines; + sofa::MultiLink < CompositeCollisionPipeline, BaseSubCollisionPipeline, sofa::BaseLink::FLAG_DUPLICATE > l_subCollisionPipelines; friend class CollisionPipeline; // to be able to call do*() 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 index 1cdbb8cff5f..99ab9710378 100644 --- 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 @@ -42,7 +42,7 @@ using namespace sofa::core::collision; void registerSubCollisionPipeline(sofa::core::ObjectFactory* factory) { - factory->registerObjects(core::ObjectRegistrationData("Collision pipeline to be used with MultiCollisionPipeline.") + factory->registerObjects(core::ObjectRegistrationData("Collision pipeline to be used with CompositeCollisionPipeline.") .add< SubCollisionPipeline >()); } 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 2541b470943..b8ae17c38bf 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,7 +29,7 @@ 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 registerCompositeCollisionPipeline(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); @@ -66,7 +66,7 @@ void registerObjects(sofa::core::ObjectFactory* factory) registerBruteForceBroadPhase(factory); registerBruteForceDetection(factory); registerBVHNarrowPhase(factory); - registerMultiCollisionPipeline(factory); + registerCompositeCollisionPipeline(factory); registerSubCollisionPipeline(factory); registerCollisionPipeline(factory); registerDirectSAP(factory); From 948d1b8b394ec8349e4f71951f0c7c1455b4342b Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 22 Jan 2026 13:27:22 +0900 Subject: [PATCH 19/28] the compat class CollisionPipeline now inherits CompositeCollisionPipeline --- .../detection/algorithm/CollisionPipeline.cpp | 67 ++++++++----------- .../detection/algorithm/CollisionPipeline.h | 16 +---- .../algorithm/CompositeCollisionPipeline.cpp | 10 +-- .../algorithm/CompositeCollisionPipeline.h | 4 +- 4 files changed, 36 insertions(+), 61 deletions(-) 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 95ee9f10593..449a48993f5 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 @@ -67,46 +67,55 @@ CollisionPipeline::CollisionPipeline() ("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); } void CollisionPipeline::init() { - Inherit1::init(); - msg_info() << "CollisionPipeline is now a wrapper to CompositeCollisionPipeline with a single SubCollisionPipeline."; msg_info() << "If you want more flexibility, use directly the components CompositeCollisionPipeline and SubCollisionPipeline, with their respective Data."; - + auto context = this->getContext(); assert(context); + + m_subCollisionPipeline = sofa::core::objectmodel::New(); + m_subCollisionPipeline->d_depth.setParent(&this->d_depth); + // 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); + // set the other components to the sub collision pipeline + // intersection + sofa::core::collision::Intersection* intersectionMethod = nullptr; + context->get(intersectionMethod, BaseContext::SearchRoot); + m_subCollisionPipeline->l_intersectionMethod.set(intersectionMethod); + + // broad phase + sofa::core::collision::BroadPhaseDetection* broadPhaseDetection = nullptr; + context->get(broadPhaseDetection, BaseContext::SearchRoot); + m_subCollisionPipeline->l_broadPhaseDetection.set(broadPhaseDetection); + // narrow phase + sofa::core::collision::NarrowPhaseDetection* narrowPhaseDetection = nullptr; + context->get(narrowPhaseDetection, BaseContext::SearchRoot); + m_subCollisionPipeline->l_narrowPhaseDetection.set(narrowPhaseDetection); + + // contact manager + sofa::core::collision::ContactManager* contactManager = nullptr; + context->get(contactManager, BaseContext::SearchRoot); + m_subCollisionPipeline->l_contactManager.set(contactManager); + m_subCollisionPipeline->init(); - m_multiCollisionPipeline->init(); + this->l_subCollisionPipelines.add(m_subCollisionPipeline.get()); /// Insure that all the value provided by the user are valid and report message if it is not. checkDataValues() ; + + Inherit1::init(); } void CollisionPipeline::checkDataValues() @@ -119,24 +128,4 @@ void CollisionPipeline::checkDataValues() } } -void CollisionPipeline::doCollisionReset() -{ - m_multiCollisionPipeline->doCollisionReset(); -} - -void CollisionPipeline::doCollisionDetection(const type::vector& collisionModels) -{ - m_multiCollisionPipeline->doCollisionDetection(collisionModels); -} - -void CollisionPipeline::doCollisionResponse() -{ - m_multiCollisionPipeline->doCollisionResponse(); -} - -std::set< std::string > CollisionPipeline::getResponseList() const -{ - 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 7ea3e7add9d..d497092d498 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 @@ -22,17 +22,16 @@ #pragma once #include -#include #include #include namespace sofa::component::collision::detection::algorithm { -class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CollisionPipeline : public sofa::simulation::PipelineImpl +class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CollisionPipeline : public CompositeCollisionPipeline { public: - SOFA_CLASS(CollisionPipeline,sofa::simulation::PipelineImpl); + SOFA_CLASS(CollisionPipeline, CompositeCollisionPipeline); Data d_doPrintInfoMessage; Data d_doDebugDraw; @@ -43,20 +42,9 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CollisionPipeline : publi public: void init() 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; - virtual void checkDataValues() ; - CompositeCollisionPipeline::SPtr m_multiCollisionPipeline; SubCollisionPipeline::SPtr m_subCollisionPipeline; public: diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp index 9ba35ff316d..133ac5134c8 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp @@ -147,11 +147,11 @@ void CompositeCollisionPipeline::doCollisionDetection(const type::vectorcomputeCollisionDetection(); } @@ -160,7 +160,7 @@ void CompositeCollisionPipeline::doCollisionDetection(const type::vectorcomputeCollisionResponse(); } @@ -185,7 +185,7 @@ void CompositeCollisionPipeline::computeCollisionDetection() return; //useless - std::vector collisionModels; + static std::vector collisionModels{}; doCollisionDetection(collisionModels); } @@ -201,7 +201,7 @@ void CompositeCollisionPipeline::computeCollisionResponse() void CompositeCollisionPipeline::draw(const core::visual::VisualParams* vparams) { - for (const auto& subPipeline : m_subCollisionPipelines) + for (const auto& subPipeline : l_subCollisionPipelines) { subPipeline->draw(vparams); } diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h index 87350713118..8a94ac8f655 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h @@ -63,9 +63,7 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CompositeCollisionPipelin /// 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; - - std::vector m_subCollisionPipelines; + virtual void computeCollisionResponse() override final; public: sofa::Data d_parallelDetection; From 667a4e491c1a005518f8a13a32b8c4a94441cba6 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 23 Jan 2026 14:04:36 +0900 Subject: [PATCH 20/28] CollisionPipeline: search from the local node instead of the root node --- .../detection/algorithm/CollisionPipeline.cpp | 10 +++++----- .../detection/algorithm/CompositeCollisionPipeline.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) 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 449a48993f5..ff70730318e 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 @@ -82,7 +82,7 @@ void CollisionPipeline::init() // set the whole collision models list to the sub collision pipeline sofa::type::vector collisionModels; - context->get>(&collisionModels, BaseContext::SearchRoot); + context->get>(&collisionModels, BaseContext::SearchDown); for(auto collisionModel : collisionModels) { m_subCollisionPipeline->l_collisionModels.add(collisionModel.get()); @@ -91,22 +91,22 @@ void CollisionPipeline::init() // set the other components to the sub collision pipeline // intersection sofa::core::collision::Intersection* intersectionMethod = nullptr; - context->get(intersectionMethod, BaseContext::SearchRoot); + context->get(intersectionMethod, BaseContext::SearchDown); m_subCollisionPipeline->l_intersectionMethod.set(intersectionMethod); // broad phase sofa::core::collision::BroadPhaseDetection* broadPhaseDetection = nullptr; - context->get(broadPhaseDetection, BaseContext::SearchRoot); + context->get(broadPhaseDetection, BaseContext::SearchDown); m_subCollisionPipeline->l_broadPhaseDetection.set(broadPhaseDetection); // narrow phase sofa::core::collision::NarrowPhaseDetection* narrowPhaseDetection = nullptr; - context->get(narrowPhaseDetection, BaseContext::SearchRoot); + context->get(narrowPhaseDetection, BaseContext::SearchDown); m_subCollisionPipeline->l_narrowPhaseDetection.set(narrowPhaseDetection); // contact manager sofa::core::collision::ContactManager* contactManager = nullptr; - context->get(contactManager, BaseContext::SearchRoot); + context->get(contactManager, BaseContext::SearchDown); m_subCollisionPipeline->l_contactManager.set(contactManager); m_subCollisionPipeline->init(); diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp index 133ac5134c8..63efa722299 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp @@ -78,7 +78,7 @@ void CompositeCollisionPipeline::init() } // UX: warn if there is any CollisionModel not handled by any SubCollisionPipeline - simulation::Node* root = dynamic_cast(getContext()->getRootContext()); + simulation::Node* root = dynamic_cast(getContext()); std::vector sceneCollisionModels; root->getTreeObjects(&sceneCollisionModels); From f7338a26500351fd8a849ed121d9f6e4bb1bce4e Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Fri, 23 Jan 2026 15:55:07 +0900 Subject: [PATCH 21/28] refactoring --- .../algorithm/BaseSubCollisionPipeline.cpp | 34 ++--------------- .../algorithm/BaseSubCollisionPipeline.h | 14 +++---- .../detection/algorithm/CollisionPipeline.cpp | 1 + .../algorithm/CompositeCollisionPipeline.cpp | 19 +--------- .../algorithm/CompositeCollisionPipeline.h | 3 -- .../algorithm/SubCollisionPipeline.cpp | 38 +++++++++++++++++++ .../algorithm/SubCollisionPipeline.h | 9 ++++- 7 files changed, 58 insertions(+), 60 deletions(-) diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp index 4abd536c0c3..60f226299f3 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp @@ -23,15 +23,13 @@ #include #include +#include namespace sofa::component::collision::detection::algorithm { BaseSubCollisionPipeline::BaseSubCollisionPipeline() : 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")) { } @@ -49,34 +47,8 @@ void BaseSubCollisionPipeline::doDraw(const core::visual::VisualParams* vparams) void BaseSubCollisionPipeline::init() { - 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); - } - + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Loading); + doInit(); } diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h index e5a8d26e1cd..f1ff039fcf2 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h @@ -22,14 +22,16 @@ #pragma once #include -#include #include -#include -#include #include #include +namespace sofa::core +{ +class CollisionModel; +} + namespace sofa::component::collision::detection::algorithm { @@ -51,15 +53,13 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API BaseSubCollisionPipeline virtual void computeCollisionDetection() = 0; virtual void computeCollisionResponse() = 0; + virtual std::vector getCollisionModels() = 0; + void init() override final; void draw(const core::visual::VisualParams* vparams) override final; void handleEvent(sofa::core::objectmodel::Event* e) override final; static std::set< std::string > getResponseList(); - - sofa::MultiLink < BaseSubCollisionPipeline, sofa::core::CollisionModel, sofa::BaseLink::FLAG_DUPLICATE > l_collisionModels; - sofa::SingleLink< BaseSubCollisionPipeline, sofa::core::collision::Intersection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_intersectionMethod; - sofa::SingleLink< BaseSubCollisionPipeline, 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 ff70730318e..fe92271992c 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 @@ -111,6 +111,7 @@ void CollisionPipeline::init() m_subCollisionPipeline->init(); this->l_subCollisionPipelines.add(m_subCollisionPipeline.get()); + this->addSlave(m_subCollisionPipeline.get()); /// Insure that all the value provided by the user are valid and report message if it is not. checkDataValues() ; diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp index 63efa722299..41fccdc6932 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp @@ -92,7 +92,7 @@ void CompositeCollisionPipeline::init() return; } - for (auto cm : subPipeline->l_collisionModels) + for (auto* cm : subPipeline->getCollisionModels()) { pipelineCollisionModels.insert(cm); } @@ -108,14 +108,6 @@ void CompositeCollisionPipeline::init() } -void CompositeCollisionPipeline::bwdInit() -{ - for(const auto& subPipeline : l_subCollisionPipelines) - { - subPipeline->bwdInit(); - } -} - void CompositeCollisionPipeline::reset() { @@ -198,13 +190,4 @@ void CompositeCollisionPipeline::computeCollisionResponse() doCollisionResponse(); } - -void CompositeCollisionPipeline::draw(const core::visual::VisualParams* vparams) -{ - for (const auto& subPipeline : l_subCollisionPipelines) - { - subPipeline->draw(vparams); - } -} - } // namespace sofa::component::collision::detection::algorithm diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h index 8a94ac8f655..3e28a8c86b0 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h @@ -41,7 +41,6 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CompositeCollisionPipelin CompositeCollisionPipeline(); 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; @@ -56,8 +55,6 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CompositeCollisionPipelin 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 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 index 99ab9710378..724dcfd8328 100644 --- 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 @@ -49,6 +49,9 @@ void registerSubCollisionPipeline(sofa::core::ObjectFactory* factory) 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_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")) , l_broadPhaseDetection(initLink("broadPhaseDetection", "Broad phase detection to use in this pipeline")) , l_narrowPhaseDetection(initLink("narrowPhaseDetection", "Narrow phase detection to use in this pipeline")) { @@ -58,6 +61,25 @@ void SubCollisionPipeline::doInit() { bool validity = true; + //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 (!l_broadPhaseDetection) { msg_warning() << "A BroadPhase component is required to compute collision detection."; @@ -73,6 +95,10 @@ void SubCollisionPipeline::doInit() { this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); } + else + { + this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); + } } @@ -241,4 +267,16 @@ void SubCollisionPipeline::computeCollisionResponse() } } + +std::vector SubCollisionPipeline::getCollisionModels() +{ + std::vector collisionModels; + collisionModels.reserve(l_collisionModels.getSize()); + for(auto* collisionModel : l_collisionModels) + { + collisionModels.push_back(collisionModel); + } + return collisionModels; +} + } // 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 index e091c2e25ae..ff535930df1 100644 --- 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 @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include @@ -49,8 +51,13 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API SubCollisionPipeline : pu void computeCollisionReset() override; void computeCollisionDetection() override; void computeCollisionResponse() override; - + + std::vector getCollisionModels() override; + sofa::Data d_depth; + sofa::MultiLink < SubCollisionPipeline, sofa::core::CollisionModel, sofa::BaseLink::FLAG_DUPLICATE > l_collisionModels; + sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::Intersection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_intersectionMethod; + sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::ContactManager, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_contactManager; 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; From eccb3d3a79e672128d9dfd4e4401c9fa6493ab2c Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 27 Jan 2026 08:27:20 +0900 Subject: [PATCH 22/28] Apply suggestions from code review (reformatting) Co-authored-by: Hugo --- .../algorithm/SubCollisionPipeline.cpp | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) 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 index 724dcfd8328..a8449c5a1cd 100644 --- 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 @@ -131,14 +131,11 @@ void SubCollisionPipeline::computeCollisionDetection() if (!this->isComponentStateValid()) return; - msg_info() - << "doCollisionDetection, compute Bounding Trees" ; + 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"); @@ -156,8 +153,7 @@ void SubCollisionPipeline::computeCollisionDetection() for (auto it = l_collisionModels.begin(); it != l_collisionModels.end(); ++it) { - msg_info() - << "doCollisionDetection, consider model" ; + msg_info() << "doCollisionDetection, consider model" ; if (!(*it)->isActive()) continue; @@ -179,12 +175,10 @@ void SubCollisionPipeline::computeCollisionDetection() } - msg_info() - << "doCollisionDetection, Computed "<getName(); + msg_info() << "doCollisionDetection, BroadPhaseDetection "<getName(); { SCOPED_TIMER_VARNAME(broadphase, "BroadPhase"); @@ -195,8 +189,7 @@ void SubCollisionPipeline::computeCollisionDetection() l_intersectionMethod->endBroadPhase(); } - msg_info() - << "doCollisionDetection, NarrowPhaseDetection "<< l_narrowPhaseDetection->getName(); + msg_info() << "doCollisionDetection, NarrowPhaseDetection "<< l_narrowPhaseDetection->getName(); { SCOPED_TIMER_VARNAME(narrowphase, "NarrowPhase"); @@ -204,8 +197,7 @@ void SubCollisionPipeline::computeCollisionDetection() l_narrowPhaseDetection->beginNarrowPhase(); const type::vector >& vectCMPair = l_broadPhaseDetection->getCollisionModelPairs(); - msg_info() - << "doCollisionDetection, "<< vectCMPair.size()<<" colliding model pairs" ; + msg_info() << "doCollisionDetection, "<< vectCMPair.size()<<" colliding model pairs" ; l_narrowPhaseDetection->addCollisionPairs(vectCMPair); l_narrowPhaseDetection->endNarrowPhase(); @@ -221,8 +213,7 @@ void SubCollisionPipeline::computeCollisionResponse() core::objectmodel::BaseContext* scene = getContext(); - msg_info() - << "Create Contacts " << l_contactManager->getName() ; + msg_info() << "Create Contacts " << l_contactManager->getName() ; { SCOPED_TIMER_VARNAME(createContactsTimer, "CreateContacts"); @@ -230,7 +221,6 @@ void SubCollisionPipeline::computeCollisionResponse() } // 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 @@ -258,8 +248,7 @@ void SubCollisionPipeline::computeCollisionResponse() SCOPED_TIMER_VARNAME(createResponseTimer, "CreateMovingObjectsResponse"); - msg_info() - << "Linking all contacts to Scene" ; + msg_info() << "Linking all contacts to Scene" ; for (const auto& contact : notStaticContacts) { From 848a7eafbc9e2f6d163c521aa6ddea95f37bd874 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 27 Jan 2026 13:34:37 +0900 Subject: [PATCH 23/28] print path instead of the name only --- .../detection/algorithm/CompositeCollisionPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp index 41fccdc6932..0f6691f4054 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp @@ -102,7 +102,7 @@ void CompositeCollisionPipeline::init() { if (pipelineCollisionModels.find(cm) == pipelineCollisionModels.end()) { - msg_warning() << "CollisionModel " << cm->getName() << " is not handled by any SubCollisionPipeline."; + msg_warning() << "CollisionModel " << cm->getPathName() << " is not handled by any SubCollisionPipeline."; } } From 24cc25f810845f86a94732a32798838cb40ad76c Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 27 Jan 2026 13:46:13 +0900 Subject: [PATCH 24/28] add show cases --- .../Detection/CompositeCollisionPipeline.scn | 96 +++++++++++++++++++ .../CompositeCollisionPipeline.scn.view | 17 ++++ .../CompositeCollisionPipeline_none.scn | 75 +++++++++++++++ .../CompositeCollisionPipeline_none.scn.view | 17 ++++ 4 files changed, 205 insertions(+) create mode 100644 examples/Component/Collision/Detection/CompositeCollisionPipeline.scn create mode 100644 examples/Component/Collision/Detection/CompositeCollisionPipeline.scn.view create mode 100644 examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn create mode 100644 examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn.view diff --git a/examples/Component/Collision/Detection/CompositeCollisionPipeline.scn b/examples/Component/Collision/Detection/CompositeCollisionPipeline.scn new file mode 100644 index 00000000000..50fdf09fd01 --- /dev/null +++ b/examples/Component/Collision/Detection/CompositeCollisionPipeline.scn @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Component/Collision/Detection/CompositeCollisionPipeline.scn.view b/examples/Component/Collision/Detection/CompositeCollisionPipeline.scn.view new file mode 100644 index 00000000000..0f039dffcad --- /dev/null +++ b/examples/Component/Collision/Detection/CompositeCollisionPipeline.scn.view @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn b/examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn new file mode 100644 index 00000000000..9fd746092c6 --- /dev/null +++ b/examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn.view b/examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn.view new file mode 100644 index 00000000000..0f039dffcad --- /dev/null +++ b/examples/Component/Collision/Detection/CompositeCollisionPipeline_none.scn.view @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + From 015353b05f31cbe59f360894cd21adf2236f8b80 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 27 Jan 2026 15:23:30 +0900 Subject: [PATCH 25/28] add regression --- examples/RegressionStateScenes.regression-tests | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/RegressionStateScenes.regression-tests b/examples/RegressionStateScenes.regression-tests index 6270d2211ad..fc9ec569ad2 100644 --- a/examples/RegressionStateScenes.regression-tests +++ b/examples/RegressionStateScenes.regression-tests @@ -37,6 +37,8 @@ Demos/SofaScene.scn 120 1e-5 1 1 ### Component scenes ### Component/Collision/Response/RuleBasedContactManager.scn 100 1e-4 0 1 Component/Collision/Response/FrictionContact.scn 100 1e-4 0 1 +Component/Collision/Detection/CompositeCollisionPipeline.scn 200 1e-4 0 1 +Component/Collision/Detection/CompositeCollisionPipeline_none.scn 200 1e-4 0 1 Component/Constraint/Lagrangian/BilateralLagrangianConstraint_NNCG.scn 100 1e-4 0 1 Component/Constraint/Lagrangian/BilateralLagrangianConstraint_PGS.scn 100 1e-4 0 1 Component/Constraint/Lagrangian/BilateralLagrangianConstraint_UGS.scn 100 1e-4 0 1 From 3ac2add3f3cb3ab5f4b0ffc1833ba552ef0b7ddb Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 28 Jan 2026 08:25:37 +0900 Subject: [PATCH 26/28] add comments --- .../algorithm/BaseSubCollisionPipeline.cpp | 17 ++++- .../algorithm/BaseSubCollisionPipeline.h | 44 +++++++++-- .../algorithm/CompositeCollisionPipeline.cpp | 53 +++++++++++--- .../algorithm/CompositeCollisionPipeline.h | 51 ++++++++++--- .../algorithm/SubCollisionPipeline.cpp | 73 ++++++++++++++++--- .../algorithm/SubCollisionPipeline.h | 48 +++++++++++- 6 files changed, 243 insertions(+), 43 deletions(-) diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp index 60f226299f3..b15df4b776e 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.cpp @@ -31,27 +31,36 @@ namespace sofa::component::collision::detection::algorithm BaseSubCollisionPipeline::BaseSubCollisionPipeline() : sofa::core::objectmodel::BaseObject() { - + } void BaseSubCollisionPipeline::doBwdInit() { - + } void BaseSubCollisionPipeline::doDraw(const core::visual::VisualParams* vparams) { SOFA_UNUSED(vparams); - + } void BaseSubCollisionPipeline::init() { this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Loading); - + doInit(); } +/** + * @brief Queries all registered contact response types from the Contact factory. + * + * This static method iterates through all contact types registered in the + * Contact::Factory and returns their names. These represent the available + * collision response methods (e.g., "PenalityContactForceField", "FrictionContact"). + * + * @return A set of strings containing all registered contact response type names. + */ std::set< std::string > BaseSubCollisionPipeline::getResponseList() { std::set< std::string > listResponse; diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h index f1ff039fcf2..bdf1bab7919 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/BaseSubCollisionPipeline.h @@ -35,6 +35,15 @@ class CollisionModel; namespace sofa::component::collision::detection::algorithm { +/** + * @brief Abstract base class defining the interface for sub-collision pipelines. + * + * This base class is designed to be used with CompositeCollisionPipeline, which + * aggregates multiple sub-pipelines and can execute them in parallel. + * + * @see SubCollisionPipeline for a concrete implementation + * @see CompositeCollisionPipeline for the aggregator that manages sub-pipelines + */ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API BaseSubCollisionPipeline : public sofa::core::objectmodel::BaseObject { public: @@ -42,23 +51,48 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API BaseSubCollisionPipeline protected: BaseSubCollisionPipeline(); - + + /// @brief Called during initialization. Derived classes must implement validation and setup logic. virtual void doInit() = 0; + + /// @brief Called after all objects are initialized. Default implementation is empty. virtual void doBwdInit(); + + /// @brief Called to handle simulation events. Derived classes must implement event processing. virtual void doHandleEvent(sofa::core::objectmodel::Event* e) = 0; + + /// @brief Called during rendering. Default implementation is empty. virtual void doDraw(const core::visual::VisualParams* vparams); - + public: + ///@{ + /// @name Collision Pipeline Interface + /// These methods define the three-phase collision workflow that derived classes must implement. + + /// @brief Clears collision state from the previous time step (contacts, responses). virtual void computeCollisionReset() = 0; + + /// @brief Performs collision detection (bounding tree, broad phase, narrow phase). virtual void computeCollisionDetection() = 0; + + /// @brief Creates collision responses based on detected contacts. virtual void computeCollisionResponse() = 0; - + + ///@} + + /// @brief Returns the list of collision models handled by this sub-pipeline. virtual std::vector getCollisionModels() = 0; - + + /// @brief Initializes the component. Marked final to enforce Template Method pattern. void init() override final; + + /// @brief Renders debug visualization. Marked final to enforce Template Method pattern. void draw(const core::visual::VisualParams* vparams) override final; + + /// @brief Processes simulation events. Marked final to enforce Template Method pattern. void handleEvent(sofa::core::objectmodel::Event* e) override final; - + + /// @brief Returns all available contact response types registered in the Contact factory. static std::set< std::string > getResponseList(); }; diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp index 0f6691f4054..e50d6e6b0e8 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.cpp @@ -58,12 +58,23 @@ CompositeCollisionPipeline::CompositeCollisionPipeline() { } +/** + * @brief Initializes the composite pipeline and validates its configuration. + * + * This method performs several validation and setup steps: + * 1. Validates that at least one sub-pipeline is linked + * 2. Initializes the task scheduler if parallel detection is enabled + * 3. Validates all linked sub-pipelines are valid (non-null) + * 4. Checks that all collision models in the scene are covered by at least one sub-pipeline + * (issues warnings for any uncovered models to help users identify configuration issues) + */ void CompositeCollisionPipeline::init() { Inherit1::init(); this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Valid); + // Validate that at least one sub-pipeline is defined if(l_subCollisionPipelines.size() == 0) { msg_warning() << "No SubCollisionPipeline defined in CompositeCollisionPipeline. Nothing will be done." ; @@ -71,17 +82,19 @@ void CompositeCollisionPipeline::init() this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } - + + // Initialize task scheduler for parallel execution if enabled if(d_parallelDetection.getValue()) { this->initTaskScheduler(); } - // UX: warn if there is any CollisionModel not handled by any SubCollisionPipeline + // Collect all collision models from the scene to verify coverage simulation::Node* root = dynamic_cast(getContext()); std::vector sceneCollisionModels; root->getTreeObjects(&sceneCollisionModels); + // Collect all collision models handled by sub-pipelines std::set pipelineCollisionModels; for(auto* subPipeline : l_subCollisionPipelines) { @@ -91,13 +104,15 @@ void CompositeCollisionPipeline::init() this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); return; } - + for (auto* cm : subPipeline->getCollisionModels()) { pipelineCollisionModels.insert(cm); } } + // Warn about collision models not covered by any sub-pipeline + // This helps users identify configuration issues where some models won't participate in collision for (const auto& cm : sceneCollisionModels) { if (pipelineCollisionModels.find(cm) == pipelineCollisionModels.end()) @@ -105,7 +120,7 @@ void CompositeCollisionPipeline::init() msg_warning() << "CollisionModel " << cm->getPathName() << " is not handled by any SubCollisionPipeline."; } } - + } void CompositeCollisionPipeline::reset() @@ -113,6 +128,7 @@ void CompositeCollisionPipeline::reset() } +/// Delegates collision reset to all sub-pipelines sequentially. void CompositeCollisionPipeline::doCollisionReset() { msg_info() << "CompositeCollisionPipeline::doCollisionReset" ; @@ -123,6 +139,15 @@ void CompositeCollisionPipeline::doCollisionReset() } } +/** + * @brief Executes collision detection across all sub-pipelines. + * + * If parallel detection is enabled and a task scheduler is available, the detection + * phase of each sub-pipeline runs concurrently. This can significantly improve + * performance when there are multiple independent collision groups. + * + * @param collisionModels Ignored - each sub-pipeline uses its own linked collision models. + */ void CompositeCollisionPipeline::doCollisionDetection(const type::vector& collisionModels) { SOFA_UNUSED(collisionModels); @@ -131,6 +156,7 @@ void CompositeCollisionPipeline::doCollisionDetection(const type::vectorcomputeCollisionDetection(); } }; - + sofa::simulation::forEachRange(sofa::simulation::ForEachExecutionPolicy::PARALLEL, *m_taskScheduler, l_subCollisionPipelines.begin(), l_subCollisionPipelines.end(), computeCollisionDetection); } else { + // Sequential execution: process each sub-pipeline one after another for (const auto& subPipeline : l_subCollisionPipelines) { subPipeline->computeCollisionDetection(); @@ -150,6 +177,7 @@ void CompositeCollisionPipeline::doCollisionDetection(const type::vector CompositeCollisionPipeline::getResponseList() const { return BaseSubCollisionPipeline::getResponseList(); } +/// Entry point for collision reset phase, called by the simulation loop. void CompositeCollisionPipeline::computeCollisionReset() { if(!this->isComponentStateValid()) return; - + doCollisionReset(); } +/// Entry point for collision detection phase, called by the simulation loop. void CompositeCollisionPipeline::computeCollisionDetection() { if(!this->isComponentStateValid()) return; - - //useless + + // The collision models parameter is not used by this pipeline + // since each sub-pipeline manages its own set of models static std::vector collisionModels{}; - + doCollisionDetection(collisionModels); } +/// Entry point for collision response phase, called by the simulation loop. void CompositeCollisionPipeline::computeCollisionResponse() { if(!this->isComponentStateValid()) return; - + doCollisionResponse(); } diff --git a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h index 3e28a8c86b0..99088c894a5 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h +++ b/Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CompositeCollisionPipeline.h @@ -31,6 +31,25 @@ namespace sofa::component::collision::detection::algorithm class BaseSubCollisionPipeline; +/** + * @brief A collision pipeline that aggregates multiple sub-pipelines using the Composite pattern. + * + * CompositeCollisionPipeline enables partitioning collision detection into independent groups, + * where each group is handled by its own SubCollisionPipeline. This architecture provides + * several benefits: + * + * 1. Modularity: Different collision model groups can use different detection algorithms, + * intersection methods, or contact managers + * + * 2. Parallelization: When enabled via d_parallelDetection, the collision detection phase + * of each sub-pipeline runs concurrently, potentially improving performance on multi-core systems + * + * 3. Isolation: Collision models in different sub-pipelines won't generate contacts with each other, + * allowing intentional separation of non-interacting object groups + * + * @see SubCollisionPipeline + * @see BaseSubCollisionPipeline + */ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CompositeCollisionPipeline : public sofa::core::collision::Pipeline, public sofa::simulation::TaskSchedulerUser { public: @@ -40,33 +59,43 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API CompositeCollisionPipelin protected: CompositeCollisionPipeline(); public: + /// @brief Initializes the pipeline and validates sub-pipeline configuration. void init() override; - /// get the set of response available with the current collision pipeline + /// @brief Returns the set of available collision response types. std::set< std::string > getResponseList() const override; protected: // -- Pipeline interface - /// Remove collision response from last step + + /// @brief Delegates reset to all sub-pipelines to clear previous contacts. void doCollisionReset() override; - /// Detect new collisions. Note that this step must not modify the simulation graph + + /// @brief Delegates collision detection to all sub-pipelines (optionally in parallel). + /// @note The collisionModels parameter is ignored; each sub-pipeline uses its own models. void doCollisionDetection(const sofa::type::vector& collisionModels) override; - /// Add collision response in the simulation graph + + /// @brief Delegates response creation to all sub-pipelines. void doCollisionResponse() override; void reset() override; - - /// Remove collision response from last step + + /// @brief Entry point for collision reset, called by the simulation loop. virtual void computeCollisionReset() override final; - /// Detect new collisions. Note that this step must not modify the simulation graph + + /// @brief Entry point for collision detection, called by the simulation loop. virtual void computeCollisionDetection() override final; - /// Add collision response in the simulation graph - virtual void computeCollisionResponse() override final; + + /// @brief Entry point for collision response, called by the simulation loop. + virtual void computeCollisionResponse() override final; public: + /// When true, collision detection across sub-pipelines runs in parallel using the task scheduler. sofa::Data d_parallelDetection; + + /// List of sub-pipelines to aggregate. Each handles an independent set of collision models. sofa::MultiLink < CompositeCollisionPipeline, BaseSubCollisionPipeline, sofa::BaseLink::FLAG_DUPLICATE > l_subCollisionPipelines; - - + + friend class CollisionPipeline; // to be able to call do*() }; 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 index a8449c5a1cd..9cdb4479ac2 100644 --- 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 @@ -57,29 +57,42 @@ SubCollisionPipeline::SubCollisionPipeline() { } +/** + * @brief Validates that all required components are properly linked. + * + * Checks for the presence of all mandatory links: + * - At least one collision model + * - An intersection method + * - A contact manager + * - A broad phase detection component + * - A narrow phase detection component + * + * Sets the component state to Invalid if any required component is missing, + * which prevents the pipeline from executing collision detection. + */ void SubCollisionPipeline::doInit() { bool validity = true; - //Check given parameters + // Validate all required links are set 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 (!l_broadPhaseDetection) { msg_warning() << "A BroadPhase component is required to compute collision detection."; @@ -91,6 +104,7 @@ void SubCollisionPipeline::doInit() validity = false; } + // Set component state based on validation results if (!validity) { this->d_componentState.setValue(sofa::core::objectmodel::ComponentState::Invalid); @@ -102,6 +116,15 @@ void SubCollisionPipeline::doInit() } +/** + * @brief Resets the collision state by clearing all existing contact responses. + * + * This method prepares for a new collision detection cycle by: + * 1. Propagating the intersection method to all detection components + * 2. Removing all contact responses created during the previous time step + * + * This ensures a clean slate before new collisions are detected. + */ void SubCollisionPipeline::computeCollisionReset() { if (!this->isComponentStateValid()) @@ -109,11 +132,12 @@ void SubCollisionPipeline::computeCollisionReset() msg_info() << "SubCollisionPipeline::doCollisionReset"; + // Propagate the intersection method to all collision detection components l_broadPhaseDetection->setIntersectionMethod(l_intersectionMethod.get()); l_narrowPhaseDetection->setIntersectionMethod(l_intersectionMethod.get()); l_contactManager->setIntersectionMethod(l_intersectionMethod.get()); - // clear all contacts + // Remove all contact responses from the previous time step const type::vector& contacts = l_contactManager->getContacts(); for (const auto& contact : contacts) { @@ -124,6 +148,9 @@ void SubCollisionPipeline::computeCollisionReset() } } +/** + * @brief Performs collision detection in two phases: broad phase and narrow phase. + */ void SubCollisionPipeline::computeCollisionDetection() { SCOPED_TIMER_VARNAME(docollisiontimer, "doCollisionDetection"); @@ -133,43 +160,49 @@ void SubCollisionPipeline::computeCollisionDetection() 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 - + // Phase 1: Compute bounding volumes for all collision models + // These hierarchical structures enable efficient spatial queries type::vector vectBoundingVolume; { SCOPED_TIMER_VARNAME(bboxtimer, "ComputeBoundingTree"); + // Check if continuous collision detection (CCD) is enabled const bool continuous = l_intersectionMethod->useContinuous(); const auto continuousIntersectionType = l_intersectionMethod->continuousIntersectionType(); const SReal dt = getContext()->getDt(); int nActive = 0; + // Use full tree depth only if detection algorithms require it, otherwise use depth 0 const int used_depth = ( (l_broadPhaseDetection->needsDeepBoundingTree()) || (l_narrowPhaseDetection->needsDeepBoundingTree()) ) ? d_depth.getValue() : 0; + // Iterate through all linked collision models for (auto it = l_collisionModels.begin(); it != l_collisionModels.end(); ++it) { msg_info() << "doCollisionDetection, consider model" ; + // Skip inactive models if (!(*it)->isActive()) continue; if (continuous) { + // CCD: Compute swept bounding volumes that cover the motion trajectory const std::string msg = "Compute Continuous BoundingTree: " + (*it)->getName(); ScopedAdvancedTimer continuousBoundingTreeTimer(msg.c_str()); (*it)->computeContinuousBoundingTree(dt, continuousIntersectionType, used_depth); } else { + // Discrete: Compute bounding volumes at current positions std::string msg = "Compute BoundingTree: " + (*it)->getName(); ScopedAdvancedTimer boundingTreeTimer(msg.c_str()); (*it)->computeBoundingTree(used_depth); } + // getFirst() returns the root of the bounding tree hierarchy vectBoundingVolume.push_back ((*it)->getFirst()); ++nActive; } @@ -178,27 +211,34 @@ void SubCollisionPipeline::computeCollisionDetection() 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->addCollisionModels(vectBoundingVolume); // Actual detection happens here l_broadPhaseDetection->endBroadPhase(); l_intersectionMethod->endBroadPhase(); } + // Phase 3: Narrow Phase Detection + // Performs precise intersection tests on potentially colliding pairs msg_info() << "doCollisionDetection, NarrowPhaseDetection "<< l_narrowPhaseDetection->getName(); { SCOPED_TIMER_VARNAME(narrowphase, "NarrowPhase"); l_intersectionMethod->beginNarrowPhase(); l_narrowPhaseDetection->beginNarrowPhase(); + + // Get the pairs identified by broad phase const type::vector >& vectCMPair = l_broadPhaseDetection->getCollisionModelPairs(); msg_info() << "doCollisionDetection, "<< vectCMPair.size()<<" colliding model pairs" ; + // Perform precise intersection tests on each pair l_narrowPhaseDetection->addCollisionPairs(vectCMPair); l_narrowPhaseDetection->endNarrowPhase(); l_intersectionMethod->endNarrowPhase(); @@ -206,6 +246,9 @@ void SubCollisionPipeline::computeCollisionDetection() } +/** + * @brief Creates collision responses based on detected contacts. + */ void SubCollisionPipeline::computeCollisionResponse() { if (!this->isComponentStateValid()) @@ -215,17 +258,19 @@ void SubCollisionPipeline::computeCollisionResponse() msg_info() << "Create Contacts " << l_contactManager->getName() ; + // Create contact objects from narrow phase detection results { 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 + // Separate contacts into two categories based on whether they involve static objects type::vector notStaticContacts; + // Process contacts involving static (non-simulated) objects first + // These get their response attached to the simulated object's context { SCOPED_TIMER_VARNAME(createStaticObjectsResponseTimer, "CreateStaticObjectsResponse"); for (const auto& contact : contacts) @@ -233,19 +278,24 @@ void SubCollisionPipeline::computeCollisionResponse() const auto collisionModels = contact->getCollisionModels(); if (collisionModels.first != nullptr && !collisionModels.first->isSimulated()) { + // First model is static, attach response to second model's context contact->createResponse(collisionModels.second->getContext()); } else if (collisionModels.second != nullptr && !collisionModels.second->isSimulated()) { + // Second model is static, attach response to first model's context contact->createResponse(collisionModels.first->getContext()); } else { + // Both models are simulated, handle separately notStaticContacts.push_back(contact); } } } + // Process contacts between two simulated (moving) objects + // These get their response attached to the scene context SCOPED_TIMER_VARNAME(createResponseTimer, "CreateMovingObjectsResponse"); msg_info() << "Linking all contacts to Scene" ; @@ -257,6 +307,7 @@ void SubCollisionPipeline::computeCollisionResponse() } +/// Returns the list of collision models explicitly linked to this pipeline. std::vector SubCollisionPipeline::getCollisionModels() { std::vector collisionModels; 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 index ff535930df1..406d036b493 100644 --- 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 @@ -36,6 +36,28 @@ namespace sofa::component::collision::detection::algorithm { +/** + * @brief A self-contained collision pipeline for a specific set of collision models. + * + * SubCollisionPipeline implements a complete collision detection and response workflow + * for an explicitly defined subset of collision models. Unlike the standard CollisionPipeline + * which processes all collision models in the scene graph, this component only handles + * the collision models explicitly linked to it. + * + * This class is designed to be used as part of a CompositeCollisionPipeline, which can + * aggregate multiple SubCollisionPipelines to handle different groups of collision models + * independently (and potentially in parallel). + * + * Required components (via links): + * - At least one CollisionModel + * - An Intersection method (e.g., MinProximityIntersection, NewProximityIntersection) + * - A BroadPhaseDetection (e.g., BruteForceBroadPhase, BVHNarrowPhase) + * - A NarrowPhaseDetection (e.g., BVHNarrowPhase, DirectSAP) + * - A ContactManager (e.g., DefaultContactManager) + * + * @see CompositeCollisionPipeline + * @see BaseSubCollisionPipeline + */ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API SubCollisionPipeline : public BaseSubCollisionPipeline { public: @@ -45,22 +67,44 @@ class SOFA_COMPONENT_COLLISION_DETECTION_ALGORITHM_API SubCollisionPipeline : pu SubCollisionPipeline(); public: virtual ~SubCollisionPipeline() override = default; + + /// @brief Validates that all required components are linked and sets the component state accordingly. void doInit() override; + + /// @brief Event handling (currently no-op for this pipeline). void doHandleEvent(sofa::core::objectmodel::Event*) override {} + /// @brief Clears contact responses from the previous time step. void computeCollisionReset() override; + + /// @brief Performs collision detection: computes bounding trees, runs broad and narrow phase detection. void computeCollisionDetection() override; + + /// @brief Creates contact responses based on detected collisions. void computeCollisionResponse() override; - + + /// @brief Returns the list of collision models handled by this pipeline. std::vector getCollisionModels() override; - + + /// Maximum depth of bounding trees used in collision detection. sofa::Data d_depth; + + /// List of collision models to process in this pipeline. sofa::MultiLink < SubCollisionPipeline, sofa::core::CollisionModel, sofa::BaseLink::FLAG_DUPLICATE > l_collisionModels; + + /// Intersection method defining how to detect intersections between geometric primitives. sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::Intersection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_intersectionMethod; + + /// Contact manager responsible for creating and managing contact objects. sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::ContactManager, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_contactManager; + + /// Broad phase detection algorithm for quickly identifying potentially colliding pairs. sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::BroadPhaseDetection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_broadPhaseDetection; + + /// Narrow phase detection algorithm for precise intersection testing. sofa::SingleLink< SubCollisionPipeline, sofa::core::collision::NarrowPhaseDetection, sofa::BaseLink::FLAG_STOREPATH | sofa::BaseLink::FLAG_STRONGLINK > l_narrowPhaseDetection; + /// Default value for the bounding tree depth parameter. static inline constexpr unsigned int s_defaultDepthValue = 6; }; From 021e91bcb710fb74a10112ed09fda37108ee8a5b Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 4 Feb 2026 20:54:13 +0900 Subject: [PATCH 27/28] Update Sofa/Component/Collision/Detection/Algorithm/src/sofa/component/collision/detection/algorithm/CollisionPipeline.cpp (update msg) Co-authored-by: Alex Bilger --- .../collision/detection/algorithm/CollisionPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fe92271992c..bbf6cf31888 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 @@ -71,7 +71,7 @@ CollisionPipeline::CollisionPipeline() void CollisionPipeline::init() { - msg_info() << "CollisionPipeline is now a wrapper to CompositeCollisionPipeline with a single SubCollisionPipeline."; + msg_info() << "Since v26.06, CollisionPipeline is a wrapper to CompositeCollisionPipeline with a single SubCollisionPipeline."; msg_info() << "If you want more flexibility, use directly the components CompositeCollisionPipeline and SubCollisionPipeline, with their respective Data."; auto context = this->getContext(); From 20362c5db45f91f21cb75c3de667ae52ae37a5d5 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Thu, 5 Feb 2026 07:58:32 +0900 Subject: [PATCH 28/28] fix typo --- .../collision/detection/algorithm/SubCollisionPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 9cdb4479ac2..2e28949e6f8 100644 --- 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 @@ -48,7 +48,7 @@ void registerSubCollisionPipeline(sofa::core::ObjectFactory* factory) 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())) + , d_depth(initData(&d_depth, s_defaultDepthValue, "depth", ("Max depth of bounding trees. (default=" + std::to_string(s_defaultDepthValue) + ", min=?, max=?)").c_str())) , 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"))