From eb74c6794559ad4e72d8f45324c3556818d1f273 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Thu, 22 Feb 2024 23:07:15 +0000 Subject: [PATCH 01/11] resolve URI wrt to file path Signed-off-by: Ian Chen --- include/sdf/ParserConfig.hh | 14 +++++++++++++- src/Mesh.cc | 4 +++- src/ParserConfig.cc | 20 ++++++++++++++++++++ src/SDF.cc | 11 +++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/include/sdf/ParserConfig.hh b/include/sdf/ParserConfig.hh index ad8195fe5..b396a56b1 100644 --- a/include/sdf/ParserConfig.hh +++ b/include/sdf/ParserConfig.hh @@ -179,6 +179,10 @@ class SDFORMAT_VISIBLE ParserConfig /// and merge the child link into the parent. public: bool URDFPreserveFixedJoint() const; + /// \brief Set the storeResolvedURIs flag value. + /// \sa SetStoreResolvedURIs + public: void SetStoreResovledURIs(bool _resolveURI); + /// \brief Set the storeResolvedURIs flag value. /// \param[in] _resolveURI True to make the parser attempt to resolve any /// URIs found and store them. False to preserve original URIs @@ -188,13 +192,21 @@ class SDFORMAT_VISIBLE ParserConfig /// If the FindFileCallback provides a non-empty string, the URI will be /// stored in the DOM object, and the original (unresolved) URI will be /// stored in the underlying Element. - public: void SetStoreResovledURIs(bool _resolveURI); + public: void SetStoreResolvedURIs(bool _resolveURI); /// \brief Get the storeResolvedURIs flag value. /// \return True if the parser will attempt to resolve any URIs found and /// store them. False to preserve original URIs public: bool StoreResolvedURIs() const; + /// \brief Set the path to the SDF file. + /// \param[in] _path Full path to SDF file. + public: void SetFilePath(const std::string &_path); + + /// \brief Get the path to the SDF file. + /// \return Full path to SDF file. + public: const std::string &FilePath() const; + /// \brief Private data pointer. GZ_UTILS_IMPL_PTR(dataPtr) }; diff --git a/src/Mesh.cc b/src/Mesh.cc index 25b1d1ac0..bca5817e3 100644 --- a/src/Mesh.cc +++ b/src/Mesh.cc @@ -82,9 +82,11 @@ Errors Mesh::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("uri")) { + auto config = _config; + config.SetFilePath(this->dataPtr->filePath); this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, - _config, errors); + config, errors); } else { diff --git a/src/ParserConfig.cc b/src/ParserConfig.cc index d6282383a..56b18a077 100644 --- a/src/ParserConfig.cc +++ b/src/ParserConfig.cc @@ -56,6 +56,9 @@ class sdf::ParserConfig::Implementation /// \brief Flag to expand URIs where possible store the resolved paths public: bool storeResolvedURIs = false; + + /// \brief Path to file where this sdf is loaded + public: std::string filePath; }; @@ -183,6 +186,12 @@ bool ParserConfig::URDFPreserveFixedJoint() const ///////////////////////////////////////////////// void ParserConfig::SetStoreResovledURIs(bool _resolveURI) +{ + this->SetStoreResolvedURIs(_resolveURI); +} + +///////////////////////////////////////////////// +void ParserConfig::SetStoreResolvedURIs(bool _resolveURI) { this->dataPtr->storeResolvedURIs = _resolveURI; } @@ -193,3 +202,14 @@ bool ParserConfig::StoreResolvedURIs() const return this->dataPtr->storeResolvedURIs; } +///////////////////////////////////////////////// +void ParserConfig::SetFilePath(const std::string &_path) +{ + this->dataPtr->filePath = _path; +} + +///////////////////////////////////////////////// +const std::string &ParserConfig::FilePath() const +{ + return this->dataPtr->filePath; +} diff --git a/src/SDF.cc b/src/SDF.cc index 7a43d0f9a..62331b9e3 100644 --- a/src/SDF.cc +++ b/src/SDF.cc @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -115,6 +116,16 @@ std::string findFile(const std::string &_filename, bool _searchLocalPath, { return path; } + else + { + auto p = std::filesystem::path(_config.FilePath()).parent_path(); + p = p / filename; + path = p.string(); + if (sdf::filesystem::exists(path)) + { + return path; + } + } } // Next check the install path. From 0314f800003f3cf777854af4687b2fbde45536ad Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Thu, 22 Feb 2024 23:12:34 +0000 Subject: [PATCH 02/11] check empty filepath Signed-off-by: Ian Chen --- src/SDF.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SDF.cc b/src/SDF.cc index 62331b9e3..d95ac5c54 100644 --- a/src/SDF.cc +++ b/src/SDF.cc @@ -116,7 +116,7 @@ std::string findFile(const std::string &_filename, bool _searchLocalPath, { return path; } - else + else if (!_config.FilePath().empty()) { auto p = std::filesystem::path(_config.FilePath()).parent_path(); p = p / filename; From a83998cd64d0055c5feeb1063edf9608fea9596f Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Fri, 23 Feb 2024 19:46:49 +0000 Subject: [PATCH 03/11] update function name to SetRelativeURISearchPath, add tests Signed-off-by: Ian Chen --- include/sdf/ParserConfig.hh | 12 ++--- src/Heightmap.cc | 8 +++- src/Material.cc | 9 +++- src/Mesh.cc | 5 +- src/ParserConfig.cc | 12 ++--- src/SDF.cc | 8 ++-- src/Sky.cc | 7 ++- test/integration/resolve_uris.cc | 65 ++++++++++++++++++++++++++ test/sdf/media/dummy_cubemap.dds | 0 test/sdf/media/dummy_material.material | 0 test/sdf/media/dummy_mesh.dae | 0 test/sdf/resolve_relative_uris.sdf | 54 +++++++++++++++++++++ 12 files changed, 157 insertions(+), 23 deletions(-) create mode 100644 test/sdf/media/dummy_cubemap.dds create mode 100644 test/sdf/media/dummy_material.material create mode 100644 test/sdf/media/dummy_mesh.dae create mode 100644 test/sdf/resolve_relative_uris.sdf diff --git a/include/sdf/ParserConfig.hh b/include/sdf/ParserConfig.hh index b396a56b1..a8de279d0 100644 --- a/include/sdf/ParserConfig.hh +++ b/include/sdf/ParserConfig.hh @@ -199,13 +199,13 @@ class SDFORMAT_VISIBLE ParserConfig /// store them. False to preserve original URIs public: bool StoreResolvedURIs() const; - /// \brief Set the path to the SDF file. - /// \param[in] _path Full path to SDF file. - public: void SetFilePath(const std::string &_path); + /// \brief Set the path to search for URIs with relative path. + /// \param[in] _path Path to search. + public: void SetRelativeURISearchPath(const std::string &_path); - /// \brief Get the path to the SDF file. - /// \return Full path to SDF file. - public: const std::string &FilePath() const; + /// \brief Get the path to search for URIs with relative path. + /// \return Path to search + public: const std::string &RelativeURISearchPath() const; /// \brief Private data pointer. GZ_UTILS_IMPL_PTR(dataPtr) diff --git a/src/Heightmap.cc b/src/Heightmap.cc index 5c7b1a8b6..6c00d73bf 100644 --- a/src/Heightmap.cc +++ b/src/Heightmap.cc @@ -15,6 +15,7 @@ * */ +#include #include #include "Utils.hh" @@ -324,11 +325,14 @@ Errors Heightmap::Load(ElementPtr _sdf, const ParserConfig &_config) return errors; } + auto config = _config; + auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); + config.SetRelativeURISearchPath(path.string()); if (_sdf->HasElement("uri")) { this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, - _config, errors); + config, errors); } else { @@ -349,7 +353,7 @@ Errors Heightmap::Load(ElementPtr _sdf, const ParserConfig &_config) this->dataPtr->sampling).first; Errors textureLoadErrors = loadRepeated(_sdf, - "texture", this->dataPtr->textures, _config); + "texture", this->dataPtr->textures, config); errors.insert(errors.end(), textureLoadErrors.begin(), textureLoadErrors.end()); diff --git a/src/Material.cc b/src/Material.cc index 0a0b77c42..d6cb19403 100644 --- a/src/Material.cc +++ b/src/Material.cc @@ -14,8 +14,10 @@ * limitations under the License. * */ -#include + +#include #include +#include #include #include @@ -122,7 +124,10 @@ Errors Material::Load(sdf::ElementPtr _sdf, const sdf::ParserConfig &_config) " element is empty."}); } - this->dataPtr->scriptUri = resolveURI(uriPair.first, _config, errors); + auto config = _config; + auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); + config.SetRelativeURISearchPath(path.string()); + this->dataPtr->scriptUri = resolveURI(uriPair.first, config, errors); std::pair namePair = elem->Get(errors, "name", ""); diff --git a/src/Mesh.cc b/src/Mesh.cc index bca5817e3..ebd7269d3 100644 --- a/src/Mesh.cc +++ b/src/Mesh.cc @@ -14,6 +14,8 @@ * limitations under the License. * */ +#include + #include "sdf/parser.hh" #include "sdf/Mesh.hh" #include "Utils.hh" @@ -83,7 +85,8 @@ Errors Mesh::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("uri")) { auto config = _config; - config.SetFilePath(this->dataPtr->filePath); + auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); + config.SetRelativeURISearchPath(path.string()); this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, config, errors); diff --git a/src/ParserConfig.cc b/src/ParserConfig.cc index 56b18a077..732a6fae2 100644 --- a/src/ParserConfig.cc +++ b/src/ParserConfig.cc @@ -57,8 +57,8 @@ class sdf::ParserConfig::Implementation /// \brief Flag to expand URIs where possible store the resolved paths public: bool storeResolvedURIs = false; - /// \brief Path to file where this sdf is loaded - public: std::string filePath; + /// \brief Path to search for URIs with relative paths + public: std::string relativeUriSearchPath; }; @@ -203,13 +203,13 @@ bool ParserConfig::StoreResolvedURIs() const } ///////////////////////////////////////////////// -void ParserConfig::SetFilePath(const std::string &_path) +void ParserConfig::SetRelativeURISearchPath(const std::string &_path) { - this->dataPtr->filePath = _path; + this->dataPtr->relativeUriSearchPath = _path; } ///////////////////////////////////////////////// -const std::string &ParserConfig::FilePath() const +const std::string &ParserConfig::RelativeURISearchPath() const { - return this->dataPtr->filePath; + return this->dataPtr->relativeUriSearchPath; } diff --git a/src/SDF.cc b/src/SDF.cc index d95ac5c54..c4c1e6302 100644 --- a/src/SDF.cc +++ b/src/SDF.cc @@ -16,7 +16,6 @@ */ #include -#include #include #include #include @@ -116,11 +115,10 @@ std::string findFile(const std::string &_filename, bool _searchLocalPath, { return path; } - else if (!_config.FilePath().empty()) + else if (!_config.RelativeURISearchPath().empty()) { - auto p = std::filesystem::path(_config.FilePath()).parent_path(); - p = p / filename; - path = p.string(); + path = sdf::filesystem::append(_config.RelativeURISearchPath(), + filename); if (sdf::filesystem::exists(path)) { return path; diff --git a/src/Sky.cc b/src/Sky.cc index 672748ff8..023e811d2 100644 --- a/src/Sky.cc +++ b/src/Sky.cc @@ -14,6 +14,8 @@ * limitations under the License. * */ +#include + #include "sdf/parser.hh" #include "sdf/Sky.hh" #include "Utils.hh" @@ -201,9 +203,12 @@ Errors Sky::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("cubemap_uri")) { + auto config = _config; + auto path = std::filesystem::path(_sdf->FilePath()).parent_path(); + config.SetRelativeURISearchPath(path.string()); this->dataPtr->cubemapUri = resolveURI( _sdf->Get(errors, "cubemap_uri", "").first, - _config, errors); + config, errors); } if ( _sdf->HasElement("clouds")) diff --git a/test/integration/resolve_uris.cc b/test/integration/resolve_uris.cc index c3498beb9..2bafed355 100644 --- a/test/integration/resolve_uris.cc +++ b/test/integration/resolve_uris.cc @@ -15,6 +15,7 @@ * */ +#include #include #include @@ -418,3 +419,67 @@ TEST(ResolveURIs, BadCallback) EXPECT_EQ(kDiffuseUri, diffuseElemUri); EXPECT_EQ(kNormalUri, normalElemUri); } + +///////////////////////////////////////////////// +TEST(ResolveURIs, ResolvedRelativeURIs) +{ + const std::string testFile = + sdf::testing::TestFile("sdf", "resolve_relative_uris.sdf"); + const std::string sdfDir = sdf::testing::TestFile("sdf"); + + sdf::ParserConfig config; + config.SetStoreResovledURIs(true); + EXPECT_TRUE(config.StoreResolvedURIs()); + + size_t callbackCount = 0; + config.SetFindCallback( + [=, &callbackCount](const std::string &) + { + callbackCount++; + return ""; + }); + + sdf::Root root; + auto errors = root.Load(testFile, config); + EXPECT_TRUE(errors.empty()); + // no user callbacks should be invoked + EXPECT_EQ(0, callbackCount); + + auto world = root.WorldByIndex(0); + ASSERT_NE(nullptr, world); + + /// Skybox Cubemap Uri + auto sky = world->Scene()->Sky(); + auto cubemapUri = sky->CubemapUri(); + + /// Visual mesh and material script uri + auto model = world->ModelByName("model"); + ASSERT_NE(nullptr, model); + auto link = model->LinkByName("link"); + ASSERT_NE(nullptr, link); + auto visual = link->VisualByName("visual"); + ASSERT_NE(nullptr, visual); + auto meshVisualUri = visual->Geom()->MeshShape()->Uri(); + auto scriptUri = visual->Material()->ScriptUri(); + + // Collision mesh uri + auto meshCol = link->CollisionByName("collision"); + ASSERT_NE(nullptr, meshCol); + auto meshColUri = meshCol->Geom()->MeshShape()->Uri(); + + EXPECT_NE(std::string::npos, cubemapUri.find("dummy_cubemap.dds")); + EXPECT_NE(std::string::npos, scriptUri.find("dummy_material.material")); + EXPECT_NE(std::string::npos, meshVisualUri.find("dummy_mesh.dae")); + EXPECT_NE(std::string::npos, meshColUri.find("dummy_mesh.dae")); + EXPECT_EQ(meshVisualUri, meshColUri); + + EXPECT_TRUE(std::filesystem::path(cubemapUri).is_absolute()); + EXPECT_TRUE(std::filesystem::path(scriptUri).is_absolute()); + EXPECT_TRUE(std::filesystem::path(meshVisualUri).is_absolute()); + EXPECT_TRUE(std::filesystem::path(meshColUri).is_absolute()); + + EXPECT_TRUE(sdf::filesystem::exists(cubemapUri)); + EXPECT_TRUE(sdf::filesystem::exists(scriptUri)); + EXPECT_TRUE(sdf::filesystem::exists(meshVisualUri)); + EXPECT_TRUE(sdf::filesystem::exists(meshColUri)); +} diff --git a/test/sdf/media/dummy_cubemap.dds b/test/sdf/media/dummy_cubemap.dds new file mode 100644 index 000000000..e69de29bb diff --git a/test/sdf/media/dummy_material.material b/test/sdf/media/dummy_material.material new file mode 100644 index 000000000..e69de29bb diff --git a/test/sdf/media/dummy_mesh.dae b/test/sdf/media/dummy_mesh.dae new file mode 100644 index 000000000..e69de29bb diff --git a/test/sdf/resolve_relative_uris.sdf b/test/sdf/resolve_relative_uris.sdf new file mode 100644 index 000000000..642db70d5 --- /dev/null +++ b/test/sdf/resolve_relative_uris.sdf @@ -0,0 +1,54 @@ + + + + + + 0.3 0.4 0.5 + 0.6 0.7 0.8 + true + true + true + + + 4 + 21 + + 1.2 + 1.5 + 0.2 + 0.9 + 0.1 0.2 0.3 + + media/dummy_cubemap.dds + + + + + true + + + + + media/dummy_mesh.dae + + + + + false + + + media/dummy_mesh.dae + + + + + + + + + + + From 70e897e875362e4e6924674d5699ed81fcdae55a Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 28 Feb 2024 21:49:17 +0000 Subject: [PATCH 04/11] add search paths arg to resolveURI Signed-off-by: Ian Chen --- include/sdf/ParserConfig.hh | 8 -------- src/Heightmap.cc | 16 +++++++++------- src/Material.cc | 5 ++--- src/Mesh.cc | 4 +--- src/ParserConfig.cc | 12 ------------ src/SDF.cc | 9 --------- src/Sky.cc | 4 +--- src/Utils.cc | 16 +++++++++++++++- src/Utils.hh | 6 +++++- 9 files changed, 33 insertions(+), 47 deletions(-) diff --git a/include/sdf/ParserConfig.hh b/include/sdf/ParserConfig.hh index a8de279d0..b1a97ad0d 100644 --- a/include/sdf/ParserConfig.hh +++ b/include/sdf/ParserConfig.hh @@ -199,14 +199,6 @@ class SDFORMAT_VISIBLE ParserConfig /// store them. False to preserve original URIs public: bool StoreResolvedURIs() const; - /// \brief Set the path to search for URIs with relative path. - /// \param[in] _path Path to search. - public: void SetRelativeURISearchPath(const std::string &_path); - - /// \brief Get the path to search for URIs with relative path. - /// \return Path to search - public: const std::string &RelativeURISearchPath() const; - /// \brief Private data pointer. GZ_UTILS_IMPL_PTR(dataPtr) }; diff --git a/src/Heightmap.cc b/src/Heightmap.cc index 6c00d73bf..3dced1650 100644 --- a/src/Heightmap.cc +++ b/src/Heightmap.cc @@ -134,9 +134,11 @@ Errors HeightmapTexture::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("diffuse")) { + auto path = std::filesystem::path( + this->dataPtr->sdf->FilePath()).parent_path(); this->dataPtr->diffuse = resolveURI( _sdf->Get(errors, "diffuse", this->dataPtr->diffuse).first, - _config, errors); + _config, errors, {path.string()}); } else { @@ -146,9 +148,11 @@ Errors HeightmapTexture::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("normal")) { + auto path = std::filesystem::path( + this->dataPtr->sdf->FilePath()).parent_path(); this->dataPtr->normal = resolveURI( _sdf->Get(errors, "normal", this->dataPtr->normal).first, - _config, errors); + _config, errors, {path.string()}); } else { @@ -325,14 +329,12 @@ Errors Heightmap::Load(ElementPtr _sdf, const ParserConfig &_config) return errors; } - auto config = _config; - auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); - config.SetRelativeURISearchPath(path.string()); if (_sdf->HasElement("uri")) { + auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, - config, errors); + _config, errors, {path.string()}); } else { @@ -353,7 +355,7 @@ Errors Heightmap::Load(ElementPtr _sdf, const ParserConfig &_config) this->dataPtr->sampling).first; Errors textureLoadErrors = loadRepeated(_sdf, - "texture", this->dataPtr->textures, config); + "texture", this->dataPtr->textures, _config); errors.insert(errors.end(), textureLoadErrors.begin(), textureLoadErrors.end()); diff --git a/src/Material.cc b/src/Material.cc index d6cb19403..8335fbda0 100644 --- a/src/Material.cc +++ b/src/Material.cc @@ -124,10 +124,9 @@ Errors Material::Load(sdf::ElementPtr _sdf, const sdf::ParserConfig &_config) " element is empty."}); } - auto config = _config; auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); - config.SetRelativeURISearchPath(path.string()); - this->dataPtr->scriptUri = resolveURI(uriPair.first, config, errors); + this->dataPtr->scriptUri = resolveURI(uriPair.first, _config, errors, + {path.string()}); std::pair namePair = elem->Get(errors, "name", ""); diff --git a/src/Mesh.cc b/src/Mesh.cc index ebd7269d3..068102f77 100644 --- a/src/Mesh.cc +++ b/src/Mesh.cc @@ -84,12 +84,10 @@ Errors Mesh::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("uri")) { - auto config = _config; auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); - config.SetRelativeURISearchPath(path.string()); this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, - config, errors); + _config, errors, {path.string()}); } else { diff --git a/src/ParserConfig.cc b/src/ParserConfig.cc index 732a6fae2..0661b352c 100644 --- a/src/ParserConfig.cc +++ b/src/ParserConfig.cc @@ -201,15 +201,3 @@ bool ParserConfig::StoreResolvedURIs() const { return this->dataPtr->storeResolvedURIs; } - -///////////////////////////////////////////////// -void ParserConfig::SetRelativeURISearchPath(const std::string &_path) -{ - this->dataPtr->relativeUriSearchPath = _path; -} - -///////////////////////////////////////////////// -const std::string &ParserConfig::RelativeURISearchPath() const -{ - return this->dataPtr->relativeUriSearchPath; -} diff --git a/src/SDF.cc b/src/SDF.cc index c4c1e6302..7a43d0f9a 100644 --- a/src/SDF.cc +++ b/src/SDF.cc @@ -115,15 +115,6 @@ std::string findFile(const std::string &_filename, bool _searchLocalPath, { return path; } - else if (!_config.RelativeURISearchPath().empty()) - { - path = sdf::filesystem::append(_config.RelativeURISearchPath(), - filename); - if (sdf::filesystem::exists(path)) - { - return path; - } - } } // Next check the install path. diff --git a/src/Sky.cc b/src/Sky.cc index 023e811d2..d54e6daf6 100644 --- a/src/Sky.cc +++ b/src/Sky.cc @@ -203,12 +203,10 @@ Errors Sky::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("cubemap_uri")) { - auto config = _config; auto path = std::filesystem::path(_sdf->FilePath()).parent_path(); - config.SetRelativeURISearchPath(path.string()); this->dataPtr->cubemapUri = resolveURI( _sdf->Get(errors, "cubemap_uri", "").first, - config, errors); + _config, errors, {path.string()}); } if ( _sdf->HasElement("clouds")) diff --git a/src/Utils.cc b/src/Utils.cc index 77ea4d35d..5e1c9f245 100644 --- a/src/Utils.cc +++ b/src/Utils.cc @@ -18,6 +18,7 @@ #include #include #include "sdf/Assert.hh" +#include "sdf/Filesystem.hh" #include "sdf/SDFImpl.hh" #include "Utils.hh" @@ -382,11 +383,24 @@ void copyChildren(ElementPtr _sdf, tinyxml2::XMLElement *_xml, ///////////////////////////////////////////////// std::string resolveURI(const std::string &_inputURI, - const sdf::ParserConfig &_config, sdf::Errors &_errors) + const sdf::ParserConfig &_config, sdf::Errors &_errors, + const std::unordered_set &_searchPaths) { std::string resolvedURI = _inputURI; if (_config.StoreResolvedURIs()) { + std::string sep("://"); + if (!_searchPaths.empty() && _inputURI.find(sep) == std::string::npos) + { + for (const auto &sp : _searchPaths) + { + std::string fullPath = sdf::filesystem::append(sp, _inputURI); + resolvedURI = sdf::findFile(fullPath, true, false); + if (!resolvedURI.empty()) + return resolvedURI; + } + } + resolvedURI = sdf::findFile(_inputURI, true, true, _config); if (resolvedURI.empty()) { diff --git a/src/Utils.hh b/src/Utils.hh index 18898eadd..b02ff91a0 100644 --- a/src/Utils.hh +++ b/src/Utils.hh @@ -23,6 +23,7 @@ #include #include #include +#include #include "sdf/Error.hh" #include "sdf/Element.hh" #include "sdf/InterfaceElements.hh" @@ -262,10 +263,13 @@ namespace sdf /// \param[in] _inputURI URI from parsed SDF file to resolve /// \param[in] _config Parser configuration to use to resolve /// \param[in, out] _errors Error vector to append to if resolution fails + /// \param[in] _searchPaths Optional additional search paths (directory) + /// when resolving URI /// \return Resolved URI or Original URI, depending on parser configuration std::string resolveURI(const std::string &_inputURI, const sdf::ParserConfig &_config, - sdf::Errors &_errors); + sdf::Errors &_errors, + const std::unordered_set &_searchPaths = {}); } } #endif From f0921808fe4648415523a53b8778f049e6f3405e Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 28 Feb 2024 21:52:15 +0000 Subject: [PATCH 05/11] remove unused var Signed-off-by: Ian Chen --- src/ParserConfig.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ParserConfig.cc b/src/ParserConfig.cc index 0661b352c..14ca46af5 100644 --- a/src/ParserConfig.cc +++ b/src/ParserConfig.cc @@ -56,9 +56,6 @@ class sdf::ParserConfig::Implementation /// \brief Flag to expand URIs where possible store the resolved paths public: bool storeResolvedURIs = false; - - /// \brief Path to search for URIs with relative paths - public: std::string relativeUriSearchPath; }; From e973d806453305aaedf855c83f3e404a4ef20d45 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 28 Feb 2024 21:56:37 +0000 Subject: [PATCH 06/11] check abs path Signed-off-by: Ian Chen --- src/Utils.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Utils.cc b/src/Utils.cc index 5e1c9f245..28e41c00d 100644 --- a/src/Utils.cc +++ b/src/Utils.cc @@ -14,6 +14,7 @@ * limitations under the License. * */ +#include #include #include #include @@ -390,7 +391,9 @@ std::string resolveURI(const std::string &_inputURI, if (_config.StoreResolvedURIs()) { std::string sep("://"); - if (!_searchPaths.empty() && _inputURI.find(sep) == std::string::npos) + if (!_searchPaths.empty() && + _inputURI.find(sep) == std::string::npos && + !std::filesystem::path(_inputURI).is_absolute()) { for (const auto &sp : _searchPaths) { From 143f1b0042d507bbd2e2cd4fc4087e2203859898 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Wed, 28 Feb 2024 21:59:24 +0000 Subject: [PATCH 07/11] line wrap Signed-off-by: Ian Chen --- src/Utils.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Utils.hh b/src/Utils.hh index b02ff91a0..7a253aa34 100644 --- a/src/Utils.hh +++ b/src/Utils.hh @@ -269,7 +269,8 @@ namespace sdf std::string resolveURI(const std::string &_inputURI, const sdf::ParserConfig &_config, sdf::Errors &_errors, - const std::unordered_set &_searchPaths = {}); + const std::unordered_set + &_searchPaths = {}); } } #endif From c130219fce8a874cca1e419fefa4d524df9e16b8 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Sat, 2 Mar 2024 00:10:44 +0000 Subject: [PATCH 08/11] add test for sdf string Signed-off-by: Ian Chen --- test/integration/resolve_uris.cc | 88 +++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/test/integration/resolve_uris.cc b/test/integration/resolve_uris.cc index 2bafed355..e6c41a543 100644 --- a/test/integration/resolve_uris.cc +++ b/test/integration/resolve_uris.cc @@ -448,11 +448,11 @@ TEST(ResolveURIs, ResolvedRelativeURIs) auto world = root.WorldByIndex(0); ASSERT_NE(nullptr, world); - /// Skybox Cubemap Uri + // Skybox Cubemap Uri auto sky = world->Scene()->Sky(); auto cubemapUri = sky->CubemapUri(); - /// Visual mesh and material script uri + // Visual mesh and material script uri auto model = world->ModelByName("model"); ASSERT_NE(nullptr, model); auto link = model->LinkByName("link"); @@ -483,3 +483,87 @@ TEST(ResolveURIs, ResolvedRelativeURIs) EXPECT_TRUE(sdf::filesystem::exists(meshVisualUri)); EXPECT_TRUE(sdf::filesystem::exists(meshColUri)); } + +///////////////////////////////////////////////// +TEST(ResolveURIs, ResolvedRelativeURIsSdfString) +{ + const std::string testSdfString = R"( + + + + + + + media/dummy_mesh.dae + + + + + false + + + media/dummy_mesh.dae + + + + + + + + + )"; + + sdf::ParserConfig config; + config.SetStoreResovledURIs(true); + EXPECT_TRUE(config.StoreResolvedURIs()); + + const std::string kTestMediaPath = sdf::testing::TestFile("sdf", "media"); + size_t callbackCount = 0; + config.SetFindCallback( + [=, &callbackCount](const std::string &_file) + { + std::string path = _file; + std::string media = "media"; + path.replace(path.find(media), media.length(), kTestMediaPath); + callbackCount++; + return path; + }); + + sdf::Root root; + auto errors = root.LoadSdfString(testSdfString, config); + EXPECT_TRUE(errors.empty()); + // callbacks should be invoked for all relative uris + EXPECT_EQ(3, callbackCount); + + auto model = root.Model(); + ASSERT_NE(nullptr, model); + + // Visual mesh and material script uri + auto link = model->LinkByName("link"); + ASSERT_NE(nullptr, link); + auto visual = link->VisualByName("visual"); + ASSERT_NE(nullptr, visual); + auto meshVisualUri = visual->Geom()->MeshShape()->Uri(); + auto scriptUri = visual->Material()->ScriptUri(); + + // Collision mesh uri + auto meshCol = link->CollisionByName("collision"); + ASSERT_NE(nullptr, meshCol); + auto meshColUri = meshCol->Geom()->MeshShape()->Uri(); + + EXPECT_NE(std::string::npos, scriptUri.find("dummy_material.material")); + EXPECT_NE(std::string::npos, meshVisualUri.find("dummy_mesh.dae")); + EXPECT_NE(std::string::npos, meshColUri.find("dummy_mesh.dae")); + EXPECT_EQ(meshVisualUri, meshColUri); + + EXPECT_TRUE(std::filesystem::path(scriptUri).is_absolute()); + EXPECT_TRUE(std::filesystem::path(meshVisualUri).is_absolute()); + EXPECT_TRUE(std::filesystem::path(meshColUri).is_absolute()); + + EXPECT_TRUE(sdf::filesystem::exists(scriptUri)); + EXPECT_TRUE(sdf::filesystem::exists(meshVisualUri)); + EXPECT_TRUE(sdf::filesystem::exists(meshColUri)); +} From 443dfd200d7f0f341d42b10a280de58c235d39d9 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Sat, 2 Mar 2024 00:22:29 +0000 Subject: [PATCH 09/11] check empty filepath Signed-off-by: Ian Chen --- src/Heightmap.cc | 29 +++++++++++++++++++++-------- src/Material.cc | 9 +++++++-- src/Mesh.cc | 9 +++++++-- src/Sky.cc | 9 +++++++-- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/Heightmap.cc b/src/Heightmap.cc index 3dced1650..1062612ea 100644 --- a/src/Heightmap.cc +++ b/src/Heightmap.cc @@ -134,11 +134,15 @@ Errors HeightmapTexture::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("diffuse")) { - auto path = std::filesystem::path( - this->dataPtr->sdf->FilePath()).parent_path(); + std::unordered_set paths; + if (!this->dataPtr->sdf->FilePath().empty()) + { + paths.insert( + std::filesystem::path(this->dataPtr->sdf->FilePath()).parent_path()); + } this->dataPtr->diffuse = resolveURI( _sdf->Get(errors, "diffuse", this->dataPtr->diffuse).first, - _config, errors, {path.string()}); + _config, errors, paths); } else { @@ -148,11 +152,15 @@ Errors HeightmapTexture::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("normal")) { - auto path = std::filesystem::path( - this->dataPtr->sdf->FilePath()).parent_path(); + std::unordered_set paths; + if (!this->dataPtr->sdf->FilePath().empty()) + { + paths.insert( + std::filesystem::path(this->dataPtr->sdf->FilePath()).parent_path()); + } this->dataPtr->normal = resolveURI( _sdf->Get(errors, "normal", this->dataPtr->normal).first, - _config, errors, {path.string()}); + _config, errors, paths); } else { @@ -331,10 +339,15 @@ Errors Heightmap::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("uri")) { - auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); + std::unordered_set paths; + if (!this->dataPtr->filePath.empty()) + { + paths.insert( + std::filesystem::path(this->dataPtr->filePath).parent_path()); + } this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, - _config, errors, {path.string()}); + _config, errors, paths); } else { diff --git a/src/Material.cc b/src/Material.cc index 8335fbda0..a7871080a 100644 --- a/src/Material.cc +++ b/src/Material.cc @@ -124,9 +124,14 @@ Errors Material::Load(sdf::ElementPtr _sdf, const sdf::ParserConfig &_config) " element is empty."}); } - auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); + std::unordered_set paths; + if (!this->dataPtr->filePath.empty()) + { + paths.insert( + std::filesystem::path(this->dataPtr->filePath).parent_path()); + } this->dataPtr->scriptUri = resolveURI(uriPair.first, _config, errors, - {path.string()}); + paths); std::pair namePair = elem->Get(errors, "name", ""); diff --git a/src/Mesh.cc b/src/Mesh.cc index 068102f77..55133aa18 100644 --- a/src/Mesh.cc +++ b/src/Mesh.cc @@ -84,10 +84,15 @@ Errors Mesh::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("uri")) { - auto path = std::filesystem::path(this->dataPtr->filePath).parent_path(); + std::unordered_set paths; + if (!this->dataPtr->filePath.empty()) + { + paths.insert( + std::filesystem::path(this->dataPtr->filePath).parent_path()); + } this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, - _config, errors, {path.string()}); + _config, errors, paths); } else { diff --git a/src/Sky.cc b/src/Sky.cc index d54e6daf6..56a6f9d7d 100644 --- a/src/Sky.cc +++ b/src/Sky.cc @@ -203,10 +203,15 @@ Errors Sky::Load(ElementPtr _sdf, const ParserConfig &_config) if (_sdf->HasElement("cubemap_uri")) { - auto path = std::filesystem::path(_sdf->FilePath()).parent_path(); + std::unordered_set paths; + if (!_sdf->FilePath().empty()) + { + paths.insert( + std::filesystem::path(_sdf->FilePath()).parent_path()); + } this->dataPtr->cubemapUri = resolveURI( _sdf->Get(errors, "cubemap_uri", "").first, - _config, errors, {path.string()}); + _config, errors, paths); } if ( _sdf->HasElement("clouds")) From f51bd4bed5765ac31a23f2f9fe8f3688316492a1 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Mon, 4 Mar 2024 18:20:02 +0000 Subject: [PATCH 10/11] fixing windows filesystem path Signed-off-by: Ian Chen --- src/Heightmap.cc | 12 ++++++------ src/Material.cc | 4 ++-- src/Mesh.cc | 4 ++-- src/Sky.cc | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Heightmap.cc b/src/Heightmap.cc index 1062612ea..18433c11e 100644 --- a/src/Heightmap.cc +++ b/src/Heightmap.cc @@ -137,8 +137,8 @@ Errors HeightmapTexture::Load(ElementPtr _sdf, const ParserConfig &_config) std::unordered_set paths; if (!this->dataPtr->sdf->FilePath().empty()) { - paths.insert( - std::filesystem::path(this->dataPtr->sdf->FilePath()).parent_path()); + paths.insert(std::filesystem::path( + this->dataPtr->sdf->FilePath()).parent_path().string()); } this->dataPtr->diffuse = resolveURI( _sdf->Get(errors, "diffuse", this->dataPtr->diffuse).first, @@ -155,8 +155,8 @@ Errors HeightmapTexture::Load(ElementPtr _sdf, const ParserConfig &_config) std::unordered_set paths; if (!this->dataPtr->sdf->FilePath().empty()) { - paths.insert( - std::filesystem::path(this->dataPtr->sdf->FilePath()).parent_path()); + paths.insert(std::filesystem::path( + this->dataPtr->sdf->FilePath()).parent_path().string()); } this->dataPtr->normal = resolveURI( _sdf->Get(errors, "normal", this->dataPtr->normal).first, @@ -342,8 +342,8 @@ Errors Heightmap::Load(ElementPtr _sdf, const ParserConfig &_config) std::unordered_set paths; if (!this->dataPtr->filePath.empty()) { - paths.insert( - std::filesystem::path(this->dataPtr->filePath).parent_path()); + paths.insert(std::filesystem::path( + this->dataPtr->filePath).parent_path().string()); } this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, diff --git a/src/Material.cc b/src/Material.cc index a7871080a..6a0456686 100644 --- a/src/Material.cc +++ b/src/Material.cc @@ -127,8 +127,8 @@ Errors Material::Load(sdf::ElementPtr _sdf, const sdf::ParserConfig &_config) std::unordered_set paths; if (!this->dataPtr->filePath.empty()) { - paths.insert( - std::filesystem::path(this->dataPtr->filePath).parent_path()); + paths.insert(std::filesystem::path( + this->dataPtr->filePath).parent_path().string()); } this->dataPtr->scriptUri = resolveURI(uriPair.first, _config, errors, paths); diff --git a/src/Mesh.cc b/src/Mesh.cc index 55133aa18..139be73fe 100644 --- a/src/Mesh.cc +++ b/src/Mesh.cc @@ -87,8 +87,8 @@ Errors Mesh::Load(ElementPtr _sdf, const ParserConfig &_config) std::unordered_set paths; if (!this->dataPtr->filePath.empty()) { - paths.insert( - std::filesystem::path(this->dataPtr->filePath).parent_path()); + paths.insert(std::filesystem::path( + this->dataPtr->filePath).parent_path().string()); } this->dataPtr->uri = resolveURI( _sdf->Get(errors, "uri", "").first, diff --git a/src/Sky.cc b/src/Sky.cc index 56a6f9d7d..ba46f209b 100644 --- a/src/Sky.cc +++ b/src/Sky.cc @@ -206,8 +206,8 @@ Errors Sky::Load(ElementPtr _sdf, const ParserConfig &_config) std::unordered_set paths; if (!_sdf->FilePath().empty()) { - paths.insert( - std::filesystem::path(_sdf->FilePath()).parent_path()); + paths.insert(std::filesystem::path( + _sdf->FilePath()).parent_path().string()); } this->dataPtr->cubemapUri = resolveURI( _sdf->Get(errors, "cubemap_uri", "").first, From 83af965d2375ffe3438385e5b8e73a410db6a359 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Mon, 4 Mar 2024 21:09:08 +0000 Subject: [PATCH 11/11] Resovlve -> Resolve, add comments Signed-off-by: Ian Chen --- src/Utils.cc | 2 ++ test/integration/resolve_uris.cc | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Utils.cc b/src/Utils.cc index 28e41c00d..8003939b0 100644 --- a/src/Utils.cc +++ b/src/Utils.cc @@ -397,6 +397,7 @@ std::string resolveURI(const std::string &_inputURI, { for (const auto &sp : _searchPaths) { + // find file by searching local path but do not use callbacks std::string fullPath = sdf::filesystem::append(sp, _inputURI); resolvedURI = sdf::findFile(fullPath, true, false); if (!resolvedURI.empty()) @@ -404,6 +405,7 @@ std::string resolveURI(const std::string &_inputURI, } } + // find file by searching local path and use registered callbacks resolvedURI = sdf::findFile(_inputURI, true, true, _config); if (resolvedURI.empty()) { diff --git a/test/integration/resolve_uris.cc b/test/integration/resolve_uris.cc index e6c41a543..cfaa88e7a 100644 --- a/test/integration/resolve_uris.cc +++ b/test/integration/resolve_uris.cc @@ -60,7 +60,7 @@ TEST(ResolveURIs, StoreResolvedDisabled) const std::string sdfDir = sdf::testing::TestFile("sdf"); sdf::ParserConfig config; - config.SetStoreResovledURIs(false); + config.SetStoreResolvedURIs(false); EXPECT_FALSE(config.StoreResolvedURIs()); size_t callbackCount = 0; @@ -176,7 +176,7 @@ TEST(ResolveURIs, StoreResolvedEnabled) const std::string sdfDir = sdf::testing::TestFile("sdf"); sdf::ParserConfig config; - config.SetStoreResovledURIs(true); + config.SetStoreResolvedURIs(true); EXPECT_TRUE(config.StoreResolvedURIs()); size_t callbackCount = 0; @@ -302,7 +302,7 @@ TEST(ResolveURIs, BadCallback) const std::string sdfDir = sdf::testing::TestFile("sdf"); sdf::ParserConfig config; - config.SetStoreResovledURIs(true); + config.SetStoreResolvedURIs(true); EXPECT_TRUE(config.StoreResolvedURIs()); size_t callbackCount = 0; @@ -428,7 +428,7 @@ TEST(ResolveURIs, ResolvedRelativeURIs) const std::string sdfDir = sdf::testing::TestFile("sdf"); sdf::ParserConfig config; - config.SetStoreResovledURIs(true); + config.SetStoreResolvedURIs(true); EXPECT_TRUE(config.StoreResolvedURIs()); size_t callbackCount = 0; @@ -517,7 +517,7 @@ TEST(ResolveURIs, ResolvedRelativeURIsSdfString) )"; sdf::ParserConfig config; - config.SetStoreResovledURIs(true); + config.SetStoreResolvedURIs(true); EXPECT_TRUE(config.StoreResolvedURIs()); const std::string kTestMediaPath = sdf::testing::TestFile("sdf", "media");