Merge main branch from mujoco
This commit is contained in:
@@ -29,7 +29,7 @@ set(MUJOCO_DEP_VERSION_lodepng
|
||||
|
||||
project(
|
||||
mujoco_simulate
|
||||
VERSION 3.3.1
|
||||
VERSION 3.3.3
|
||||
DESCRIPTION "MuJoCo simulate binaries"
|
||||
HOMEPAGE_URL "https://mujoco.org"
|
||||
)
|
||||
@@ -114,6 +114,7 @@ target_sources(
|
||||
target_compile_options(platform_ui_adapter PRIVATE ${MUJOCO_SIMULATE_COMPILE_OPTIONS})
|
||||
if(APPLE)
|
||||
target_sources(platform_ui_adapter PUBLIC glfw_corevideo.h PRIVATE glfw_corevideo.mm)
|
||||
set_source_files_properties(glfw_corevideo.mm PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations)
|
||||
target_link_libraries(platform_ui_adapter PUBLIC "-framework CoreVideo")
|
||||
endif()
|
||||
target_include_directories(
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ extern "C" {
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
#include <sys/errno.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
+198
-119
@@ -298,7 +298,7 @@ void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) {
|
||||
memset(sim->figcost.linepnt, 0, mjMAXLINE*sizeof(int));
|
||||
|
||||
// number of islands that have diagnostics
|
||||
int nisland = mjMIN(d->solver_nisland, mjNISLAND);
|
||||
int nisland = mjMAX(1, mjMIN(d->nisland, mjNISLAND));
|
||||
|
||||
// iterate over islands
|
||||
for (int k=0; k < nisland; k++) {
|
||||
@@ -328,7 +328,7 @@ void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) {
|
||||
sim->figconstraint.linedata[start + 4][2*i] = i;
|
||||
|
||||
// y
|
||||
int nefc = nisland == 1 ? d->nefc : d->island_efcnum[k];
|
||||
int nefc = nisland == 1 ? d->nefc : d->island_nefc[k];
|
||||
sim->figconstraint.linedata[start + 0][2*i+1] = nefc;
|
||||
const mjSolverStat* stat = d->solver + k*mjNSOLVER + i;
|
||||
sim->figconstraint.linedata[start + 1][2*i+1] = stat->nactive;
|
||||
@@ -413,7 +413,7 @@ void UpdateProfiler(mj::Simulate* sim, const mjModel* m, const mjData* d) {
|
||||
static_cast<float>(d->nefc),
|
||||
static_cast<float>(sqrt_nnz),
|
||||
static_cast<float>(d->ncon),
|
||||
static_cast<float>(solver_niter)
|
||||
static_cast<float>(solver_niter) / nisland
|
||||
};
|
||||
|
||||
// update figsize
|
||||
@@ -464,7 +464,7 @@ void InitializeSensor(mj::Simulate* sim) {
|
||||
// title
|
||||
mju::strcpy_arr(figsensor.title, "Sensor data");
|
||||
|
||||
// y-tick nubmer format
|
||||
// y-tick number format
|
||||
mju::strcpy_arr(figsensor.yformat, "%.1f");
|
||||
|
||||
// grid size
|
||||
@@ -546,6 +546,16 @@ void ShowFigure(mj::Simulate* sim, mjrRect viewport, mjvFigure* fig){
|
||||
mjr_figure(viewport, fig, &sim->platform_ui->mjr_context());
|
||||
}
|
||||
|
||||
void ShowOverlayText(mj::Simulate* sim, mjrRect viewport, int font, int gridpos,
|
||||
std::string text1, std::string text2) {
|
||||
mjr_overlay(font, gridpos, viewport, text1.c_str(), text2.c_str(),
|
||||
&sim->platform_ui->mjr_context());
|
||||
}
|
||||
|
||||
void ShowImage(mj::Simulate* sim, mjrRect viewport, const unsigned char* image) {
|
||||
mjr_drawPixels(image, nullptr, viewport, &sim->platform_ui->mjr_context());
|
||||
}
|
||||
|
||||
// load state from history buffer
|
||||
static void LoadScrubState(mj::Simulate* sim) {
|
||||
// get index into circular buffer
|
||||
@@ -572,7 +582,7 @@ void UpdateInfoText(mj::Simulate* sim, const mjModel* m, const mjData* d,
|
||||
char tmp[20];
|
||||
|
||||
// number of islands with statistics
|
||||
int nisland = mjMIN(d->solver_nisland, mjNISLAND);
|
||||
int nisland = mjMAX(1, mjMIN(d->nisland, mjNISLAND));
|
||||
|
||||
// compute solver error (maximum over islands)
|
||||
mjtNum solerr = 0;
|
||||
@@ -679,7 +689,7 @@ void UpdateWatch(mj::Simulate* sim, const mjModel* m, const mjData* d) {
|
||||
|
||||
// make physics section of UI
|
||||
void MakePhysicsSection(mj::Simulate* sim) {
|
||||
mjOption* opt = sim->is_passive_ ? &sim->scnstate_.model.opt : &sim->m_->opt;
|
||||
mjOption* opt = sim->is_passive_ ? &sim->m_passive_->opt : &sim->m_->opt;
|
||||
mjuiDef defPhysics[] = {
|
||||
{mjITEM_SECTION, "Physics", mjPRESERVE, nullptr, "AP"},
|
||||
{mjITEM_SELECT, "Integrator", 2, &(opt->integrator), "Euler\nRK4\nimplicit\nimplicitfast"},
|
||||
@@ -863,13 +873,13 @@ void MakeRenderingSection(mj::Simulate* sim, const mjModel* m) {
|
||||
|
||||
// make visualization section of UI
|
||||
void MakeVisualizationSection(mj::Simulate* sim, const mjModel* m) {
|
||||
mjStatistic* stat = sim->is_passive_ ? &sim->scnstate_.model.stat : &sim->m_->stat;
|
||||
mjVisual* vis = sim->is_passive_ ? &sim->scnstate_.model.vis : &sim->m_->vis;
|
||||
mjStatistic* stat = sim->is_passive_ ? &sim->m_passive_->stat : &sim->m_->stat;
|
||||
mjVisual* vis = sim->is_passive_ ? &sim->m_passive_->vis : &sim->m_->vis;
|
||||
|
||||
mjuiDef defVisualization[] = {
|
||||
{mjITEM_SECTION, "Visualization", mjPRESERVE, nullptr, "AV"},
|
||||
{mjITEM_SEPARATOR, "Headlight", 1},
|
||||
{mjITEM_RADIO, "Active", 5, &(vis->headlight.active), "Off\nOn"},
|
||||
{mjITEM_RADIO, "Active", 2, &(vis->headlight.active), "Off\nOn"},
|
||||
{mjITEM_EDITFLOAT, "Ambient", 2, &(vis->headlight.ambient), "3"},
|
||||
{mjITEM_EDITFLOAT, "Diffuse", 2, &(vis->headlight.diffuse), "3"},
|
||||
{mjITEM_EDITFLOAT, "Specular", 2, &(vis->headlight.specular), "3"},
|
||||
@@ -882,7 +892,7 @@ void MakeVisualizationSection(mj::Simulate* sim, const mjModel* m) {
|
||||
{mjITEM_BUTTON, "Align", 2, nullptr, "CA"},
|
||||
{mjITEM_SEPARATOR, "Global", 1},
|
||||
{mjITEM_EDITNUM, "Extent", 2, &(stat->extent), "1"},
|
||||
{mjITEM_RADIO, "Inertia", 5, &(vis->global.ellipsoidinertia), "Box\nEllipsoid"},
|
||||
{mjITEM_RADIO, "Inertia", 2, &(vis->global.ellipsoidinertia), "Box\nEllipsoid"},
|
||||
{mjITEM_RADIO, "BVH active", 5, &(vis->global.bvactive), "False\nTrue"},
|
||||
{mjITEM_SEPARATOR, "Map", 1},
|
||||
{mjITEM_EDITFLOAT, "Stiffness", 2, &(vis->map.stiffness), "1"},
|
||||
@@ -1141,17 +1151,74 @@ void AlignAndScaleView(mj::Simulate* sim, const mjModel* m) {
|
||||
}
|
||||
|
||||
|
||||
// copy qpos to clipboard as key
|
||||
void CopyPose(mj::Simulate* sim, const mjModel* m, const mjData* d) {
|
||||
char clipboard[5000] = "<key qpos='";
|
||||
// copy state to clipboard as key
|
||||
void CopyKey(mj::Simulate* sim, const mjModel* m, const mjData* d, bool fp) {
|
||||
char clipboard[5000] = "<key\n";
|
||||
char buf[200];
|
||||
const char p_regular[] = "%g";
|
||||
const char p_full[] = "%-22.16g";
|
||||
const char* format = fp ? p_full : p_regular;
|
||||
|
||||
// prepare string
|
||||
for (int i=0; i<m->nq; i++) {
|
||||
mju::sprintf_arr(buf, i==m->nq-1 ? "%g" : "%g ", d->qpos[i]);
|
||||
// time
|
||||
mju::strcat_arr(clipboard, " time=\"");
|
||||
mju::sprintf_arr(buf, format, d->time);
|
||||
mju::strcat_arr(clipboard, buf);
|
||||
|
||||
// qpos
|
||||
mju::strcat_arr(clipboard, "\"\n qpos=\"");
|
||||
for (int i = 0; i < m->nq; i++) {
|
||||
mju::sprintf_arr(buf, format, d->qpos[i]);
|
||||
if (i < m->nq-1) mju::strcat_arr(buf, " ");
|
||||
mju::strcat_arr(clipboard, buf);
|
||||
}
|
||||
mju::strcat_arr(clipboard, "'/>");
|
||||
|
||||
// qvel
|
||||
mju::strcat_arr(clipboard, "\"\n qvel=\"");
|
||||
for (int i = 0; i < m->nv; i++) {
|
||||
mju::sprintf_arr(buf, format, d->qvel[i]);
|
||||
if (i < m->nv-1) mju::strcat_arr(buf, " ");
|
||||
mju::strcat_arr(clipboard, buf);
|
||||
}
|
||||
|
||||
// act
|
||||
if (m->na > 0) {
|
||||
mju::strcat_arr(clipboard, "\"\n act=\"");
|
||||
for (int i = 0; i < m->na; i++) {
|
||||
mju::sprintf_arr(buf, format, d->act[i]);
|
||||
if (i < m->na-1) mju::strcat_arr(buf, " ");
|
||||
mju::strcat_arr(clipboard, buf);
|
||||
}
|
||||
}
|
||||
|
||||
// ctrl
|
||||
if (m->nu > 0) {
|
||||
mju::strcat_arr(clipboard, "\"\n ctrl=\"");
|
||||
for (int i = 0; i < m->nu; i++) {
|
||||
mju::sprintf_arr(buf, format, d->ctrl[i]);
|
||||
if (i < m->nu-1) mju::strcat_arr(buf, " ");
|
||||
mju::strcat_arr(clipboard, buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (m->nmocap > 0) {
|
||||
// mocap_pos
|
||||
mju::strcat_arr(clipboard, "\"\n mpos=\"");
|
||||
for (int i = 0; i < 3*m->nmocap; i++) {
|
||||
mju::sprintf_arr(buf, format, d->mocap_pos[i]);
|
||||
if (i < 3*m->nmocap-1) mju::strcat_arr(buf, " ");
|
||||
mju::strcat_arr(clipboard, buf);
|
||||
}
|
||||
|
||||
// mocap_quat
|
||||
mju::strcat_arr(clipboard, "\"\n mquat=\"");
|
||||
for (int i = 0; i < 4*m->nmocap; i++) {
|
||||
mju::sprintf_arr(buf, format, d->mocap_quat[i]);
|
||||
if (i < 4*m->nmocap-1) mju::strcat_arr(buf, " ");
|
||||
mju::strcat_arr(clipboard, buf);
|
||||
}
|
||||
}
|
||||
|
||||
mju::strcat_arr(clipboard, "\"\n/>");
|
||||
|
||||
// copy to clipboard
|
||||
sim->platform_ui->SetClipboardString(clipboard);
|
||||
@@ -1412,8 +1479,9 @@ void UiEvent(mjuiState* state) {
|
||||
sim->pending_.align = true;
|
||||
break;
|
||||
|
||||
case 4: // Copy pose
|
||||
sim->pending_.copy_pose = true;
|
||||
case 4: // Copy key
|
||||
sim->pending_.copy_key = true;
|
||||
sim->pending_.copy_key_full_precision = sim->platform_ui->IsShiftKeyPressed();
|
||||
break;
|
||||
|
||||
case 5: // Adjust key
|
||||
@@ -1435,7 +1503,7 @@ void UiEvent(mjuiState* state) {
|
||||
|
||||
// physics section
|
||||
else if (it && it->sectionid==SECT_PHYSICS && sim->m_) {
|
||||
mjOption* opt = sim->is_passive_ ? &sim->scnstate_.model.opt : &sim->m_->opt;
|
||||
mjOption* opt = sim->is_passive_ ? &sim->m_passive_->opt : &sim->m_->opt;
|
||||
|
||||
// update disable flags in mjOption
|
||||
opt->disableflags = 0;
|
||||
@@ -1697,15 +1765,14 @@ void UiEvent(mjuiState* state) {
|
||||
return;
|
||||
}
|
||||
|
||||
// local pointers used below
|
||||
mjModel* model = sim->is_passive_ ? sim->m_passive_ : sim->m_;
|
||||
mjData* data = sim->is_passive_ ? sim->d_passive_ : sim->d_;
|
||||
|
||||
// 3D scroll
|
||||
if (state->type==mjEVENT_SCROLL && state->mouserect==3) {
|
||||
if (state->type==mjEVENT_SCROLL && state->mouserect==3 && model) {
|
||||
// emulate vertical mouse motion = 2% of window height
|
||||
if (sim->m_ && !sim->is_passive_) {
|
||||
mjv_moveCamera(sim->m_, mjMOUSE_ZOOM, 0, -zoom_increment*state->sy, &sim->scn, &sim->cam);
|
||||
} else {
|
||||
mjv_moveCameraFromState(
|
||||
&sim->scnstate_, mjMOUSE_ZOOM, 0, -zoom_increment*state->sy, &sim->scn, &sim->cam);
|
||||
}
|
||||
mjv_moveCamera(model, mjMOUSE_ZOOM, 0, -zoom_increment*state->sy, &sim->scn, &sim->cam);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1761,25 +1828,11 @@ void UiEvent(mjuiState* state) {
|
||||
// move perturb or camera
|
||||
mjrRect r = state->rect[3];
|
||||
if (sim->pert.active) {
|
||||
if (!sim->is_passive_) {
|
||||
mjv_movePerturb(
|
||||
sim->m_, sim->d_, action, state->dx / r.height, -state->dy / r.height,
|
||||
&sim->scn, &sim->pert);
|
||||
} else {
|
||||
mjv_movePerturbFromState(
|
||||
&sim->scnstate_, action, state->dx / r.height, -state->dy / r.height,
|
||||
&sim->scn, &sim->pert);
|
||||
}
|
||||
mjv_movePerturb(model, data, action, state->dx / r.height, -state->dy / r.height,
|
||||
&sim->scn, &sim->pert);
|
||||
} else {
|
||||
if (!sim->is_passive_) {
|
||||
mjv_moveCamera(
|
||||
sim->m_, action, state->dx / r.height, -state->dy / r.height,
|
||||
&sim->scn, &sim->cam);
|
||||
} else {
|
||||
mjv_moveCameraFromState(
|
||||
&sim->scnstate_, action, state->dx / r.height, -state->dy / r.height,
|
||||
&sim->scn, &sim->cam);
|
||||
}
|
||||
mjv_moveCamera(model, action, state->dx / r.height, -state->dy / r.height,
|
||||
&sim->scn, &sim->cam);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1813,10 +1866,11 @@ Simulate::Simulate(std::unique_ptr<PlatformUIAdapter> platform_ui,
|
||||
platform_ui(std::move(platform_ui)),
|
||||
uistate(this->platform_ui->state()) {
|
||||
mjv_defaultScene(&scn);
|
||||
mjv_defaultSceneState(&scnstate_);
|
||||
}
|
||||
|
||||
// synchronize model and data
|
||||
|
||||
//------------------------- Synchronize render and physics threads ---------------------------------
|
||||
|
||||
// operations which require holding the mutex, prevents racing with physics thread
|
||||
void Simulate::Sync() {
|
||||
MutexLock lock(this->mtx);
|
||||
@@ -1877,48 +1931,30 @@ void Simulate::Sync() {
|
||||
}
|
||||
}
|
||||
|
||||
// in passive mode, synchronize user's mjModel with changes made via the UI
|
||||
if (is_passive_) {
|
||||
// synchronize m_->opt with changes made via the UI
|
||||
#define X(name) \
|
||||
if (IsDifferent(scnstate_.model.opt.name, mjopt_prev_.name)) { \
|
||||
pending_.ui_update_physics = true; \
|
||||
Copy(m_->opt.name, scnstate_.model.opt.name); \
|
||||
}
|
||||
// synchronize mjModel.opt
|
||||
if (std::memcmp(&m_passive_->opt, &mjopt_prev_, sizeof(mjOption))) {
|
||||
pending_.ui_update_physics = true;
|
||||
m_->opt = m_passive_->opt;
|
||||
}
|
||||
|
||||
X(timestep);
|
||||
X(apirate);
|
||||
X(impratio);
|
||||
X(tolerance);
|
||||
X(noslip_tolerance);
|
||||
X(ccd_tolerance);
|
||||
X(gravity);
|
||||
X(wind);
|
||||
X(magnetic);
|
||||
X(density);
|
||||
X(viscosity);
|
||||
X(o_margin);
|
||||
X(o_solref);
|
||||
X(o_solimp);
|
||||
X(o_friction);
|
||||
X(integrator);
|
||||
X(cone);
|
||||
X(jacobian);
|
||||
X(solver);
|
||||
X(iterations);
|
||||
X(noslip_iterations);
|
||||
X(ccd_iterations);
|
||||
X(disableflags);
|
||||
X(enableflags);
|
||||
X(disableactuator);
|
||||
X(sdf_initpoints);
|
||||
X(sdf_iterations);
|
||||
// synchronize mjModel.vis
|
||||
if (std::memcmp(&m_passive_->vis, &mjvis_prev_, sizeof(mjVisual))) {
|
||||
pending_.ui_update_visualization = true;
|
||||
m_->vis = m_passive_->vis;
|
||||
}
|
||||
|
||||
#undef X
|
||||
// synchronize mjModel.stat
|
||||
if (std::memcmp(&m_passive_->stat, &mjstat_prev_, sizeof(mjStatistic))) {
|
||||
pending_.ui_update_visualization = true;
|
||||
m_->stat = m_passive_->stat;
|
||||
}
|
||||
|
||||
// synchronize number of mjWARN_VGEOMFULL warnings
|
||||
if (scnstate_.data.warning[mjWARN_VGEOMFULL].number > warn_vgeomfull_prev_) {
|
||||
if (d_passive_->warning[mjWARN_VGEOMFULL].number > warn_vgeomfull_prev_) {
|
||||
d_->warning[mjWARN_VGEOMFULL].number +=
|
||||
scnstate_.data.warning[mjWARN_VGEOMFULL].number - warn_vgeomfull_prev_;
|
||||
d_passive_->warning[mjWARN_VGEOMFULL].number - warn_vgeomfull_prev_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1967,9 +2003,10 @@ void Simulate::Sync() {
|
||||
pending_.align = false;
|
||||
}
|
||||
|
||||
if (pending_.copy_pose) {
|
||||
CopyPose(this, m_, d_);
|
||||
pending_.copy_pose = false;
|
||||
if (pending_.copy_key) {
|
||||
CopyKey(this, m_, d_, pending_.copy_key_full_precision);
|
||||
pending_.copy_key = false;
|
||||
pending_.copy_key_full_precision = false;
|
||||
}
|
||||
|
||||
if (pending_.load_from_history) {
|
||||
@@ -2066,25 +2103,19 @@ void Simulate::Sync() {
|
||||
pending_.select = false;
|
||||
}
|
||||
|
||||
// update scene
|
||||
// update scene or sync data from user in passive mode
|
||||
if (!is_passive_) {
|
||||
mjv_updateScene(m_, d_, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn);
|
||||
} else {
|
||||
mjv_updateSceneState(m_, d_, &this->opt, &scnstate_);
|
||||
mjv_copyModel(m_passive_, m_);
|
||||
mjv_copyData(d_passive_, m_passive_, d_);
|
||||
|
||||
// append geoms from user_scn to scnstate_ scratch space
|
||||
// append geoms from user_scn to scratch space
|
||||
if (user_scn) {
|
||||
int ngeom = user_scn->ngeom;
|
||||
int maxgeom = scnstate_.scratch.maxgeom - scnstate_.scratch.ngeom;
|
||||
if (ngeom > maxgeom) {
|
||||
mj_warning(d_, mjWARN_VGEOMFULL, scnstate_.scratch.maxgeom);
|
||||
ngeom = maxgeom;
|
||||
}
|
||||
if (ngeom > 0) {
|
||||
std::memcpy(scnstate_.scratch.geoms + scnstate_.scratch.ngeom,
|
||||
user_scn->geoms,
|
||||
sizeof(mjvGeom) * ngeom);
|
||||
scnstate_.scratch.ngeom += ngeom;
|
||||
user_scn_geoms_.clear();
|
||||
user_scn_geoms_.reserve(user_scn->ngeom);
|
||||
for (int i = 0; i < user_scn->ngeom; ++i) {
|
||||
user_scn_geoms_.push_back(user_scn->geoms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2100,8 +2131,10 @@ void Simulate::Sync() {
|
||||
Copy(user_scn_flags_prev_, user_scn->flags);
|
||||
}
|
||||
|
||||
mjopt_prev_ = scnstate_.model.opt;
|
||||
warn_vgeomfull_prev_ = scnstate_.data.warning[mjWARN_VGEOMFULL].number;
|
||||
mjopt_prev_ = m_passive_->opt;
|
||||
mjvis_prev_ = m_passive_->vis;
|
||||
mjstat_prev_ = m_passive_->stat;
|
||||
warn_vgeomfull_prev_ = d_passive_->warning[mjWARN_VGEOMFULL].number;
|
||||
}
|
||||
|
||||
// update settings
|
||||
@@ -2260,15 +2293,8 @@ void Simulate::LoadOnRenderThread() {
|
||||
}
|
||||
}
|
||||
|
||||
// re-create scene and context
|
||||
// re-create scene
|
||||
mjv_makeScene(this->m_, &this->scn, kMaxGeom);
|
||||
if (this->is_passive_) {
|
||||
mjopt_prev_ = m_->opt;
|
||||
opt_prev_ = opt;
|
||||
cam_prev_ = cam;
|
||||
warn_vgeomfull_prev_ = d_->warning[mjWARN_VGEOMFULL].number;
|
||||
mjv_makeSceneState(this->m_, this->d_, &this->scnstate_, kMaxGeom);
|
||||
}
|
||||
|
||||
this->platform_ui->RefreshMjrContext(this->m_, 50*(this->font+1));
|
||||
UiModify(&this->ui0, &this->uistate, &this->platform_ui->mjr_context());
|
||||
@@ -2297,12 +2323,18 @@ void Simulate::LoadOnRenderThread() {
|
||||
mju::strcpy_arr(this->previous_filename, this->filename);
|
||||
}
|
||||
|
||||
// update scene
|
||||
// update scene in managed mode, in passive mode copy data from user (update in RenderLoop)
|
||||
if (!is_passive_) {
|
||||
mjv_updateScene(this->m_, this->d_,
|
||||
&this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn);
|
||||
mjv_updateScene(this->m_, this->d_, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn);
|
||||
} else {
|
||||
mjv_updateSceneState(this->m_, this->d_, &this->opt, &this->scnstate_);
|
||||
mjopt_prev_ = m_->opt;
|
||||
opt_prev_ = opt;
|
||||
cam_prev_ = cam;
|
||||
warn_vgeomfull_prev_ = d_->warning[mjWARN_VGEOMFULL].number;
|
||||
|
||||
// full copy on init
|
||||
m_passive_ = mj_copyModel(nullptr, m_);
|
||||
d_passive_ = mj_copyData(nullptr, m_passive_, d_);
|
||||
}
|
||||
|
||||
// set window title to model name
|
||||
@@ -2423,6 +2455,13 @@ void Simulate::Render() {
|
||||
pending_.ui_update_physics = false;
|
||||
}
|
||||
|
||||
if (pending_.ui_update_visualization) {
|
||||
if (this->ui0_enable && this->ui0.sect[SECT_VISUALIZATION].state) {
|
||||
mjui0_update_section(this, SECT_VISUALIZATION);
|
||||
}
|
||||
pending_.ui_update_visualization = false;
|
||||
}
|
||||
|
||||
if (is_passive_) {
|
||||
if (this->ui0_enable && this->ui0.sect[SECT_RENDERING].state &&
|
||||
(cam_prev_.type != cam.type ||
|
||||
@@ -2593,10 +2632,38 @@ void Simulate::Render() {
|
||||
}
|
||||
|
||||
// user figures
|
||||
if (this->newfigurerequest.load() == 1) {
|
||||
this->user_figures_.clear();
|
||||
std::swap(this->user_figures_, this->user_figures_new_);
|
||||
int value = 1;
|
||||
this->newfigurerequest.compare_exchange_strong(value, 0);
|
||||
}
|
||||
for (auto& [viewport, figure] : this->user_figures_) {
|
||||
ShowFigure(this, viewport, &figure);
|
||||
}
|
||||
|
||||
// overlay text
|
||||
if (this->newtextrequest.load() == 1) {
|
||||
this->user_texts_.clear();
|
||||
std::swap(this->user_texts_, this->user_texts_new_);
|
||||
int value = 1;
|
||||
this->newtextrequest.compare_exchange_strong(value, 0);
|
||||
}
|
||||
for (auto& [font, gridpos, text1, text2] : this->user_texts_) {
|
||||
ShowOverlayText(this, rect, font, gridpos, text1, text2);
|
||||
}
|
||||
|
||||
// user images
|
||||
if (this->newimagerequest.load() == 1) {
|
||||
this->user_images_.clear();
|
||||
std::swap(this->user_images_, this->user_images_new_);
|
||||
int value = 1;
|
||||
this->newimagerequest.compare_exchange_strong(value, 0);
|
||||
}
|
||||
for (auto& [viewport, image] : this->user_images_) {
|
||||
ShowImage(this, viewport, image.get());
|
||||
}
|
||||
|
||||
// finalize
|
||||
this->platform_ui->SwapBuffers();
|
||||
}
|
||||
@@ -2714,11 +2781,22 @@ void Simulate::RenderLoop() {
|
||||
}
|
||||
|
||||
// update scene, doing a full sync if in fully managed mode
|
||||
if (!this->is_passive_) {
|
||||
if (!is_passive_) {
|
||||
Sync();
|
||||
} else {
|
||||
scnstate_.data.warning[mjWARN_VGEOMFULL].number += mjv_updateSceneFromState(
|
||||
&scnstate_, &this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn);
|
||||
} else if (m_passive_ && d_passive_) {
|
||||
// the user has called Sync() in their code
|
||||
mjv_updateScene(m_passive_, d_passive_,
|
||||
&this->opt, &this->pert, &this->cam, mjCAT_ALL, &this->scn);
|
||||
|
||||
// add user geoms to scene
|
||||
int nusergeom = user_scn_geoms_.size();
|
||||
int ngeom = std::min(nusergeom, this->scn.maxgeom - this->scn.ngeom);
|
||||
if (ngeom < nusergeom) {
|
||||
mj_warning(d_passive_, mjWARN_VGEOMFULL, this->scn.maxgeom);
|
||||
}
|
||||
std::memcpy(this->scn.geoms + this->scn.ngeom, user_scn_geoms_.data(),
|
||||
ngeom * sizeof(mjvGeom));
|
||||
this->scn.ngeom += ngeom;
|
||||
}
|
||||
} // MutexLock (unblocks simulation thread)
|
||||
|
||||
@@ -2739,7 +2817,8 @@ void Simulate::RenderLoop() {
|
||||
const MutexLock lock(this->mtx);
|
||||
mjv_freeScene(&this->scn);
|
||||
if (is_passive_) {
|
||||
mjv_freeSceneState(&scnstate_);
|
||||
mj_deleteData(d_passive_);
|
||||
mj_deleteModel(m_passive_);
|
||||
}
|
||||
|
||||
this->exitrequest.store(2);
|
||||
|
||||
+22
-4
@@ -23,6 +23,7 @@
|
||||
#include <optional>
|
||||
#include <ratio>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -129,8 +130,15 @@ class Simulate {
|
||||
std::vector<mjtNum> ctrl_;
|
||||
std::vector<mjtNum> ctrl_prev_;
|
||||
|
||||
mjvSceneState scnstate_;
|
||||
// in passive mode the user owns m_ and d_, these "passive" instances are
|
||||
// owned by Simulate, updated from the user by the Sync() method
|
||||
mjModel* m_passive_ = nullptr;
|
||||
mjData* d_passive_ = nullptr;
|
||||
std::vector<mjvGeom> user_scn_geoms_;
|
||||
|
||||
mjOption mjopt_prev_;
|
||||
mjVisual mjvis_prev_;
|
||||
mjStatistic mjstat_prev_;
|
||||
mjvOption opt_prev_;
|
||||
mjvCamera cam_prev_;
|
||||
|
||||
@@ -144,7 +152,8 @@ class Simulate {
|
||||
std::optional<std::string> print_data;
|
||||
bool reset;
|
||||
bool align;
|
||||
bool copy_pose;
|
||||
bool copy_key;
|
||||
bool copy_key_full_precision;
|
||||
bool load_from_history;
|
||||
bool load_key;
|
||||
bool save_key;
|
||||
@@ -155,6 +164,7 @@ class Simulate {
|
||||
bool ui_update_simulation;
|
||||
bool ui_update_physics;
|
||||
bool ui_update_rendering;
|
||||
bool ui_update_visualization;
|
||||
bool ui_update_joint;
|
||||
bool ui_update_ctrl;
|
||||
bool ui_remake_ctrl;
|
||||
@@ -196,6 +206,9 @@ class Simulate {
|
||||
std::atomic_int droploadrequest = 0;
|
||||
std::atomic_int screenshotrequest = 0;
|
||||
std::atomic_int uiloadrequest = 0;
|
||||
std::atomic_int newfigurerequest = 0;
|
||||
std::atomic_int newtextrequest = 0;
|
||||
std::atomic_int newimagerequest = 0;
|
||||
|
||||
// loadrequest
|
||||
// 3: display a loading message
|
||||
@@ -249,10 +262,15 @@ class Simulate {
|
||||
mjvFigure figsize = {};
|
||||
mjvFigure figsensor = {};
|
||||
|
||||
// additional user-defined visualization geoms (used in passive mode)
|
||||
// additional user-defined visualization
|
||||
mjvScene* user_scn = nullptr;
|
||||
mjtByte user_scn_flags_prev_[mjNRNDFLAG];
|
||||
std::vector<std::pair<mjrRect, mjvFigure>> user_figures_;
|
||||
std::vector<std::pair<mjrRect, mjvFigure>> user_figures_new_;
|
||||
std::vector<std::tuple<int, int, std::string, std::string>> user_texts_;
|
||||
std::vector<std::tuple<int, int, std::string, std::string>> user_texts_new_;
|
||||
std::vector<std::tuple<mjrRect, std::unique_ptr<unsigned char[]>>> user_images_;
|
||||
std::vector<std::tuple<mjrRect, std::unique_ptr<unsigned char[]>>> user_images_new_;
|
||||
|
||||
// OpenGL rendering and UI
|
||||
int refresh_rate = 60;
|
||||
@@ -293,7 +311,7 @@ class Simulate {
|
||||
{mjITEM_BUTTON, "Reset", 2, nullptr, " #259"},
|
||||
{mjITEM_BUTTON, "Reload", 5, nullptr, "CL"},
|
||||
{mjITEM_BUTTON, "Align", 2, nullptr, "CA"},
|
||||
{mjITEM_BUTTON, "Copy pose", 2, nullptr, "CC"},
|
||||
{mjITEM_BUTTON, "Copy state", 2, nullptr, "CC"},
|
||||
{mjITEM_SLIDERINT, "Key", 3, &this->key, "0 0"},
|
||||
{mjITEM_BUTTON, "Load key", 3},
|
||||
{mjITEM_BUTTON, "Save key", 3},
|
||||
|
||||
Reference in New Issue
Block a user