diff --git a/Gems/ROS2/Code/Source/RobotImporter/FixURDF/FixURDF.cpp b/Gems/ROS2/Code/Source/RobotImporter/FixURDF/FixURDF.cpp new file mode 100644 index 000000000..146ce38c7 --- /dev/null +++ b/Gems/ROS2/Code/Source/RobotImporter/FixURDF/FixURDF.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "FixURDF.h" +#include +#include +#include +#include + +namespace ROS2::Utils +{ + //! Modifies a parsed URDF in memory to add missing inertia to links, which prevents SDF error 19. + //! @param urdf URDF to modify. + //! @returns a list of names of links that were modified. + AZStd::vector AddMissingInertiaToLinks(AZ::rapidxml::xml_node<>* urdf) + { + AZStd::vector modifiedLinks; + using namespace AZ::rapidxml; + AZStd::vector*> links; + for (xml_node<>* link = urdf->first_node("link"); link; link = link->next_sibling("link")) + { + if (!link->first_node("inertial")) + { + links.push_back(link); + } + } + + for (auto* link : links) + { + xml_node<>* inertial = urdf->document()->allocate_node(node_element, "inertial"); + + xml_node<>* mass = urdf->document()->allocate_node(node_element, "mass"); + mass->append_attribute(urdf->document()->allocate_attribute("value", "1.")); + inertial->append_node(mass); + xml_node<>* inertia = urdf->document()->allocate_node(node_element, "inertia"); + + inertia->append_attribute(urdf->document()->allocate_attribute("ixx", "1.")); + inertia->append_attribute(urdf->document()->allocate_attribute("ixy", "0.")); + inertia->append_attribute(urdf->document()->allocate_attribute("ixz", "0.")); + + inertia->append_attribute(urdf->document()->allocate_attribute("iyx", "0.")); + inertia->append_attribute(urdf->document()->allocate_attribute("iyy", "1.")); + inertia->append_attribute(urdf->document()->allocate_attribute("iyz", "0.")); + + inertia->append_attribute(urdf->document()->allocate_attribute("izx", "0.")); + inertia->append_attribute(urdf->document()->allocate_attribute("izy", "0.")); + inertia->append_attribute(urdf->document()->allocate_attribute("izz", "1.")); + + inertial->append_node(inertia); + + xml_node<>* origin = urdf->document()->allocate_node(node_element, "origin"); + origin->append_attribute(urdf->document()->allocate_attribute("xyz", "0. 0. 0.")); + inertial->append_node(origin); + + auto* name = link->first_attribute("name"); + if (name) + { + modifiedLinks.push_back(name->value()); + } + link->append_node(inertial); + } + + return modifiedLinks; + } + + //! Handles a case of multiple joints and the link sharing a common names which causes SDF error2 (but is fine in URDF) + //! Function will add a suffix "_dup" to the name of the joint if it is also the name of a link. + //! If there are name collisions in links, this will not be able to fix it, the SDF parser will throw an error. + //! @param urdf URDF to modify. + //! @returns a list of links that were modified + AZStd::vector RenameDuplicatedJoints(AZ::rapidxml::xml_node<>* urdf) + { + using namespace AZ::rapidxml; + AZStd::vector modifiedLinks; + AZStd::unordered_map linkAndJointsName; + for (xml_node<>* link = urdf->first_node("link"); link; link = link->next_sibling("link")) + { + auto* name = link->first_attribute("name"); + if (name) + { + linkAndJointsName.insert(AZStd::make_pair(name->value(), 0)); + } + } + for (xml_node<>* joint = urdf->first_node("joint"); joint; joint = joint->next_sibling("joint")) + { + auto* name = joint->first_attribute("name"); + if (name) + { + if (linkAndJointsName.contains(name->value())) + { + unsigned int& count = linkAndJointsName[name->value()]; + auto newName = AZStd::string::format("%s_dup%u", name->value(), count); + name->value(urdf->document()->allocate_string(newName.c_str())); + count++; + modifiedLinks.push_back(AZStd::move(newName)); + } + else + { + linkAndJointsName.insert(AZStd::make_pair(name->value(), 0)); + } + } + } + return modifiedLinks; + } + + AZStd::pair> ModifyURDFInMemory(const std::string& data) + { + AZStd::vector modifiedElements; + using namespace AZ::rapidxml; + xml_document<> doc; + doc.parse<0>(const_cast(data.c_str())); + xml_node<>* urdf = doc.first_node("robot"); + auto links = AddMissingInertiaToLinks(urdf); + modifiedElements.insert(modifiedElements.end(), AZStd::make_move_iterator(links.begin()), AZStd::make_move_iterator(links.end())); + + auto renames = RenameDuplicatedJoints(urdf); + modifiedElements.insert( + modifiedElements.end(), AZStd::make_move_iterator(renames.begin()), AZStd::make_move_iterator(renames.end())); + + std::string xmlDocString; + AZ::rapidxml::print(std::back_inserter(xmlDocString), *urdf, 0); + return { xmlDocString, modifiedElements }; + } +} // namespace ROS2::Utils diff --git a/Gems/ROS2/Code/Source/RobotImporter/FixURDF/FixURDF.h b/Gems/ROS2/Code/Source/RobotImporter/FixURDF/FixURDF.h new file mode 100644 index 000000000..73a77c021 --- /dev/null +++ b/Gems/ROS2/Code/Source/RobotImporter/FixURDF/FixURDF.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace ROS2::Utils +{ + //! Modifies in memory URDF to increase chance of successful conversion to SDF. + //! It does the following: + //! - Adds missing inertia to links of mass 1 kg and identity inertia matrix. + //! - Renames joints that have the same name as a link. + //! @param urdf URDF to modify. + //! @returns a modified URDF and a list of XML element that were modified + AZStd::pair> ModifyURDFInMemory(const std::string& data); +} // namespace ROS2::Utils diff --git a/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.cpp b/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.cpp index 2c68a830e..16a2a014a 100644 --- a/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.cpp +++ b/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.cpp @@ -14,6 +14,7 @@ namespace ROS2 CheckUrdfPage::CheckUrdfPage(QWizard* parent) : QWizardPage(parent) , m_success(false) + , m_warning(false) { m_log = new QTextEdit(this); setTitle(tr("URDF opening results:")); @@ -24,10 +25,11 @@ namespace ROS2 setLayout(layout); } - void CheckUrdfPage::ReportURDFResult(const QString& status, bool isSuccess) + void CheckUrdfPage::ReportURDFResult(const QString& status, bool isSuccess, bool isWarning) { m_log->setMarkdown(status); m_success = isSuccess; + m_warning = isWarning; emit completeChanged(); } @@ -35,4 +37,9 @@ namespace ROS2 { return m_success; } + + bool CheckUrdfPage::isWarning() const + { + return m_warning; + } } // namespace ROS2 diff --git a/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.h b/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.h index 7877f4af3..293ffc09e 100644 --- a/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.h +++ b/Gems/ROS2/Code/Source/RobotImporter/Pages/CheckUrdfPage.h @@ -22,12 +22,14 @@ namespace ROS2 Q_OBJECT public: explicit CheckUrdfPage(QWizard* parent); - void ReportURDFResult(const QString& result, bool isSuccess); + void ReportURDFResult(const QString& result, bool isSuccess, bool isWarning = false); bool isComplete() const override; + bool isWarning() const; private: QTextEdit* m_log; QString m_fileName; bool m_success; + bool m_warning; }; } // namespace ROS2 diff --git a/Gems/ROS2/Code/Source/RobotImporter/ROS2RobotImporterEditorSystemComponent.cpp b/Gems/ROS2/Code/Source/RobotImporter/ROS2RobotImporterEditorSystemComponent.cpp index 65d6bf566..61506ee28 100644 --- a/Gems/ROS2/Code/Source/RobotImporter/ROS2RobotImporterEditorSystemComponent.cpp +++ b/Gems/ROS2/Code/Source/RobotImporter/ROS2RobotImporterEditorSystemComponent.cpp @@ -106,7 +106,7 @@ namespace ROS2 sdf::ParserConfig parserConfig; parserConfig.URDFSetPreserveFixedJoint(sdfBuilderSettings.m_urdfPreserveFixedJoints); - auto parsedUrdfOutcome = UrdfParser::ParseFromFile(filePath, parserConfig); + auto parsedUrdfOutcome = UrdfParser::ParseFromFile(filePath, parserConfig, sdfBuilderSettings); if (!parsedUrdfOutcome) { const AZStd::string log = Utils::JoinSdfErrorsToString(parsedUrdfOutcome.GetSdfErrors()); diff --git a/Gems/ROS2/Code/Source/RobotImporter/RobotImporterWidget.cpp b/Gems/ROS2/Code/Source/RobotImporter/RobotImporterWidget.cpp index 9c4783c91..db6b93348 100644 --- a/Gems/ROS2/Code/Source/RobotImporter/RobotImporterWidget.cpp +++ b/Gems/ROS2/Code/Source/RobotImporter/RobotImporterWidget.cpp @@ -12,15 +12,15 @@ #include #include "RobotImporterWidget.h" +#include +#include +#include #include #include #include +#include #include #include -#include -#include -#include -#include namespace ROS2 { @@ -119,8 +119,7 @@ namespace ROS2 report += "```\n"; if (outcome.m_logErrorOutput.size()) { - report += - QString::fromUtf8(outcome.m_logErrorOutput.data(), static_cast(outcome.m_logErrorOutput.size())); + report += QString::fromUtf8(outcome.m_logErrorOutput.data(), static_cast(outcome.m_logErrorOutput.size())); } else { @@ -131,8 +130,8 @@ namespace ROS2 report += "```\n"; if (outcome.m_logStandardOutput.size()) { - report += QString::fromUtf8( - outcome.m_logStandardOutput.data(), static_cast(outcome.m_logStandardOutput.size())); + report += + QString::fromUtf8(outcome.m_logStandardOutput.data(), static_cast(outcome.m_logStandardOutput.size())); } else { @@ -147,21 +146,36 @@ namespace ROS2 else if (Utils::IsFileUrdf(m_urdfPath)) { // standard URDF - parsedUrdfOutcome = UrdfParser::ParseFromFile(m_urdfPath, parserConfig); + parsedUrdfOutcome = UrdfParser::ParseFromFile(m_urdfPath, parserConfig, sdfBuilderSettings); } else { AZ_Assert(false, "Unknown file extension : %s \n", m_urdfPath.c_str()); } AZStd::string log; - bool urdfParsedSuccess{ parsedUrdfOutcome }; + const bool urdfParsedSuccess{ parsedUrdfOutcome }; + const bool urdfParsedWithWarnings{ parsedUrdfOutcome.UrdfParsedWithModifiedContent() }; if (urdfParsedSuccess) { + if (urdfParsedWithWarnings) + { + report += "# " + tr("The URDF was parsed, though results were modified to be compatible with SDFormat") + "\n"; + report += tr("Modified tags in URDF:") + "\n"; + for (const auto& modifiedTag : parsedUrdfOutcome.m_modifiedURDFTags) + { + report += " - " + QString::fromUtf8(modifiedTag.data(), static_cast(modifiedTag.size())) + "\n"; + } + report += "\n# "+tr("The modified URDF code:") + "\n"; + report += "```\n" + QString::fromStdString(parsedUrdfOutcome.m_modifiedURDFContent) + "```\n"; + } + else + { + report += "# " + tr("The URDF was parsed and opened successfully") + "\n"; + AZ_Printf("Wizard", "Wizard skips m_checkUrdfPage since there is no errors in URDF\n"); + } m_parsedUrdf = AZStd::move(parsedUrdfOutcome.GetRoot()); - report += "# " + tr("The URDF was parsed and opened successfully") + "\n"; m_prefabMaker.reset(); // Report the status of skipping this page - AZ_Printf("Wizard", "Wizard skips m_checkUrdfPage since there is no errors in URDF\n"); m_meshNames = Utils::GetMeshesFilenames(&m_parsedUrdf, true, true); m_assetPage->ClearAssetsList(); } @@ -177,6 +191,7 @@ namespace ROS2 report += QString::fromUtf8(log.data(), int(log.size())); report += "`"; } + m_checkUrdfPage->ReportURDFResult(report, urdfParsedSuccess, urdfParsedWithWarnings); if (parsedUrdfOutcome.m_parseMessages.size() > 0) { report += "\n\n"; @@ -358,6 +373,10 @@ namespace ROS2 { if ((currentPage() == m_fileSelectPage && m_params.empty()) || currentPage() == m_xacroParamsPage) { + if (m_parsedUrdf.Model() != nullptr && !m_checkUrdfPage->isWarning()) + { + return m_xacroParamsPage->nextId(); + } if (m_parsedUrdf.Model() != nullptr && m_checkUrdfPage->isComplete()) { if (m_meshNames.size() == 0) diff --git a/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.cpp b/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.cpp index aa3a7b79f..effede711 100644 --- a/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.cpp +++ b/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.cpp @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include namespace ROS2::UrdfParser { @@ -26,8 +28,7 @@ namespace ROS2::UrdfParser // @param consoleStream Reference to sdf::Console::ConsoleStream whose // output will be redirected // @param redirectStream reference to stream where console stream output is redirected to - RedirectSDFOutputStream(sdf::Console::ConsoleStream& consoleStream, - std::ostream& redirectStream) + RedirectSDFOutputStream(sdf::Console::ConsoleStream& consoleStream, std::ostream& redirectStream) : m_consoleStreamRef(consoleStream) , m_origConsoleStream(consoleStream) { @@ -51,7 +52,7 @@ namespace ROS2::UrdfParser { return m_root; } - const sdf::Root& ParseResult::GetRoot() const & + const sdf::Root& ParseResult::GetRoot() const& { return m_root; } @@ -85,6 +86,11 @@ namespace ROS2::UrdfParser return m_sdfErrors.empty(); } + bool ParseResult::UrdfParsedWithModifiedContent() const + { + return m_modifiedURDFTags.size() > 0; + } + RootObjectOutcome Parse(AZStd::string_view xmlString, const sdf::ParserConfig& parserConfig) { return Parse(std::string(xmlString.data(), xmlString.size()), parserConfig); @@ -110,7 +116,8 @@ namespace ROS2::UrdfParser return parseResult; } - RootObjectOutcome ParseFromFile(AZ::IO::PathView filePath, const sdf::ParserConfig& parserConfig) + RootObjectOutcome ParseFromFile( + AZ::IO::PathView filePath, const sdf::ParserConfig& parserConfig, const SdfAssetBuilderSettings& settings) { // Store path in a AZ::IO::FixedMaxPath which is stack based structure that provides memory // for the path string and is null terminated. @@ -121,13 +128,26 @@ namespace ROS2::UrdfParser { auto fileNotFoundMessage = AZStd::fixed_string<1024>::format("File %.*s does not exist", AZ_PATH_ARG(urdfFilePath)); ParseResult fileNotFoundResult; - fileNotFoundResult.m_sdfErrors.emplace_back(sdf::ErrorCode::FILE_READ, - std::string{ fileNotFoundMessage.c_str(), fileNotFoundMessage.size() }, - std::string{ urdfFilePath.c_str(), urdfFilePath.Native().size() }); + fileNotFoundResult.m_sdfErrors.emplace_back( + sdf::ErrorCode::FILE_READ, + std::string{ fileNotFoundMessage.c_str(), fileNotFoundMessage.size() }, + std::string{ urdfFilePath.c_str(), urdfFilePath.Native().size() }); return fileNotFoundResult; } std::string xmlStr((std::istreambuf_iterator(istream)), std::istreambuf_iterator()); + if (Utils::IsFileUrdf(filePath) && settings.m_fixURDF) + { + // modify in memory + auto [modifiedXmlStr, modifiedElements] = (ROS2::Utils::ModifyURDFInMemory(xmlStr)); + + auto result = Parse(modifiedXmlStr, parserConfig); + result.m_modifiedURDFTags = AZStd::move(modifiedElements); + result.m_modifiedURDFContent = AZStd::move(modifiedXmlStr); + return result; + } return Parse(xmlStr, parserConfig); } -} // namespace ROS2 + + +} // namespace ROS2::UrdfParser diff --git a/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.h b/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.h index 376f120cd..e929647c7 100644 --- a/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.h +++ b/Gems/ROS2/Code/Source/RobotImporter/URDF/UrdfParser.h @@ -9,17 +9,19 @@ #pragma once #include +#include #include -#include +#include +#include +#include +#include +#include #include -#include #include +#include #include -#include -#include +#include #include -#include -#include namespace ROS2 { @@ -33,8 +35,10 @@ namespace ROS2 private: // Provides custom sdf::ErrorCode values when parsing is done through O3DE inline static constexpr auto O3DESdfErrorCodeStart = static_cast(1000); + public: - inline static constexpr auto O3DESdfErrorParseNotStarted = static_cast(static_cast(O3DESdfErrorCodeStart) + 1); + inline static constexpr auto O3DESdfErrorParseNotStarted = + static_cast(static_cast(O3DESdfErrorCodeStart) + 1); //! Ref qualifier overloads for retrieving sdf::Root //! it supports a non-const lvalue overload to allow @@ -61,9 +65,18 @@ namespace ROS2 //! Returns if the parsing of the SDF file has succeeded explicit operator bool() const; + //! Returns if the URDF content was modified during parsing + bool UrdfParsedWithModifiedContent() const; + sdf::Root m_root; AZStd::string m_parseMessages; - sdf::Errors m_sdfErrors{ sdf::Error{ O3DESdfErrorParseNotStarted, std::string{"No Parsing has occurred yet"}} }; + sdf::Errors m_sdfErrors{ sdf::Error{ O3DESdfErrorParseNotStarted, std::string{ "No Parsing has occurred yet" } } }; + + //! Stores the modified URDF content after parsing, empty if no modification occurred + std::string m_modifiedURDFContent; + + //! Stores the modified URDF tags after parsing, empty if no modification occurred + AZStd::vector m_modifiedURDFTags; }; using RootObjectOutcome = ParseResult; @@ -83,7 +96,9 @@ namespace ROS2 //! The relevant ParserConfig functions for URDF importing are //! URDFPreserveFixedJoint() function to prevent merging of robot links bound by fixed joint //! AddURIPath() function to provide a mapping of package:// and model:// references to the local filesystem + //! @param settings structure that contains configuration options for the SDFAssetBuilder //! @return SDF root object containing parsed or tags - RootObjectOutcome ParseFromFile(AZ::IO::PathView filePath, const sdf::ParserConfig& parserConfig); + RootObjectOutcome ParseFromFile( + AZ::IO::PathView filePath, const sdf::ParserConfig& parserConfig, const SdfAssetBuilderSettings& settings); }; // namespace UrdfParser } // namespace ROS2 diff --git a/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilder.cpp b/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilder.cpp index c47336d21..e70461ccb 100644 --- a/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilder.cpp +++ b/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilder.cpp @@ -194,7 +194,7 @@ namespace ROS2 parserConfig.URDFSetPreserveFixedJoint(m_globalSettings.m_urdfPreserveFixedJoints); AZ_Info(SdfAssetBuilderName, "Parsing source file: %s", fullSourcePath.c_str()); - auto parsedSdfRootOutcome = UrdfParser::ParseFromFile(fullSourcePath, parserConfig); + auto parsedSdfRootOutcome = UrdfParser::ParseFromFile(fullSourcePath, parserConfig, m_globalSettings); if (!parsedSdfRootOutcome) { const AZStd::string sdfParseErrors = Utils::JoinSdfErrorsToString(parsedSdfRootOutcome.GetSdfErrors()); @@ -257,7 +257,7 @@ namespace ROS2 // Read in and parse the source SDF file. AZ_Info(SdfAssetBuilderName, "Parsing source file: %s", request.m_fullPath.c_str()); - auto parsedSdfRootOutcome = UrdfParser::ParseFromFile(AZ::IO::PathView(request.m_fullPath), parserConfig); + auto parsedSdfRootOutcome = UrdfParser::ParseFromFile(AZ::IO::PathView(request.m_fullPath), parserConfig, m_globalSettings); if (!parsedSdfRootOutcome) { const AZStd::string sdfParseErrors = Utils::JoinSdfErrorsToString(parsedSdfRootOutcome.GetSdfErrors()); diff --git a/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.cpp b/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.cpp index 2484566d7..a5021ca53 100644 --- a/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.cpp +++ b/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.cpp @@ -51,6 +51,7 @@ namespace ROS2 constexpr auto SdfAssetBuilderUseArticulationsRegistryKey = SDFSettingsRootKey("UseArticulations"); constexpr auto SdfAssetBuilderURDFPreserveFixedJointRegistryKey = SDFSettingsRootKey("URDFPreserveFixedJoint"); constexpr auto SdfAssetBuilderImportMeshesJointRegistryKey = SDFSettingsRootKey("ImportMeshes"); + constexpr auto SdfAssetBuilderFixURDFRegistryKey = SDFSettingsRootKey("FixURDF"); } void SdfAssetBuilderSettings::Reflect(AZ::ReflectContext* context) @@ -62,6 +63,7 @@ namespace ROS2 ->Field("UseArticulations", &SdfAssetBuilderSettings::m_useArticulations) ->Field("URDFPreserveFixedJoint", &SdfAssetBuilderSettings::m_urdfPreserveFixedJoints) ->Field("ImportReferencedMeshFiles", &SdfAssetBuilderSettings::m_importReferencedMeshFiles) + ->Field("FixURDF", &SdfAssetBuilderSettings::m_fixURDF) // m_builderPatterns aren't serialized because we only use the serialization // to detect when global settings changes cause us to rebuild our assets. @@ -90,7 +92,13 @@ namespace ROS2 AZ::Edit::UIHandlers::Default, &SdfAssetBuilderSettings::m_importReferencedMeshFiles, "Import meshes", - "Allows importing of referenced mesh content files such as .dae or .stl files when importing the URDF/SDF."); + "Allows importing of referenced mesh content files such as .dae or .stl files when importing the URDF/SDF.") + ->DataElement( + AZ::Edit::UIHandlers::Default, + &SdfAssetBuilderSettings::m_fixURDF, + "Fix URDF to be compatible with libsdformat", + "When set, fixes the URDF file before importing it. This is useful for fixing URDF files that have missing inertials or duplicate names within links and joints." + ); } } } @@ -113,6 +121,9 @@ namespace ROS2 // Query the import references meshes option from the Settings Registry to determine if mesh source assets are copied settingsRegistry->Get(m_importReferencedMeshFiles, SdfAssetBuilderImportMeshesJointRegistryKey); + // Query the fix URDF option from the Settings Registry to determine if the URDF file should be fixed before importing + settingsRegistry->Get(m_fixURDF, SdfAssetBuilderFixURDFRegistryKey); + // Visit each supported file type extension and create an asset builder wildcard pattern for it. auto VisitFileTypeExtensions = [&settingsRegistry, this] (const AZ::SettingsRegistryInterface::VisitArgs& visitArgs) diff --git a/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.h b/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.h index 04f02dbb8..f5858dafd 100644 --- a/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.h +++ b/Gems/ROS2/Code/Source/SdfAssetBuilder/SdfAssetBuilderSettings.h @@ -36,5 +36,7 @@ namespace ROS2 bool m_urdfPreserveFixedJoints = true; // When true, .dae/.stl mesh files are imported into the project folder to allow the AP to process them bool m_importReferencedMeshFiles = true; + // When true URDF will be fixed to be compatible with SDFormat. + bool m_fixURDF = true; }; } // namespace ROS2 diff --git a/Gems/ROS2/Code/ros2_editor_files.cmake b/Gems/ROS2/Code/ros2_editor_files.cmake index 2314f02fb..c1305540e 100644 --- a/Gems/ROS2/Code/ros2_editor_files.cmake +++ b/Gems/ROS2/Code/ros2_editor_files.cmake @@ -12,6 +12,8 @@ set(FILES Source/Lidar/LidarRegistrarEditorSystemComponent.h Source/Manipulation/JointsManipulationEditorComponent.cpp Source/Manipulation/JointsManipulationEditorComponent.h + Source/RobotImporter/FixURDF/FixURDF.cpp + Source/RobotImporter/FixURDF/FixURDF.h Source/RobotImporter/Pages/CheckAssetPage.cpp Source/RobotImporter/Pages/CheckAssetPage.h Source/RobotImporter/Pages/CheckUrdfPage.cpp