From 4ff09aef1af5e0bb10541b1e2276c742d8d519ed Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 22 Dec 2025 16:01:53 +0900 Subject: [PATCH 01/10] add checker for collision consistency --- .../projects/SceneChecking/CMakeLists.txt | 2 + .../SceneCheckCollisionPipelineAndModels.cpp | 104 ++++++++++++++++++ .../SceneCheckCollisionPipelineAndModels.h | 54 +++++++++ 3 files changed, 160 insertions(+) create mode 100644 applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.cpp create mode 100644 applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.h diff --git a/applications/projects/SceneChecking/CMakeLists.txt b/applications/projects/SceneChecking/CMakeLists.txt index e0832b0ad65..f4ce6b70cab 100644 --- a/applications/projects/SceneChecking/CMakeLists.txt +++ b/applications/projects/SceneChecking/CMakeLists.txt @@ -12,6 +12,7 @@ set(HEADER_FILES ${SCENECHECK_SRC_DIR}/config.h.in ${SCENECHECK_SRC_DIR}/init.h ${SCENECHECK_SRC_DIR}/SceneCheckAPIChange.h + ${SCENECHECK_SRC_DIR}/SceneCheckCollisionPipelineAndModels.h ${SCENECHECK_SRC_DIR}/SceneCheckCollisionResponse.h ${SCENECHECK_SRC_DIR}/SceneCheckDeprecatedComponents.h ${SCENECHECK_SRC_DIR}/SceneCheckDuplicatedName.h @@ -26,6 +27,7 @@ set(HEADER_FILES set(SOURCE_FILES ${SCENECHECK_SRC_DIR}/init.cpp ${SCENECHECK_SRC_DIR}/SceneCheckAPIChange.cpp + ${SCENECHECK_SRC_DIR}/SceneCheckCollisionPipelineAndModels.cpp ${SCENECHECK_SRC_DIR}/SceneCheckCollisionResponse.cpp ${SCENECHECK_SRC_DIR}/SceneCheckDeprecatedComponents.cpp ${SCENECHECK_SRC_DIR}/SceneCheckDuplicatedName.cpp diff --git a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.cpp b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.cpp new file mode 100644 index 00000000000..80019e7099b --- /dev/null +++ b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.cpp @@ -0,0 +1,104 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#include "SceneCheckCollisionPipelineAndModels.h" + +#include +#include +#include +#include + +namespace sofa::_scenechecking_ +{ + +const bool SceneCheckCollisionPipelineAndModelsRegistered = sofa::simulation::SceneCheckMainRegistry::addToRegistry(SceneCheckCollisionPipelineAndModels::newSPtr()); + +using sofa::simulation::Node; + +const std::string SceneCheckCollisionPipelineAndModels::getName() +{ + return "SceneCheckCollisionPipelineAndModels"; +} + +const std::string SceneCheckCollisionPipelineAndModels::getDesc() +{ + return "Ensure the consistency of the existence of a collision pipeline, and collision models in the scene."; +} + +void SceneCheckCollisionPipelineAndModels::doInit(Node* node) +{ + SOFA_UNUSED(node); +} + +void SceneCheckCollisionPipelineAndModels::doCheckOn(Node* node) +{ + const sofa::core::objectmodel::BaseContext* root = node->getContext()->getRootContext(); + + if(!root) + { + return; + } + + sofa::core::collision::Pipeline::SPtr anyPipeline{}; + root->get(anyPipeline, sofa::core::objectmodel::BaseContext::SearchDirection::SearchDown); + sofa::core::CollisionModel::SPtr anyCollisionModel{}; + root->get(anyCollisionModel, sofa::core::objectmodel::BaseContext::SearchDirection::SearchDown); + + if(anyPipeline) + { + if(anyCollisionModel) + { + // there is a collision pipeline and (at least one) collision model(s), carry on. + } + else + { + // there is a collision pipeline but no collision model. + // Either the collision pipeline is superfluous; + // or the collision model(s) has been forgotten. + m_message = "There is no collision model in this scene, but there is a collision pipeline. Either add one collision model or remove the collision pipeline."; + } + } + else + { + if(anyCollisionModel) + { + // At least one collision model has been detected but without any pipeline. + // Either the collision pipeline has been forgotten; + // or the collision model(s) is useless. + m_message = "At least one collision model has been found, but there is no collision pipeline. You may add a collision pipeline (or remove the collision model if it is not used)."; + } + else + { + // there is no collision pipeline and no collision model, the scene certainly does not involve any collision feature. + } + } +} + +void SceneCheckCollisionPipelineAndModels::doPrintSummary() +{ + if(!m_message.empty()) + { + msg_warning(this->getName()) << m_message; + } +} + + +} // namespace sofa::_scenechecking_ diff --git a/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.h b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.h new file mode 100644 index 00000000000..0ce57d79d77 --- /dev/null +++ b/applications/projects/SceneChecking/src/SceneChecking/SceneCheckCollisionPipelineAndModels.h @@ -0,0 +1,54 @@ +/****************************************************************************** +* SOFA, Simulation Open-Framework Architecture * +* (c) 2006 INRIA, USTL, UJF, CNRS, MGH * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Authors: The SOFA Team and external contributors (see Authors.txt) * +* * +* Contact information: contact@sofa-framework.org * +******************************************************************************/ +#pragma once + +#include +#include + +#include +#include + +namespace sofa::_scenechecking_ +{ + +class SOFA_SCENECHECKING_API SceneCheckCollisionPipelineAndModels : public sofa::simulation::SceneCheck +{ +public: + virtual ~SceneCheckCollisionPipelineAndModels() {} + typedef std::shared_ptr SPtr; + static SPtr newSPtr() { return SPtr(new SceneCheckCollisionPipelineAndModels()); } + virtual const std::string getName() override; + virtual const std::string getDesc() override; + void doInit(sofa::simulation::Node* node) override; + void doCheckOn(sofa::simulation::Node* node) override; + void doPrintSummary() override; + +private: + std::string m_message; +}; + +} // namespace sofa::_scenechecking_ + +namespace sofa::scenechecking +{ + using _scenechecking_::SceneCheckCollisionPipelineAndModels; +} From 8b7f4114d62cfeb47598d66587bf3fb4f52d2bfc Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 22 Dec 2025 16:32:45 +0900 Subject: [PATCH 02/10] add unit tests --- .../SceneChecking/tests/SceneChecker_test.cpp | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/applications/projects/SceneChecking/tests/SceneChecker_test.cpp b/applications/projects/SceneChecking/tests/SceneChecker_test.cpp index 2ffe0b510c4..96c15f66513 100644 --- a/applications/projects/SceneChecking/tests/SceneChecker_test.cpp +++ b/applications/projects/SceneChecking/tests/SceneChecker_test.cpp @@ -38,6 +38,8 @@ using sofa::scenechecking::SceneCheckMissingRequiredPlugin; using sofa::scenechecking::SceneCheckDuplicatedName; #include using sofa::scenechecking::SceneCheckUsingAlias; +#include +using sofa::scenechecking::SceneCheckCollisionPipelineAndModels; #include using sofa::helper::system::PluginManager; @@ -238,6 +240,74 @@ struct SceneChecker_test : public BaseSimulationTest checker.validate(root.get(), &sceneLoader); } } + + void checkCollisionPipelineModels(bool withPipeline, bool withCollisionModel) + { + std::string scenePrefix = R"(" + + + + + + +)"; + std::string scenePipeline = R"(" + + + + + +)"; + std::string sceneModel = R"(" + + + + +)"; + std::string sceneSuffix = R"(" + +)"; + std::string scene = scenePrefix; + if(withPipeline) + { + scene += scenePipeline; + } + if(withCollisionModel) + { + scene += sceneModel; + } + scene += sceneSuffix; + + SceneCheckerVisitor checker(sofa::core::execparams::defaultInstance()); + checker.addCheck( SceneCheckCollisionPipelineAndModels::newSPtr() ); + + SceneLoaderXML sceneLoader; + const Node::SPtr root = sceneLoader.doLoadFromMemory("testscene", scene.c_str()); + ASSERT_NE(root.get(), nullptr); + root->init(sofa::core::execparams::defaultInstance()); + + if(!withPipeline && !withCollisionModel) + { + EXPECT_MSG_NOEMIT(Warning); + checker.validate(root.get(), &sceneLoader); + } + if(withPipeline && !withCollisionModel) + { + EXPECT_MSG_EMIT(Warning); + checker.validate(root.get(), &sceneLoader); + } + if(!withPipeline && withCollisionModel) + { + EXPECT_MSG_EMIT(Warning); + checker.validate(root.get(), &sceneLoader); + } + if(withPipeline && withCollisionModel) + { + EXPECT_MSG_NOEMIT(Warning); + checker.validate(root.get(), &sceneLoader); + } + + } }; TEST_F(SceneChecker_test, checkMissingRequiredPlugin ) @@ -279,3 +349,20 @@ TEST_F(SceneChecker_test, checkUsingAlias_withoutAlias ) { checkUsingAlias(false); } + +TEST_F(SceneChecker_test, checkCollisionPipelineModels_nothing ) +{ + checkCollisionPipelineModels(false, false); +} +TEST_F(SceneChecker_test, checkCollisionPipelineModels_onlyPipeline ) +{ + checkCollisionPipelineModels(true, false); +} +TEST_F(SceneChecker_test, checkCollisionPipelineModels_onlyModel ) +{ + checkCollisionPipelineModels(false, true); +} +TEST_F(SceneChecker_test, checkCollisionPipelineModels_both ) +{ + checkCollisionPipelineModels(true, true); +} From 21ed45598e53a960b6d642f216c6c3cc0d5e5f67 Mon Sep 17 00:00:00 2001 From: Frederick ROY Date: Tue, 9 Dec 2025 11:10:21 +0100 Subject: [PATCH 03/10] 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 18cdb3b947a09b73c89bba5960a445b765655d4e Mon Sep 17 00:00:00 2001 From: Frederick ROY Date: Tue, 9 Dec 2025 11:56:37 +0100 Subject: [PATCH 04/10] 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 e2bf69bee0142bdf231bcab8771cd241f30852eb Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 22 Dec 2025 15:08:31 +0900 Subject: [PATCH 05/10] 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 06/10] 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 90416bd7c8c5380a26b8fef4d3a41773d175e222 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Mon, 15 Dec 2025 09:37:22 +0900 Subject: [PATCH 07/10] 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 2a85d64bdeaae25af38ac95417bb75523ef5beeb Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Tue, 23 Dec 2025 14:13:33 +0900 Subject: [PATCH 08/10] 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 3078fc26c56..f9dc70e5871 100644 --- a/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp +++ b/Sofa/Component/Collision/Detection/Algorithm/tests/CollisionPipeline_test.cpp @@ -72,12 +72,14 @@ class TestCollisionPipeline : public BaseSimulationTest { void checkCollisionPipelineWithMissingBroadPhase(); void checkCollisionPipelineWithMissingNarrowPhase(); void checkCollisionPipelineWithMissingContactManager(); + void checkCollisionPipelineWithMissingCollisionModel(); int checkCollisionPipelineWithMonkeyValueForDepth(int value); void doSetUp() override { this->loadPlugins({ Sofa.Component.StateContainer, + Sofa.Component.Collision.Geometry, Sofa.Component.Collision.Detection.Algorithm, Sofa.Component.Collision.Detection.Intersection, Sofa.Component.Collision.Response.Contact @@ -104,6 +106,10 @@ void TestCollisionPipeline::checkCollisionPipelineWithNoAttributes() " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" " \n" ; root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str()); @@ -127,6 +133,10 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingIntersection() " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" " \n" ; root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str()); @@ -149,6 +159,10 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingBroadPhase() " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" " \n" ; root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str()); @@ -170,8 +184,12 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingNarrowPhase() " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" " \n" ; - + root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str()); ASSERT_NE(root.get(), nullptr) ; root->init(sofa::core::execparams::defaultInstance()) ; @@ -191,6 +209,34 @@ void TestCollisionPipeline::checkCollisionPipelineWithMissingContactManager() " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" + " \n" ; + + root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str()); + ASSERT_NE(root.get(), nullptr) ; + root->init(sofa::core::execparams::defaultInstance()) ; + + BaseObject* clp = root->getObject("pipeline") ; + ASSERT_NE(clp, nullptr) ; + +} + +void TestCollisionPipeline::checkCollisionPipelineWithMissingCollisionModel() +{ + EXPECT_MSG_EMIT(Warning) ; + EXPECT_MSG_NOEMIT(Error) ; + + std::stringstream scene ; + scene << " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" ; root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str()); @@ -212,6 +258,10 @@ int TestCollisionPipeline::checkCollisionPipelineWithMonkeyValueForDepth(int dva " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" " \n" ; root = SceneLoaderXML::loadFromMemory ("testscene", scene.str().c_str()); @@ -252,6 +302,12 @@ TEST_F(TestCollisionPipeline, checkCollisionPipelineWithMissingContactManager) this->checkCollisionPipelineWithMissingContactManager(); } +TEST_F(TestCollisionPipeline, checkCollisionPipelineWithMissingCollisionModel) +{ + this->checkCollisionPipelineWithMissingCollisionModel(); +} + + TEST_F(TestCollisionPipeline, checkCollisionPipelineWithMonkeyValueForDepth_OpenIssue) { const std::vector> testvalues = { From fed32d2df4806edaf37f36fd34e47ad24f6c6472 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 24 Dec 2025 15:40:28 +0900 Subject: [PATCH 09/10] 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 d1371d511f471b8a62aceb2c28f87a66633b4bb6 Mon Sep 17 00:00:00 2001 From: Frederick Roy Date: Wed, 7 Jan 2026 18:14:59 +0900 Subject: [PATCH 10/10] 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};