diff --git a/python/mujoco/experimental/studio/native_viewer.cc b/python/mujoco/experimental/studio/native_viewer.cc index a93666e3..0146db89 100644 --- a/python/mujoco/experimental/studio/native_viewer.cc +++ b/python/mujoco/experimental/studio/native_viewer.cc @@ -16,11 +16,13 @@ #include #include #include +#include // NOLINT(build/c++17) #include #include #include #include + #include #include #include @@ -28,6 +30,7 @@ #include #include #include +#include #include "structs.h" #include #include @@ -48,8 +51,15 @@ static bool IsCrd() { } static std::vector LoadAsset(std::string_view path) { - std::string file_path = "assets/" + - std::string(path.substr(path.find(':') + 1)); + std::string_view subpath = path.substr(path.find(':') + 1); + static const std::string asset_dir = []() { + std::string module_dir = + mujoco::platform::GetModuleDir((void*)&LoadAsset); + return module_dir.empty() + ? "assets" + : (std::filesystem::path(module_dir) / "assets").string(); + }(); + std::string file_path = asset_dir + "/" + std::string(subpath); std::ifstream file(file_path, std::ios::binary | std::ios::ate); if (!file.is_open()) { diff --git a/src/experimental/platform/CMakeLists.txt b/src/experimental/platform/CMakeLists.txt index c903d604..fdabc5ba 100644 --- a/src/experimental/platform/CMakeLists.txt +++ b/src/experimental/platform/CMakeLists.txt @@ -22,6 +22,8 @@ target_sources(${MUJOCO_PLATFORM_TARGET_NAME} PUBLIC helpers.cc helpers.h + sys_utils.cc + sys_utils.h hal/egl_utils.cc hal/egl_utils.h hal/graphics_mode.cc diff --git a/src/experimental/platform/sys_utils.cc b/src/experimental/platform/sys_utils.cc new file mode 100644 index 00000000..45df2f8c --- /dev/null +++ b/src/experimental/platform/sys_utils.cc @@ -0,0 +1,59 @@ +// Copyright 2026 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "experimental/platform/sys_utils.h" + +#include // NOLINT(build/c++17) +#include +#include +#if defined(_WIN32) || defined(__CYGWIN__) + #include +#else + #include +#endif + +namespace mujoco::platform { + +std::string GetModuleDir(void* addr) { +#if defined(_WIN32) || defined(__CYGWIN__) + HMODULE hModule = NULL; + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)addr, &hModule)) { + char path[MAX_PATH]; + DWORD written = GetModuleFileNameA(hModule, path, sizeof(path)); + if (written > 0 && written < sizeof(path)) { + return std::filesystem::path(path).parent_path().string(); + } + } + return ""; +#else + Dl_info info; + if (dladdr(addr, &info) != 0 && info.dli_fname != nullptr) { + std::filesystem::path p(info.dli_fname); + if (p.is_absolute()) { + return p.parent_path().string(); + } + std::error_code ec; + std::filesystem::path cwd = std::filesystem::current_path(ec); + if (!ec) { + return (cwd / p).parent_path().string(); + } + return p.parent_path().string(); + } + return ""; +#endif +} + +} // namespace mujoco::platform diff --git a/src/experimental/platform/sys_utils.h b/src/experimental/platform/sys_utils.h new file mode 100644 index 00000000..ae790adc --- /dev/null +++ b/src/experimental/platform/sys_utils.h @@ -0,0 +1,27 @@ +// Copyright 2026 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MUJOCO_SRC_EXPERIMENTAL_PLATFORM_SYS_UTILS_H_ +#define MUJOCO_SRC_EXPERIMENTAL_PLATFORM_SYS_UTILS_H_ + +#include + +namespace mujoco::platform { + +// Returns the path to the directory of the current module (exe or shared lib). +std::string GetModuleDir(void* addr); + +} // namespace mujoco::platform + +#endif // MUJOCO_SRC_EXPERIMENTAL_PLATFORM_SYS_UTILS_H_ diff --git a/src/experimental/studio/main.cc b/src/experimental/studio/main.cc index c6bc03e8..6d273966 100644 --- a/src/experimental/studio/main.cc +++ b/src/experimental/studio/main.cc @@ -16,6 +16,7 @@ #include #include +#include // NOLINT(build/c++17) #include #include #include @@ -27,6 +28,7 @@ #include #include #include "experimental/platform/hal/graphics_mode.h" +#include "experimental/platform/sys_utils.h" #include "experimental/studio/app.h" ABSL_FLAG(int, window_width, 1400, "Window width"); @@ -36,7 +38,11 @@ ABSL_FLAG(std::string, gfx, "", "Graphics API"); std::string Resolve(std::string_view path) { std::string_view subpath = path.substr(path.find(':') + 1); - return std::string("assets/") + std::string(subpath); + std::filesystem::path exe_dir = mujoco::platform::GetModuleDir((void*)&Resolve); + if (exe_dir.empty()) { + return std::string("assets/") + std::string(subpath); + } + return (exe_dir / "assets" / subpath).string(); } class FileResource {