From bebdd629431c9b27df7466cffabaa922770c68cb Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Thu, 18 Dec 2025 06:31:34 -0800 Subject: [PATCH] Allow studio to search for models in predetermined search paths. A future CL will allow users to specify search paths in their settings. PiperOrigin-RevId: 846240293 Change-Id: I1a1a4027d0fc85004a12fe01a406f50fd929f3e4 --- src/experimental/platform/helpers.cc | 42 ++++++++++++++++++++++++++++ src/experimental/platform/helpers.h | 5 ++++ src/experimental/studio/app.cc | 10 ++++--- src/experimental/studio/app.h | 2 ++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/experimental/platform/helpers.cc b/src/experimental/platform/helpers.cc index 99967cef..a24c2d14 100644 --- a/src/experimental/platform/helpers.cc +++ b/src/experimental/platform/helpers.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,47 @@ std::string LoadText(const std::string& filename) { return contents; } +static std::string CheckPathForFile(const std::filesystem::path& path, + const std::string& filename) { + std::filesystem::path resolved = path / filename; + if (std::filesystem::exists(resolved)) { + return resolved.string(); + } + resolved += ".xml"; + if (std::filesystem::exists(resolved)) { + return resolved.string(); + } + return ""; +} + +std::string ResolveFile(const std::string& filename, + const std::vector& search_paths) { + if (std::filesystem::exists(filename)) { + return filename; + } + + std::string resolved; + for (const std::string& path : search_paths) { + if (!std::filesystem::exists(path) || + !std::filesystem::is_directory(path)) { + continue; + } + + resolved = CheckPathForFile(std::filesystem::path(path), filename); + if (!resolved.empty()) { + return resolved; + } + + for (const auto& it : std::filesystem::recursive_directory_iterator(path)) { + resolved = CheckPathForFile(it.path(), filename); + if (!resolved.empty()) { + return resolved; + } + } + } + return ""; +} + void SaveColorToWebp(int width, int height, const unsigned char* data, const std::string& filename) { uint8_t* webp = nullptr; diff --git a/src/experimental/platform/helpers.h b/src/experimental/platform/helpers.h index 73842801..f60bca0a 100644 --- a/src/experimental/platform/helpers.h +++ b/src/experimental/platform/helpers.h @@ -34,6 +34,11 @@ using LoadAssetFn = std::function(std::string_view)>; void SaveText(const std::string& contents, const std::string& filename); std::string LoadText(const std::string& filename); +// Attempts to find a file with the given name by recursively searching the +// given search paths. +std::string ResolveFile(const std::string& filename, + const std::vector& search_paths); + // Exports the given color buffer to a webp file. void SaveColorToWebp(int width, int height, const unsigned char* data, const std::string& filename); diff --git a/src/experimental/studio/app.cc b/src/experimental/studio/app.cc index 3f9a1ddc..d9c7cfae 100644 --- a/src/experimental/studio/app.cc +++ b/src/experimental/studio/app.cc @@ -201,10 +201,12 @@ void App::LoadModel(std::string data, ContentType type) { // Store the file path as the model name. Note that we use this model name // to perform reload operations. model_name_ = std::move(data); - if (model_name_.ends_with(".mjb")) { - model_ = mj_loadModel(model_name_.c_str(), 0); - } else if (model_name_.ends_with(".xml")) { - spec_ = mj_parseXML(model_name_.c_str(), nullptr, err, sizeof(err)); + const std::string resolved_file = + platform::ResolveFile(model_name_, search_paths_); + if (resolved_file.ends_with(".mjb")) { + model_ = mj_loadModel(resolved_file.c_str(), 0); + } else if (resolved_file.ends_with(".xml")) { + spec_ = mj_parseXML(resolved_file.c_str(), nullptr, err, sizeof(err)); if (spec_ && err[0] == 0) { model_ = mj_compile(spec_, nullptr); } diff --git a/src/experimental/studio/app.h b/src/experimental/studio/app.h index 38ec34b9..3249de6e 100644 --- a/src/experimental/studio/app.h +++ b/src/experimental/studio/app.h @@ -185,6 +185,8 @@ class App { platform::SimProfiler profiler_; platform::SimHistory history_; + std::vector search_paths_; + mjSpec* spec_ = nullptr; mjModel* model_ = nullptr; mjData* data_ = nullptr;