Merge branch 'original/main' into vidur/remove-installing-glfw3-headers
This commit is contained in:
+25
-1
@@ -29,7 +29,7 @@ set(MUJOCO_DEP_VERSION_lodepng
|
||||
|
||||
project(
|
||||
mujoco_simulate
|
||||
VERSION 3.3.3
|
||||
VERSION 3.3.4
|
||||
DESCRIPTION "MuJoCo simulate binaries"
|
||||
HOMEPAGE_URL "https://mujoco.org"
|
||||
)
|
||||
@@ -183,6 +183,14 @@ if(SIMULATE_BUILD_EXECUTABLE)
|
||||
lodepng
|
||||
)
|
||||
|
||||
if (MUJOCO_WITH_USD)
|
||||
target_link_libraries(
|
||||
simulate
|
||||
mujoco::usd
|
||||
mujoco::usd::mjcf
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_options(simulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
|
||||
|
||||
if(APPLE AND MUJOCO_BUILD_MACOS_FRAMEWORKS)
|
||||
@@ -249,6 +257,22 @@ if(SIMULATE_BUILD_EXECUTABLE)
|
||||
MUJOCO_ENABLE_RPATH
|
||||
)
|
||||
|
||||
if (MUJOCO_WITH_USD)
|
||||
target_compile_definitions(simulate PUBLIC SIMULATE_WITH_USD)
|
||||
|
||||
# Add support to RPATH for the samples.
|
||||
target_add_rpath(
|
||||
TARGETS
|
||||
simulate
|
||||
INSTALL_DIRECTORY
|
||||
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}"
|
||||
LIB_DIRS
|
||||
${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/mujocoUsd ${USD_DIR}/lib
|
||||
DEPENDS
|
||||
MUJOCO_ENABLE_RPATH
|
||||
)
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS simulate
|
||||
EXPORT ${PROJECT_NAME}
|
||||
|
||||
+51
-4
@@ -25,6 +25,12 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#if defined(SIMULATE_WITH_USD)
|
||||
#include <mujoco/experimental/usd/usd.h>
|
||||
#include <pxr/pxr.h>
|
||||
#include <pxr/usd/usd/common.h>
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#endif
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "glfw_adapter.h"
|
||||
#include "simulate.h"
|
||||
@@ -224,13 +230,49 @@ mjModel* LoadModel(const char* file, mj::Simulate& sim) {
|
||||
char loadError[kErrorLength] = "";
|
||||
mjModel* mnew = 0;
|
||||
auto load_start = mj::Simulate::Clock::now();
|
||||
if (mju::strlen_arr(filename)>4 &&
|
||||
!std::strncmp(filename + mju::strlen_arr(filename) - 4, ".mjb",
|
||||
mju::sizeof_arr(filename) - mju::strlen_arr(filename)+4)) {
|
||||
|
||||
std::string filename_str(filename);
|
||||
std::string extension;
|
||||
size_t dot_pos = filename_str.rfind('.');
|
||||
|
||||
if (dot_pos != std::string::npos && dot_pos < filename_str.length() - 1) {
|
||||
extension = filename_str.substr(dot_pos);
|
||||
}
|
||||
|
||||
if (extension == ".mjb") {
|
||||
mnew = mj_loadModel(filename, nullptr);
|
||||
if (!mnew) {
|
||||
mju::strcpy_arr(loadError, "could not load binary model");
|
||||
}
|
||||
#if defined(SIMULATE_WITH_USD)
|
||||
} else if (extension == ".usda" || extension == ".usd" ||
|
||||
extension == ".usdc" || extension == ".usdz" ) {
|
||||
auto stage = pxr::UsdStage::Open(filename);
|
||||
if (!stage) {
|
||||
mju::strcpy_arr(loadError, "could not open USD stage");
|
||||
} else {
|
||||
mjSpec* spec = mj_parseUSDStage(stage);
|
||||
if (!spec) {
|
||||
mju::strcpy_arr(loadError, "could not parse USD stage to mjSpec");
|
||||
} else {
|
||||
mjModel* model = mj_compile(spec, nullptr);
|
||||
if (!model) {
|
||||
mju::strcpy_arr(loadError,
|
||||
"could not compile USD parsed mjSpec to mjModel:\n");
|
||||
mju::strcat_arr(loadError, mjs_getError(spec));
|
||||
} else {
|
||||
// handle compile warning
|
||||
if (mjs_isWarning(spec)) {
|
||||
mju::strcpy_arr(
|
||||
loadError,
|
||||
"warning while compiling USD parsed mjSpec to mjModel:\n");
|
||||
mju::strcat_arr(loadError, mjs_getError(spec));
|
||||
}
|
||||
}
|
||||
mnew = model;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
mnew = mj_loadXML(filename, nullptr, loadError, kErrorLength);
|
||||
|
||||
@@ -353,7 +395,7 @@ void PhysicsLoop(mj::Simulate& sim) {
|
||||
// requested slow-down factor
|
||||
double slowdown = 100 / sim.percentRealTime[sim.real_time_index];
|
||||
|
||||
// misalignment condition: distance from target sim time is bigger than syncmisalign
|
||||
// misalignment condition: distance from target sim time is bigger than syncMisalign
|
||||
bool misaligned =
|
||||
std::abs(Seconds(elapsedCPU).count()/slowdown - elapsedSim) > syncMisalign;
|
||||
|
||||
@@ -495,6 +537,11 @@ int main(int argc, char** argv) {
|
||||
// scan for libraries in the plugin directory to load additional plugins
|
||||
scanPluginLibraries();
|
||||
|
||||
#if defined(SIMULATE_WITH_USD)
|
||||
// If USD is used, print the version.
|
||||
std::printf("OpenUSD version v%d.%02d\n", PXR_MINOR_VERSION, PXR_PATCH_VERSION);
|
||||
#endif
|
||||
|
||||
mjvCamera cam;
|
||||
mjv_defaultCamera(&cam);
|
||||
|
||||
|
||||
+83
-5
@@ -112,6 +112,7 @@ enum {
|
||||
// right ui
|
||||
SECT_JOINT = 0,
|
||||
SECT_CONTROL,
|
||||
SECT_EQUALITY,
|
||||
NSECT1
|
||||
};
|
||||
|
||||
@@ -1127,6 +1128,36 @@ void MakeControlSection(mj::Simulate* sim) {
|
||||
}
|
||||
}
|
||||
|
||||
// make equality section of UI
|
||||
void MakeEqualitySection(mj::Simulate* sim) {
|
||||
mjuiDef defEquality[] = {
|
||||
{mjITEM_SECTION, "Equality", mjPRESERVE, nullptr, "AE"},
|
||||
{mjITEM_END}
|
||||
};
|
||||
mjuiDef defCheckBox[] = {
|
||||
{mjITEM_CHECKBYTE, "", 2, nullptr, ""},
|
||||
{mjITEM_END}
|
||||
};
|
||||
|
||||
// add section
|
||||
mjui_add(&sim->ui1, defEquality);
|
||||
|
||||
// add equalities, exit if UI limit reached
|
||||
for (int i= 0; i < sim->m_->neq && i<mjMAXUIITEM; i++) {
|
||||
// set data
|
||||
defCheckBox[0].pdata = &sim->d_->eq_active[i];
|
||||
|
||||
// set name
|
||||
if (!sim->equality_names_[i].empty()) {
|
||||
mju::strcpy_arr(defCheckBox[0].name, sim->equality_names_[i].c_str());
|
||||
} else {
|
||||
mju::sprintf_arr(defCheckBox[0].name, "equality %d", i);
|
||||
}
|
||||
|
||||
mjui_add(&sim->ui1, defCheckBox);
|
||||
}
|
||||
}
|
||||
|
||||
// make model-dependent UI sections
|
||||
void MakeUiSections(mj::Simulate* sim, const mjModel* m, const mjData* d) {
|
||||
// clear model-dependent sections of UI
|
||||
@@ -1140,14 +1171,23 @@ void MakeUiSections(mj::Simulate* sim, const mjModel* m, const mjData* d) {
|
||||
MakeGroupSection(sim);
|
||||
MakeJointSection(sim);
|
||||
MakeControlSection(sim);
|
||||
MakeEqualitySection(sim);
|
||||
}
|
||||
|
||||
//---------------------------------- utility functions ---------------------------------------------
|
||||
|
||||
// align and scale view
|
||||
void AlignAndScaleView(mj::Simulate* sim, const mjModel* m) {
|
||||
// use default free camera parameters
|
||||
mjv_defaultFreeCamera(m, &sim->cam);
|
||||
// if the id is valid, use the initial fixed camera
|
||||
if (m->vis.global.cameraid >= 0 && m->vis.global.cameraid < m->ncam) {
|
||||
sim->cam.fixedcamid = m->vis.global.cameraid;
|
||||
sim->cam.type = mjCAMERA_FIXED;
|
||||
}
|
||||
|
||||
// otherwise use default free camera
|
||||
else {
|
||||
mjv_defaultFreeCamera(m, &sim->cam);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1872,7 +1912,7 @@ Simulate::Simulate(std::unique_ptr<PlatformUIAdapter> platform_ui,
|
||||
//------------------------- Synchronize render and physics threads ---------------------------------
|
||||
|
||||
// operations which require holding the mutex, prevents racing with physics thread
|
||||
void Simulate::Sync() {
|
||||
void Simulate::Sync(bool state_only) {
|
||||
MutexLock lock(this->mtx);
|
||||
|
||||
if (!m_) {
|
||||
@@ -1931,6 +1971,18 @@ void Simulate::Sync() {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_->neq; ++i) {
|
||||
if (eq_active_[i] != eq_active_prev_[i]) {
|
||||
d_->eq_active[i] = eq_active_[i];
|
||||
} else {
|
||||
eq_active_[i] = d_->eq_active[i];
|
||||
}
|
||||
if (eq_active_prev_[i] != eq_active_[i]) {
|
||||
pending_.ui_update_equality = true;
|
||||
eq_active_prev_[i] = eq_active_[i];
|
||||
}
|
||||
}
|
||||
|
||||
// in passive mode, synchronize user's mjModel with changes made via the UI
|
||||
if (is_passive_) {
|
||||
// synchronize mjModel.opt
|
||||
@@ -2107,8 +2159,17 @@ void Simulate::Sync() {
|
||||
if (!is_passive_) {
|
||||
mjv_updateScene(m_, d_, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn);
|
||||
} else {
|
||||
mjv_copyModel(m_passive_, m_);
|
||||
mjv_copyData(d_passive_, m_passive_, d_);
|
||||
if (state_only) {
|
||||
int state_size = mj_stateSize(m_, mjSTATE_INTEGRATION);
|
||||
mjtNum* state = new mjtNum[state_size];
|
||||
mj_getState(m_, d_, state, mjSTATE_INTEGRATION);
|
||||
mj_setState(m_passive_, d_passive_, state, mjSTATE_INTEGRATION);
|
||||
mj_forward(m_passive_, d_passive_);
|
||||
delete[] state;
|
||||
} else {
|
||||
mjv_copyModel(m_passive_, m_);
|
||||
mjv_copyData(d_passive_, m_passive_, d_);
|
||||
}
|
||||
|
||||
// append geoms from user_scn to scratch space
|
||||
if (user_scn) {
|
||||
@@ -2261,6 +2322,12 @@ void Simulate::LoadOnRenderThread() {
|
||||
actuator_names_.emplace_back(this->m_->names + this->m_->name_actuatoradr[i]);
|
||||
}
|
||||
|
||||
equality_names_.clear();
|
||||
equality_names_.reserve(this->m_->neq);
|
||||
for (int i = 0; i < this->m_->neq; ++i) {
|
||||
equality_names_.emplace_back(this->m_->names + this->m_->name_eqadr[i]);
|
||||
}
|
||||
|
||||
qpos_.resize(this->m_->nq);
|
||||
std::memcpy(qpos_.data(), this->d_->qpos, sizeof(this->d_->qpos[0]) * this->m_->nq);
|
||||
qpos_prev_ = qpos_;
|
||||
@@ -2269,6 +2336,10 @@ void Simulate::LoadOnRenderThread() {
|
||||
std::memcpy(ctrl_.data(), this->d_->ctrl, sizeof(this->d_->ctrl[0]) * this->m_->nu);
|
||||
ctrl_prev_ = ctrl_;
|
||||
|
||||
eq_active_.resize(this->m_->neq);
|
||||
std::memcpy(eq_active_.data(), this->d_->eq_active, sizeof(this->d_->eq_active[0]) * this->m_->neq);
|
||||
eq_active_prev_ = eq_active_;
|
||||
|
||||
// allocate history buffer: smaller of {2000 states, 100 MB}
|
||||
if (!this->is_passive_) {
|
||||
constexpr int kMaxHistoryBytes = 1e8;
|
||||
@@ -2518,6 +2589,13 @@ void Simulate::Render() {
|
||||
pending_.ui_update_ctrl = false;
|
||||
}
|
||||
|
||||
if (pending_.ui_update_equality) {
|
||||
if (this->ui1_enable && this->ui1.sect[SECT_EQUALITY].state) {
|
||||
mjui_update(SECT_EQUALITY, -1, &this->ui1, &this->uistate, &this->platform_ui->mjr_context());
|
||||
}
|
||||
pending_.ui_update_equality = false;
|
||||
}
|
||||
|
||||
// render scene
|
||||
mjr_render(rect, &this->scn, &this->platform_ui->mjr_context());
|
||||
|
||||
|
||||
+9
-3
@@ -54,9 +54,10 @@ class Simulate {
|
||||
std::unique_ptr<PlatformUIAdapter> platform_ui_adapter,
|
||||
mjvCamera* cam, mjvOption* opt, mjvPerturb* pert, bool is_passive);
|
||||
|
||||
// Synchronize mjModel and mjData state with UI inputs, and update
|
||||
// visualization.
|
||||
void Sync();
|
||||
// Synchronize state with UI inputs, and update visualization. If state_only
|
||||
// is false mjData and mjModel will be updated, otherwise only the subset of
|
||||
// mjData corresponding to mjSTATE_INTEGRATION will be synced.
|
||||
void Sync(bool state_only = false);
|
||||
|
||||
void UpdateHField(int hfieldid);
|
||||
void UpdateMesh(int meshid);
|
||||
@@ -122,6 +123,8 @@ class Simulate {
|
||||
std::vector<std::optional<std::pair<mjtNum, mjtNum>>> actuator_ctrlrange_;
|
||||
std::vector<std::string> actuator_names_;
|
||||
|
||||
std::vector<std::string> equality_names_;
|
||||
|
||||
std::vector<mjtNum> history_; // history buffer (nhistory x state_size)
|
||||
|
||||
// mjModel and mjData fields that can be modified by the user through the GUI
|
||||
@@ -129,6 +132,8 @@ class Simulate {
|
||||
std::vector<mjtNum> qpos_prev_;
|
||||
std::vector<mjtNum> ctrl_;
|
||||
std::vector<mjtNum> ctrl_prev_;
|
||||
std::vector<mjtByte> eq_active_;
|
||||
std::vector<mjtByte> eq_active_prev_;
|
||||
|
||||
// in passive mode the user owns m_ and d_, these "passive" instances are
|
||||
// owned by Simulate, updated from the user by the Sync() method
|
||||
@@ -167,6 +172,7 @@ class Simulate {
|
||||
bool ui_update_visualization;
|
||||
bool ui_update_joint;
|
||||
bool ui_update_ctrl;
|
||||
bool ui_update_equality;
|
||||
bool ui_remake_ctrl;
|
||||
} pending_ = {};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user