diff --git a/cmake/MujocoDependencies.cmake b/cmake/MujocoDependencies.cmake index 750d450f..cec1570a 100644 --- a/cmake/MujocoDependencies.cmake +++ b/cmake/MujocoDependencies.cmake @@ -276,7 +276,7 @@ if(WIN32) endif() endif() -if(MUJOCO_BUILD_TESTS) +if(MUJOCO_BUILD_TESTS OR MUJOCO_BUILD_STUDIO OR MUJOCO_USE_FILAMENT) set(ABSL_PROPAGATE_CXX_STD ON) # This specific version of Abseil does not have the following variable. We need to work with BUILD_TESTING @@ -307,6 +307,9 @@ if(MUJOCO_BUILD_TESTS) ${BUILD_TESTING_OLD} CACHE BOOL "Build tests." FORCE ) +endif() + +if(MUJOCO_BUILD_TESTS) # Avoid linking errors on Windows by dynamically linking to the C runtime. set(gtest_force_shared_crt diff --git a/cmake/third_party_deps/filament.cmake b/cmake/third_party_deps/filament.cmake index eb507471..007bc23b 100644 --- a/cmake/third_party_deps/filament.cmake +++ b/cmake/third_party_deps/filament.cmake @@ -28,10 +28,14 @@ set(FILAMENT_SKIP_SDL2 ON) set(FILAMENT_USE_EXTERNAL_ABSL ON) set(FILAMENT_USE_EXTERNAL_BENCHMARK ON) set(FILAMENT_USE_EXTERNAL_GTEST ON) +if(WIN32) + set(USE_STATIC_CRT OFF) +endif() + fetchpackage( PACKAGE_NAME filament GIT_REPO https://github.com/google/filament.git GIT_TAG ${MUJOCO_DEP_VERSION_filament} ) -set(BUILD_SHARED_LIBS BUILD_SHARED_LIBS_OLD) +set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_OLD}) diff --git a/src/experimental/filament/CMakeLists.txt b/src/experimental/filament/CMakeLists.txt index 0d286b8a..99854dfc 100644 --- a/src/experimental/filament/CMakeLists.txt +++ b/src/experimental/filament/CMakeLists.txt @@ -17,6 +17,7 @@ cmake_minimum_required(VERSION 3.16) set(MUJOCO_FILAMENT_TARGET_NAME mujoco_filament) add_library(${MUJOCO_FILAMENT_TARGET_NAME} STATIC) +target_compile_definitions(${MUJOCO_FILAMENT_TARGET_NAME} PRIVATE MJ_STATIC) target_sources(${MUJOCO_FILAMENT_TARGET_NAME} PUBLIC diff --git a/src/experimental/filament/filament/builtins.cc b/src/experimental/filament/filament/builtins.cc index a47f4f43..e7beacc8 100644 --- a/src/experimental/filament/filament/builtins.cc +++ b/src/experimental/filament/filament/builtins.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -310,7 +311,7 @@ class TubeBuilder { } void GenerateVertices(VertexType* ptr, size_t num) const { - const float delta_angle = 2.f * M_PI / (float)num_slices_; + const float delta_angle = 2.f * std::numbers::pi / (float)num_slices_; const float delta_stack = 2.f / static_cast(num_stacks_); int idx = 0; @@ -370,7 +371,8 @@ class ConeBuilder { } void GenerateVertices(VertexType* ptr, std::size_t num) const { // pole: use triangles - const float delta_angle = 2.0 * M_PI / static_cast(num_slices_); + const float delta_angle = + 2.0 * std::numbers::pi / static_cast(num_slices_); const float delta_radius = 1.0f / static_cast(num_stacks_); int idx = 0; @@ -460,7 +462,8 @@ class DiskBuilder { } void GenerateVertices(VertexType* ptr, std::size_t num) const { - const float delta_angle = 2.0 * M_PI / static_cast(num_slices_); + const float delta_angle = + 2.0 * std::numbers::pi / static_cast(num_slices_); int idx = 0; ptr[idx++] = VertexType(float3{0, 0, 0}, orientation_); @@ -514,8 +517,10 @@ class SphereBuilder { } void GenerateVertices(VertexType* ptr, size_t num) const { - const float lat_angle_delta = M_PI / static_cast(num_stacks_ + 1); - const float lon_angle_delta = 2.0 * M_PI / static_cast(num_slices_); + const float lat_angle_delta = + std::numbers::pi / static_cast(num_stacks_ + 1); + const float lon_angle_delta = + 2.0 * std::numbers::pi / static_cast(num_slices_); // Add the north and south poles. int idx = 0; @@ -621,31 +626,33 @@ class DomeBuilder { } void GenerateVertices(VertexType* ptr, size_t num) const { - const float lat_angle_delta = 0.5 * M_PI / static_cast(num_stacks_); - const float lon_angle_delta = 2.0 * M_PI / static_cast(num_slices_); + const float lat_angle_delta = + 0.5 * std::numbers::pi / static_cast(num_stacks_); + const float lon_angle_delta = + 2.0 * std::numbers::pi / static_cast(num_slices_); - // Add the pole. - int idx = 0; - ptr[idx++] = MakeVert(0, 0, 1); + // Add the pole. + int idx = 0; + ptr[idx++] = MakeVert(0, 0, 1); - // Vertices by latitude. - for (int lat = 0; lat < num_stacks_; ++lat) { - // +1 because we handle the north pole (which would be at a lat angle of - // 0-degrees) explicitly. - const float lat_angle = static_cast(lat + 1) * lat_angle_delta; - const float cos_lat_angle = std::cos(lat_angle); - const float sin_lat_angle = std::sin(lat_angle); - const float z = cos_lat_angle; + // Vertices by latitude. + for (int lat = 0; lat < num_stacks_; ++lat) { + // +1 because we handle the north pole (which would be at a lat angle of + // 0-degrees) explicitly. + const float lat_angle = static_cast(lat + 1) * lat_angle_delta; + const float cos_lat_angle = std::cos(lat_angle); + const float sin_lat_angle = std::sin(lat_angle); + const float z = cos_lat_angle; - for (int lon = 0; lon < num_slices_; ++lon) { - const float lon_angle = static_cast(lon) * lon_angle_delta; - const float cos_lon_angle = std::cos(lon_angle); - const float sin_lon_angle = std::sin(lon_angle); + for (int lon = 0; lon < num_slices_; ++lon) { + const float lon_angle = static_cast(lon) * lon_angle_delta; + const float cos_lon_angle = std::cos(lon_angle); + const float sin_lon_angle = std::sin(lon_angle); - const float x = sin_lat_angle * cos_lon_angle; - const float y = sin_lat_angle * sin_lon_angle; - ptr[idx++] = MakeVert(x, y, z); - } + const float x = sin_lat_angle * cos_lon_angle; + const float y = sin_lat_angle * sin_lon_angle; + ptr[idx++] = MakeVert(x, y, z); + } } } diff --git a/src/experimental/filament/filament/drawable.cc b/src/experimental/filament/filament/drawable.cc index 294d3a04..ed1fea28 100644 --- a/src/experimental/filament/filament/drawable.cc +++ b/src/experimental/filament/filament/drawable.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -201,7 +202,7 @@ void Drawable::SetTransform(const mjvGeom& geom) { entity_transform *= mat4::translation(float3{0, 0, size.z}); } else if (j == kCylinderBottomDisk) { entity_transform *= mat4::translation(float3{0, 0, -size.z}); - entity_transform *= mat4::rotation(M_PI, float3{1, 0, 0}); + entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0}); } } else if (geom.type == mjGEOM_CAPSULE) { // Capsules are a tube with two domes at the ends. We apply an inverse @@ -213,7 +214,7 @@ void Drawable::SetTransform(const mjvGeom& geom) { entity_transform *= mat4::scaling(float3{1, 1, xz_size / size.z}); } else if (j == kCapsuleBottomDome) { entity_transform *= mat4::translation(float3{0, 0, -size.z}); - entity_transform *= mat4::rotation(M_PI, float3{1, 0, 0}); + entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0}); entity_transform *= mat4::scaling(float3{1, 1, xz_size / size.z}); } } else if (geom.type == mjGEOM_ARROW) { @@ -229,12 +230,12 @@ void Drawable::SetTransform(const mjvGeom& geom) { mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f}); } else if (j == kArrow0ConeDisk) { entity_transform *= mat4::translation(float3{0, 0, size.z}); - entity_transform *= mat4::rotation(M_PI, float3{1, 0, 0}); + entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0}); entity_transform *= mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f}); } else if (j == kArrow0BottomDisk) { entity_transform *= mat4::translation(float3{0, 0, -size.z}); - entity_transform *= mat4::rotation(M_PI, float3{1, 0, 0}); + entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0}); } } else if (geom.type == mjGEOM_ARROW1) { // An arrow1 is a tube with a cone at the end and a disk cap at the other @@ -245,7 +246,7 @@ void Drawable::SetTransform(const mjvGeom& geom) { entity_transform *= mat4::translation(float3{0, 0, size.z}); } else if (j == kArrow1BottomDisk) { entity_transform *= mat4::translation(float3{0, 0, -size.z}); - entity_transform *= mat4::rotation(M_PI, float3{1, 0, 0}); + entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0}); } } else if (geom.type == mjGEOM_ARROW2) { // An arrow2 is a tube with a cone at both ends. Like the standard arrow, @@ -258,12 +259,12 @@ void Drawable::SetTransform(const mjvGeom& geom) { mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f}); } else if (j == kArrow2BottomCone) { entity_transform *= mat4::translation(float3{0, 0, -size.z}); - entity_transform *= mat4::rotation(M_PI, float3{1, 0, 0}); + entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0}); entity_transform *= mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f}); } else if (j == kArrow2TopConeDisk) { entity_transform *= mat4::translation(float3{0, 0, size.z}); - entity_transform *= mat4::rotation(M_PI, float3{1, 0, 0}); + entity_transform *= mat4::rotation(std::numbers::pi, float3{1, 0, 0}); entity_transform *= mat4::scaling(float3{kArrowHeadSize, kArrowHeadSize, 1.0f}); } else if (j == kArrow2BottomConeDisk) { diff --git a/src/experimental/filament/filament/gui_view.cc b/src/experimental/filament/filament/gui_view.cc index ad9ae3ca..25fe8357 100644 --- a/src/experimental/filament/filament/gui_view.cc +++ b/src/experimental/filament/filament/gui_view.cc @@ -141,9 +141,10 @@ bool GuiView::PrepareRenderable() { } std::memcpy(dst, cmds->IdxBuffer.Data, size); }; - const mujoco::FilamentBuffers& buffer = buffers_.emplace_back( - CreateIndexBuffer(engine_, cmds->IdxBuffer.Size, ifill), - CreateVertexBuffer(engine_, cmds->VtxBuffer.Size, vfill)); + buffers_.push_back( + {CreateIndexBuffer(engine_, cmds->IdxBuffer.Size, ifill), + CreateVertexBuffer(engine_, cmds->VtxBuffer.Size, vfill)}); + const mujoco::FilamentBuffers& buffer = buffers_.back(); int index_offset = 0; for (const ImDrawCmd& command : cmds->CmdBuffer) { @@ -164,7 +165,7 @@ bool GuiView::PrepareRenderable() { clip_height = height; } - mjrRect clip_rect(clip_left, clip_bottom, clip_width, clip_height); + mjrRect clip_rect{clip_left, clip_bottom, clip_width, clip_height}; builder.material(drawable_index, GetMaterialInstance(clip_rect)); builder.geometry(drawable_index, kTriangles, buffer.vertex_buffer, buffer.index_buffer, index_offset, diff --git a/src/experimental/filament/filament/imgui_editor.cc b/src/experimental/filament/filament/imgui_editor.cc index 2e83436b..76b74265 100644 --- a/src/experimental/filament/filament/imgui_editor.cc +++ b/src/experimental/filament/filament/imgui_editor.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -96,6 +97,10 @@ struct UiOpts { std::optional fstep; }; +// This is a workaround to fix compilation on gcc <= 12 and clang <= 16 +template +struct dependent_false : std::false_type {}; + template bool Ui(std::string_view label, T* value, UiOpts opts = {}) { bool changed = false; @@ -134,7 +139,7 @@ bool Ui(std::string_view label, T* value, UiOpts opts = {}) { } else if constexpr (std::is_same_v) { changed = ImGui::InputFloat4(label.data(), &value->x); } else { - static_assert(false, "Unsupported type"); + static_assert(dependent_false::value, "Unsupported type"); } return changed; } diff --git a/src/experimental/filament/filament/light.cc b/src/experimental/filament/filament/light.cc index f6cbfd62..50f75388 100644 --- a/src/experimental/filament/filament/light.cc +++ b/src/experimental/filament/filament/light.cc @@ -14,6 +14,8 @@ #include "experimental/filament/filament/light.h" +#include + #include #include #include @@ -55,9 +57,10 @@ Light::Light(ObjectManager* object_mgr, const Params& params) builder.castShadows(params.castshadow); if (type == filament::LightManager::Type::FOCUSED_SPOT) { if (params.headlight) { - builder.spotLightCone(0, M_PI / 2.0f); + builder.spotLightCone(0, std::numbers::pi / 2.0f); } else { - builder.spotLightCone(0, params.spot_cone_angle * M_PI / 180.0f); + builder.spotLightCone(0, + params.spot_cone_angle * std::numbers::pi / 180.0f); } } if (type != filament::LightManager::Type::DIRECTIONAL) { diff --git a/src/experimental/filament/render_context_filament.cc b/src/experimental/filament/render_context_filament.cc index 6b9c591c..c98129b0 100644 --- a/src/experimental/filament/render_context_filament.cc +++ b/src/experimental/filament/render_context_filament.cc @@ -132,13 +132,13 @@ void mjr_setBuffer(int framebuffer, mjrContext* con) { } void mjr_readPixels(unsigned char* rgb, float* depth, mjrRect viewport, - const mjrContext* con) { + const mjrContext* con) { CheckFilamentContext(); g_filament_context->ReadPixels(viewport, rgb, depth); } void mjr_uploadFont(unsigned char* pixels, int width, int height, int bpp, - int id, const mjrContext* con) { + int id, const mjrContext* con) { CheckFilamentContext(); if (bpp != 4) { mju_error("Only 4bpp fonts are supported, got %d", bpp); diff --git a/src/experimental/filament/render_context_filament.h b/src/experimental/filament/render_context_filament.h index fb1e68a7..dcef98bf 100644 --- a/src/experimental/filament/render_context_filament.h +++ b/src/experimental/filament/render_context_filament.h @@ -58,25 +58,25 @@ void mjr_defaultFilamentConfig(mjrFilamentConfig* config); void mjr_makeFilamentContext(const mjModel* m, mjrContext* con, const mjrFilamentConfig* config); -void mjr_defaultContext(mjrContext* con); +MJAPI void mjr_defaultContext(mjrContext* con); -void mjr_makeContext(const mjModel* m, mjrContext* con, int fontscale); +MJAPI void mjr_makeContext(const mjModel* m, mjrContext* con, int fontscale); -void mjr_freeContext(mjrContext* con); +MJAPI void mjr_freeContext(mjrContext* con); -void mjr_render(mjrRect viewport, mjvScene* scn, const mjrContext* con); +MJAPI void mjr_render(mjrRect viewport, mjvScene* scn, const mjrContext* con); -void mjr_uploadMesh(const mjModel* m, const mjrContext* con, int meshid); +MJAPI void mjr_uploadMesh(const mjModel* m, const mjrContext* con, int meshid); -void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid); +MJAPI void mjr_uploadTexture(const mjModel* m, const mjrContext* con, int texid); void mjr_uploadFont(unsigned char* pixels, int width, int height, int bpp, int id, const mjrContext* con); -void mjr_setBuffer(int framebuffer, mjrContext* con); +MJAPI void mjr_setBuffer(int framebuffer, mjrContext* con); -void mjr_readPixels(unsigned char* rgb, float* depth, mjrRect viewport, - const mjrContext* con); +MJAPI void mjr_readPixels(unsigned char* rgb, float* depth, mjrRect viewport, + const mjrContext* con); #if defined(__cplusplus) } // extern "C" diff --git a/src/experimental/filament/stubs.cc b/src/experimental/filament/stubs.cc index d29a3328..9d2d099c 100644 --- a/src/experimental/filament/stubs.cc +++ b/src/experimental/filament/stubs.cc @@ -63,9 +63,7 @@ void mjr_label(mjrRect viewport, int font, const char* txt, float r, float g, void mjr_figure(mjrRect viewport, mjvFigure* fig, const mjrContext* con) { mju_error("mjr_figure not implemented."); } -void mjr_finish() { - mju_error("mjr_finish not implemented."); -} +void mjr_finish() { mju_error("mjr_finish not implemented."); } int mjr_getError() { mju_error("mjr_getError not implemented."); return 0; diff --git a/src/experimental/studio/CMakeLists.txt b/src/experimental/studio/CMakeLists.txt index 79e5baa7..3c213d66 100644 --- a/src/experimental/studio/CMakeLists.txt +++ b/src/experimental/studio/CMakeLists.txt @@ -47,6 +47,13 @@ target_compile_definitions(${MUJOCO_STUDIO_TARGET_NAME} -D${MUJOCO_STUDIO_RENDER_CONFIG} ) +if (WIN32) + target_compile_definitions(${MUJOCO_STUDIO_TARGET_NAME} + PRIVATE + -D_USE_MATH_DEFINES + ) +endif() + include(third_party_deps/dear_imgui) include(third_party_deps/implot) target_link_libraries(${MUJOCO_STUDIO_TARGET_NAME} @@ -63,13 +70,18 @@ target_link_libraries(${MUJOCO_STUDIO_TARGET_NAME} # Filament backend requires additional files to be copied into an "assets" folder. if(MUJOCO_USE_FILAMENT) + if(WIN32) + # Copy to work around lack of symlink permissions on Windows. + set(COPY_COMMAND copy_directory) + else() + set(COPY_COMMAND create_symlink) + endif() add_custom_command( TARGET ${MUJOCO_STUDIO_TARGET_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} - -E create_symlink - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../src/experimental/filament/assets - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets + COMMAND ${CMAKE_COMMAND} -E ${COPY_COMMAND} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../src/experimental/filament/assets + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets COMMENT "Copying Filament assets to build directory" ) endif() diff --git a/src/experimental/studio/app.cc b/src/experimental/studio/app.cc index e9de6ff6..81ebecb2 100644 --- a/src/experimental/studio/app.cc +++ b/src/experimental/studio/app.cc @@ -176,8 +176,8 @@ void App::OnModelLoaded(std::string_view model_file) { window_->SetTitle("MuJoCo Studio : " + std::string(model_file)); tmp_.last_load_file = std::string(model_file_); std::filesystem::path path(model_file_); - base_path = std::string(path.parent_path()) + "/"; - model_name = std::string(path.stem()); + base_path = path.parent_path().string() + "/"; + model_name = path.stem().string(); } else { window_->SetTitle("MuJoCo Studio"); tmp_.last_load_file = base_path; diff --git a/src/experimental/toolbox/imgui_widgets.h b/src/experimental/toolbox/imgui_widgets.h index 3aa7881c..e00e8332 100644 --- a/src/experimental/toolbox/imgui_widgets.h +++ b/src/experimental/toolbox/imgui_widgets.h @@ -171,6 +171,10 @@ struct ImGuiOpts { const char* format = std::is_floating_point_v ? "%.3g" : "%d"; }; +// This is a workaround to fix compilation on gcc <= 12 and clang <= 16 +template +struct dependent_false : std::false_type {}; + // A compile-time wrapper around ImGui::InputScalarN. This is useful because // MuJoCo uses an `mjtNum` type which is an alias for float or double. // @@ -207,7 +211,7 @@ bool ImGui_InputN(const char* name, T* value, int num, ImGuiOpts opts = {}) { res = ImGui::InputScalarN(name, ImGuiDataType_Double, value, num, pstep, pstep_fast, format); } else { - static_assert(false, "Unsupported type"); + static_assert(dependent_false::value, "Unsupported type"); } if (opts.min.has_value()) { diff --git a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc index 456394a7..143bb1b7 100644 --- a/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc +++ b/src/experimental/usd/plugins/mjcf/mujoco_to_usd.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -2155,7 +2156,7 @@ class ModelWriter { 2 * znear * (use_intrinsic ? 1.0f / cam_intrinsic[1] * (cam_sensorsize[1] / 2.f - cam_intrinsic[3]) - : mju_tan((fovy / 2) * (M_PI / 180.0))); + : mju_tan((fovy / 2) * (std::numbers::pi / 180.0))); float horizontal_aperture = use_intrinsic ? 2 * znear / cam_intrinsic[0] * (cam_sensorsize[0] / 2.f - cam_intrinsic[2]) diff --git a/src/experimental/usd/usd_to_mjspec.cc b/src/experimental/usd/usd_to_mjspec.cc index 8ae885c5..48bacd57 100644 --- a/src/experimental/usd/usd_to_mjspec.cc +++ b/src/experimental/usd/usd_to_mjspec.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1837,8 +1838,8 @@ void ParseUsdPhysicsJoint(mjSpec* spec, const pxr::UsdPrim& prim, mjsBody* body, mj_joint->range[0] = lower; mj_joint->range[1] = upper; } else { - mj_joint->range[0] = lower * M_PI / 180.0; - mj_joint->range[1] = upper * M_PI / 180.0; + mj_joint->range[0] = lower * std::numbers::pi / 180.0; + mj_joint->range[1] = upper * std::numbers::pi / 180.0; } } } else if (prim.IsA()) { @@ -2129,7 +2130,7 @@ mjSpec* mj_parseUSDStage(const pxr::UsdStageRefPtr stage) { return spec; } -MJAPI mjSpec* mj_parseUSD(const char* identifier, const mjVFS* vfs, char* error, +mjSpec* mj_parseUSD(const char* identifier, const mjVFS* vfs, char* error, int error_sz) { auto stage = pxr::UsdStage::Open(identifier); return mj_parseUSDStage(stage);