Fix various MuJoCo Studio build issues on Windows, MacOS and Linux

PiperOrigin-RevId: 833313973
Change-Id: I7ec181374e0bb49c32420d025044c31f6fc3c657
This commit is contained in:
Matija Kecman
2025-11-17 06:34:43 -08:00
committed by Copybara-Service
parent a66cf303f8
commit 8eca9926dc
16 changed files with 108 additions and 67 deletions
+4 -1
View File
@@ -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
+5 -1
View File
@@ -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})
+1
View File
@@ -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
+33 -26
View File
@@ -17,6 +17,7 @@
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <numbers>
#include <filament/Box.h>
#include <filament/Engine.h>
@@ -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<float>(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<float>(num_slices_);
const float delta_angle =
2.0 * std::numbers::pi / static_cast<float>(num_slices_);
const float delta_radius = 1.0f / static_cast<float>(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<float>(num_slices_);
const float delta_angle =
2.0 * std::numbers::pi / static_cast<float>(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<float>(num_stacks_ + 1);
const float lon_angle_delta = 2.0 * M_PI / static_cast<float>(num_slices_);
const float lat_angle_delta =
std::numbers::pi / static_cast<float>(num_stacks_ + 1);
const float lon_angle_delta =
2.0 * std::numbers::pi / static_cast<float>(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<float>(num_stacks_);
const float lon_angle_delta = 2.0 * M_PI / static_cast<float>(num_slices_);
const float lat_angle_delta =
0.5 * std::numbers::pi / static_cast<float>(num_stacks_);
const float lon_angle_delta =
2.0 * std::numbers::pi / static_cast<float>(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<float>(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<float>(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<float>(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<float>(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);
}
}
}
@@ -16,6 +16,7 @@
#include <cmath>
#include <cstdint>
#include <numbers>
#include <utility>
#include <filament/Material.h>
@@ -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) {
@@ -141,9 +141,10 @@ bool GuiView::PrepareRenderable() {
}
std::memcpy(dst, cmds->IdxBuffer.Data, size);
};
const mujoco::FilamentBuffers& buffer = buffers_.emplace_back(
CreateIndexBuffer<uint16_t>(engine_, cmds->IdxBuffer.Size, ifill),
CreateVertexBuffer<GuiVertex>(engine_, cmds->VtxBuffer.Size, vfill));
buffers_.push_back(
{CreateIndexBuffer<uint16_t>(engine_, cmds->IdxBuffer.Size, ifill),
CreateVertexBuffer<GuiVertex>(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,
@@ -20,6 +20,7 @@
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <imgui.h>
#include <filament/ColorGrading.h>
@@ -96,6 +97,10 @@ struct UiOpts {
std::optional<T> fstep;
};
// This is a workaround to fix compilation on gcc <= 12 and clang <= 16
template <typename T>
struct dependent_false : std::false_type {};
template <typename T>
bool Ui(std::string_view label, T* value, UiOpts<T> opts = {}) {
bool changed = false;
@@ -134,7 +139,7 @@ bool Ui(std::string_view label, T* value, UiOpts<T> opts = {}) {
} else if constexpr (std::is_same_v<T, float4>) {
changed = ImGui::InputFloat4(label.data(), &value->x);
} else {
static_assert(false, "Unsupported type");
static_assert(dependent_false<T>::value, "Unsupported type");
}
return changed;
}
+5 -2
View File
@@ -14,6 +14,8 @@
#include "experimental/filament/filament/light.h"
#include <numbers>
#include <filament/Engine.h>
#include <filament/LightManager.h>
#include <filament/Scene.h>
@@ -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) {
@@ -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);
@@ -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"
+1 -3
View File
@@ -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;
+16 -4
View File
@@ -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()
+2 -2
View File
@@ -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;
+5 -1
View File
@@ -171,6 +171,10 @@ struct ImGuiOpts {
const char* format = std::is_floating_point_v<T> ? "%.3g" : "%d";
};
// This is a workaround to fix compilation on gcc <= 12 and clang <= 16
template <typename T>
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<T> opts = {}) {
res = ImGui::InputScalarN(name, ImGuiDataType_Double, value, num, pstep,
pstep_fast, format);
} else {
static_assert(false, "Unsupported type");
static_assert(dependent_false<T>::value, "Unsupported type");
}
if (opts.min.has_value()) {
@@ -16,6 +16,7 @@
#include <algorithm>
#include <cstddef>
#include <numbers>
#include <string>
#include <unordered_map>
#include <unordered_set>
@@ -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])
+4 -3
View File
@@ -17,6 +17,7 @@
#include <cstddef>
#include <map>
#include <memory>
#include <numbers>
#include <optional>
#include <string>
#include <vector>
@@ -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<pxr::UsdPhysicsPrismaticJoint>()) {
@@ -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);