Merge pull request #419 from aftersomemath:simulate-python
PiperOrigin-RevId: 487496285 Change-Id: I17d15bd2a3886e5ce1631f4eb78afa94830d08cc
This commit is contained in:
@@ -6,6 +6,12 @@ Changelog
|
|||||||
Upcoming version (not yet released)
|
Upcoming version (not yet released)
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
Python bindings
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
- The ``simulate`` GUI is now available through the ``mujoco`` Python package. See :ref:`documentation<PyGUI>` for
|
||||||
|
details. (Contribution by `Levi Burner <https://github.com/aftersomemath>`_.)
|
||||||
|
|
||||||
General
|
General
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,38 @@ Minimal example
|
|||||||
mujoco.mj_step(model, data)
|
mujoco.mj_step(model, data)
|
||||||
print(data.geom_xpos)
|
print(data.geom_xpos)
|
||||||
|
|
||||||
|
.. _PyGUI:
|
||||||
|
|
||||||
|
Interactive visualizer
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
MuJoCo's interactive GUI (also known as the ``simulate`` application) is available as part of the Python package.
|
||||||
|
Three distinct use cases are supported:
|
||||||
|
|
||||||
|
- Launching as a standalone application:
|
||||||
|
|
||||||
|
* ``python -m mujoco.simulate`` launches an empty visualization session, where a model can be loaded by drag-and-drop.
|
||||||
|
* ``python -m mujoco.simulate --mjcf=/path/to/some/mjcf.xml`` launches a visualization session for the specified
|
||||||
|
model file.
|
||||||
|
|
||||||
|
- Launching from a Python program/script -- import the module via ``from mujoco import simulate`` and launch the GUI
|
||||||
|
using one of the following invocations:
|
||||||
|
|
||||||
|
* ``simulate.launch()`` launches an empty visualization session, where a model can be loaded by drag-and-drop.
|
||||||
|
* ``simulate.launch(model)`` launches a visualzation session for the given ``mjModel`` where the visualizer
|
||||||
|
internally creates its own instance of ``mjData``
|
||||||
|
* ``simulate.launch(model, data)`` is the same as above, except that the visualizer operates directly on the given
|
||||||
|
``mjData`` instance -- upon exit the ``data`` object will have been modified.
|
||||||
|
|
||||||
|
- Launching from an interactive Python session (aka REPL): when working interactively either in a ``python`` or
|
||||||
|
``ipython`` shell, the visualizer can be launched in a "passive" mode via ``simulate.launch_repl(model, data)``, where
|
||||||
|
the user remains in full control of modifying or stepping the physics. In this mode, the user can interact with the
|
||||||
|
visualizer using the mouse and keyboard as usual, however the physics will be frozen unless the user explicitly calls
|
||||||
|
``mj_step`` (or perform any other modification of the ``mjData`` or ``mjModel``) in the REPL terminal. Note that since
|
||||||
|
the visualizer does not modify ``mjData`` in this mode, mouse-drag perturbations will not work unless the user
|
||||||
|
explicitly handles incoming GUI perturbation events in the REPL session.
|
||||||
|
|
||||||
|
|
||||||
.. _PyNamed:
|
.. _PyNamed:
|
||||||
|
|
||||||
Named access
|
Named access
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
include LICENSE *.md
|
include LICENSE *.md
|
||||||
recursive-include mujoco *.h *.cc CMakeLists.txt
|
recursive-include mujoco *.h *.cc *.mm CMakeLists.txt Simulate*.cmake
|
||||||
recursive-include cmake *.cmake
|
recursive-include cmake *.cmake
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ cp "${package_dir}"/../LICENSE .
|
|||||||
mkdir cmake
|
mkdir cmake
|
||||||
cp "${package_dir}"/../cmake/*.cmake cmake
|
cp "${package_dir}"/../cmake/*.cmake cmake
|
||||||
|
|
||||||
|
# Copy over Simulate source code.
|
||||||
|
cp -r "${package_dir}"/../simulate mujoco
|
||||||
|
|
||||||
python setup.py sdist --formats=gztar
|
python setup.py sdist --formats=gztar
|
||||||
tar -tf dist/mujoco-*.tar.gz
|
tar -tf dist/mujoco-*.tar.gz
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ if(NOT TARGET mujoco)
|
|||||||
)
|
)
|
||||||
set_target_properties(mujoco PROPERTIES IMPORTED_SONAME "${MUJOCO_SONAME}")
|
set_target_properties(mujoco PROPERTIES IMPORTED_SONAME "${MUJOCO_SONAME}")
|
||||||
endif()
|
endif()
|
||||||
|
add_library(mujoco::mujoco ALIAS mujoco)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ==================== ABSEIL ==================================================
|
# ==================== ABSEIL ==================================================
|
||||||
@@ -190,6 +191,9 @@ findorfetch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# ==================== MUJOCO PYTHON BINDINGS ==================================
|
# ==================== MUJOCO PYTHON BINDINGS ==================================
|
||||||
|
set(SIMULATE_BUILD_EXECUTABLE OFF)
|
||||||
|
set(SIMULATE_GLFW_DYNAMIC_SYMBOLS ON)
|
||||||
|
add_subdirectory(simulate)
|
||||||
|
|
||||||
add_subdirectory(util)
|
add_subdirectory(util)
|
||||||
|
|
||||||
@@ -376,6 +380,14 @@ target_link_libraries(
|
|||||||
structs_header
|
structs_header
|
||||||
)
|
)
|
||||||
|
|
||||||
|
mujoco_pybind11_module(_simulate simulate.cc)
|
||||||
|
target_link_libraries(
|
||||||
|
_simulate
|
||||||
|
PRIVATE mujoco
|
||||||
|
mujoco::libsimulate
|
||||||
|
raw
|
||||||
|
structs_header)
|
||||||
|
|
||||||
set(LIBRARIES_FOR_WHEEL
|
set(LIBRARIES_FOR_WHEEL
|
||||||
"$<TARGET_FILE:_callbacks>"
|
"$<TARGET_FILE:_callbacks>"
|
||||||
"$<TARGET_FILE:_constants>"
|
"$<TARGET_FILE:_constants>"
|
||||||
@@ -384,6 +396,7 @@ set(LIBRARIES_FOR_WHEEL
|
|||||||
"$<TARGET_FILE:_functions>"
|
"$<TARGET_FILE:_functions>"
|
||||||
"$<TARGET_FILE:_render>"
|
"$<TARGET_FILE:_render>"
|
||||||
"$<TARGET_FILE:_rollout>"
|
"$<TARGET_FILE:_rollout>"
|
||||||
|
"$<TARGET_FILE:_simulate>"
|
||||||
"$<TARGET_FILE:_structs>"
|
"$<TARGET_FILE:_structs>"
|
||||||
"$<TARGET_FILE:mujoco>"
|
"$<TARGET_FILE:mujoco>"
|
||||||
)
|
)
|
||||||
@@ -411,6 +424,7 @@ if(MUJOCO_PYTHON_MAKE_WHEEL)
|
|||||||
_functions
|
_functions
|
||||||
_render
|
_render
|
||||||
_rollout
|
_rollout
|
||||||
|
_simulate
|
||||||
_structs
|
_structs
|
||||||
mujoco
|
mujoco
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2022 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
|
||||||
|
//
|
||||||
|
// http://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 <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <simulate.h>
|
||||||
|
#include "raw.h"
|
||||||
|
#include "structs.h"
|
||||||
|
#include <pybind11/detail/common.h>
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
|
namespace mujoco::python {
|
||||||
|
namespace {
|
||||||
|
PYBIND11_MODULE(_simulate, pymodule) {
|
||||||
|
namespace py = ::pybind11;
|
||||||
|
using SimulateMutex = decltype(mujoco::Simulate::mtx);
|
||||||
|
|
||||||
|
py::class_<SimulateMutex>(pymodule, "SimulateMutex")
|
||||||
|
.def(
|
||||||
|
"__enter__", [](SimulateMutex& mtx) { mtx.lock(); },
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def(
|
||||||
|
"__exit__",
|
||||||
|
[](SimulateMutex& mtx, py::handle, py::handle, py::handle) {
|
||||||
|
mtx.unlock();
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>());
|
||||||
|
|
||||||
|
py::class_<mujoco::Simulate>(pymodule, "Simulate")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def(
|
||||||
|
"renderloop",
|
||||||
|
[](mujoco::Simulate& simulate) { simulate.renderloop(); },
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def(
|
||||||
|
"load",
|
||||||
|
[](mujoco::Simulate& simulate, MjModelWrapper& m, MjDataWrapper& d) {
|
||||||
|
simulate.load("", m.get(), d.get());
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def("applyposepertubations", &mujoco::Simulate::applyposepertubations,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def("applyforceperturbations",
|
||||||
|
&mujoco::Simulate::applyforceperturbations,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
|
||||||
|
.def(
|
||||||
|
"lock",
|
||||||
|
[](mujoco::Simulate& simulate) -> SimulateMutex& {
|
||||||
|
return simulate.mtx;
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>(),
|
||||||
|
py::return_value_policy::reference)
|
||||||
|
.def_readonly("ctrlnoisestd", &mujoco::Simulate::ctrlnoisestd,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_readonly("ctrlnoiserate", &mujoco::Simulate::ctrlnoiserate,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
|
||||||
|
.def_readonly("real_time_index", &mujoco::Simulate::realTimeIndex,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_readwrite("speed_changed", &mujoco::Simulate::speedChanged,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_readwrite("measured_slowdown", &mujoco::Simulate::measuredSlowdown,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_readonly("refresh_rate", &mujoco::Simulate::refreshRate,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
|
||||||
|
.def_readonly("busywait", &mujoco::Simulate::busywait,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_readonly("run", &mujoco::Simulate::run,
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
|
||||||
|
.def_property_readonly(
|
||||||
|
"exitrequest",
|
||||||
|
[](mujoco::Simulate& simulate) {
|
||||||
|
return simulate.exitrequest.load();
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
|
||||||
|
.def_property_readonly(
|
||||||
|
"uiloadrequest",
|
||||||
|
[](mujoco::Simulate& simulate) {
|
||||||
|
return simulate.uiloadrequest.load();
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def(
|
||||||
|
"uiloadrequest_decrement",
|
||||||
|
[](mujoco::Simulate& simulate) {
|
||||||
|
simulate.uiloadrequest.fetch_sub(1);
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
|
||||||
|
.def_property(
|
||||||
|
"droploadrequest",
|
||||||
|
[](mujoco::Simulate& simulate) {
|
||||||
|
return simulate.droploadrequest.load();
|
||||||
|
},
|
||||||
|
[](mujoco::Simulate& simulate, bool droploadrequest) {
|
||||||
|
simulate.droploadrequest.store(droploadrequest);
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_property_readonly(
|
||||||
|
"dropfilename",
|
||||||
|
[](mujoco::Simulate& simulate) -> std::string {
|
||||||
|
return simulate.dropfilename;
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_property_readonly(
|
||||||
|
"filename",
|
||||||
|
[](mujoco::Simulate& simulate) -> std::string {
|
||||||
|
return simulate.filename;
|
||||||
|
},
|
||||||
|
py::call_guard<py::gil_scoped_release>())
|
||||||
|
.def_property(
|
||||||
|
"load_error",
|
||||||
|
[](mujoco::Simulate& simulate) -> std::string {
|
||||||
|
return simulate.loadError;
|
||||||
|
},
|
||||||
|
[](mujoco::Simulate& simulate, const std::string& error) {
|
||||||
|
std::strncpy(simulate.loadError, error.c_str(),
|
||||||
|
simulate.kMaxFilenameLength);
|
||||||
|
});
|
||||||
|
|
||||||
|
pymodule.def("setglfwdlhandle", [](std::uintptr_t dlhandle) {
|
||||||
|
mujoco::setglfwdlhandle(reinterpret_cast<void*>(dlhandle));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace mujoco::python
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
# Copyright 2022 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
|
||||||
|
#
|
||||||
|
# http://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.
|
||||||
|
# ==============================================================================
|
||||||
|
"""Python bindings for the Simulate GUI."""
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import code
|
||||||
|
import inspect
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import typing
|
||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
import glfw
|
||||||
|
import mujoco
|
||||||
|
from mujoco import _simulate
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
if not glfw._glfw: # pylint: disable=protected-access
|
||||||
|
raise RuntimeError('GLFW dynamic library handle is not available')
|
||||||
|
else:
|
||||||
|
_simulate.setglfwdlhandle(glfw._glfw._handle) # pylint: disable=protected-access
|
||||||
|
|
||||||
|
# Logarithmically spaced realtime slow-down coefficients (percent).
|
||||||
|
PERCENT_REALTIME = (
|
||||||
|
100, 80, 66, 50, 40, 33, 25, 20, 16, 13,
|
||||||
|
10, 8, 6.6, 5, 4, 3.3, 2.5, 2, 1.6, 1.3,
|
||||||
|
1, 0.8, 0.66, 0.5, 0.4, 0.33, 0.25, 0.2, 0.16, 0.13,
|
||||||
|
0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
# Maximum time mis-alignment before re-sync.
|
||||||
|
MAX_SYNC_MISALIGN = 0.1
|
||||||
|
|
||||||
|
# Fraction of refresh available for simulation.
|
||||||
|
SIM_REFRESH_FRACTION = 0.7
|
||||||
|
|
||||||
|
CallbackType = Callable[[mujoco.MjModel, mujoco.MjData], None]
|
||||||
|
|
||||||
|
Simulate = _simulate.Simulate
|
||||||
|
|
||||||
|
|
||||||
|
def _reload_from_file(simulate: Simulate, filename: str):
|
||||||
|
"""Loads an MJCF model into the Simulate GUI."""
|
||||||
|
try:
|
||||||
|
m = mujoco.MjModel.from_xml_path(filename)
|
||||||
|
except mujoco.FatalError as e:
|
||||||
|
m = None
|
||||||
|
simulate.load_error = str(e)
|
||||||
|
|
||||||
|
if m is not None:
|
||||||
|
d = mujoco.MjData(m)
|
||||||
|
simulate.load(m, d)
|
||||||
|
mujoco.mj_forward(m, d)
|
||||||
|
else:
|
||||||
|
d = None
|
||||||
|
|
||||||
|
return m, d
|
||||||
|
|
||||||
|
|
||||||
|
def _physics_loop(simulate: Simulate,
|
||||||
|
m: Optional[mujoco.MjModel],
|
||||||
|
d: Optional[mujoco.MjData]):
|
||||||
|
"""Physics loop for the Simulate GUI, to be run in a separate thread."""
|
||||||
|
ctrlnoise = None
|
||||||
|
if m is not None:
|
||||||
|
ctrlnoise = np.zeros((m.nu,))
|
||||||
|
|
||||||
|
# CPU-sim synchronization point.
|
||||||
|
synccpu = 0.0
|
||||||
|
syncsim = 0.0
|
||||||
|
|
||||||
|
# Run until asked to exit.
|
||||||
|
while not simulate.exitrequest:
|
||||||
|
if simulate.droploadrequest:
|
||||||
|
simulate.droploadrequest = 0
|
||||||
|
new_m, new_d = _reload_from_file(simulate, simulate.dropfilename)
|
||||||
|
if new_m is not None:
|
||||||
|
m = new_m
|
||||||
|
d = new_d
|
||||||
|
ctrlnoise = np.zeros((m.nu,))
|
||||||
|
|
||||||
|
if simulate.uiloadrequest:
|
||||||
|
simulate.uiloadrequest_decrement()
|
||||||
|
new_m, new_d = _reload_from_file(simulate, simulate.dropfilename)
|
||||||
|
if new_m is not None:
|
||||||
|
m = new_m
|
||||||
|
d = new_d
|
||||||
|
ctrlnoise = np.zeros((m.nu,))
|
||||||
|
|
||||||
|
# Sleep for 1 ms or yield, to let main thread run.
|
||||||
|
if simulate.run != 0 and simulate.busywait != 0:
|
||||||
|
time.sleep(0)
|
||||||
|
else:
|
||||||
|
time.sleep(0.001)
|
||||||
|
|
||||||
|
with simulate.lock():
|
||||||
|
if m is not None:
|
||||||
|
assert d is not None
|
||||||
|
if simulate.run:
|
||||||
|
# Record CPU time at start of iteration.
|
||||||
|
startcpu = glfw.get_time()
|
||||||
|
|
||||||
|
elapsedcpu = startcpu - synccpu
|
||||||
|
elapsedsim = d.time - syncsim
|
||||||
|
|
||||||
|
# Inject noise.
|
||||||
|
if simulate.ctrlnoisestd != 0.0:
|
||||||
|
# Convert rate and scale to discrete time (Ornstein–Uhlenbeck).
|
||||||
|
rate = math.exp(-m.opt.timestep / simulate.ctrlnoiserate)
|
||||||
|
scale = simulate.ctrlnoisestd * math.sqrt(1 - rate * rate)
|
||||||
|
|
||||||
|
for i in range(m.nu):
|
||||||
|
# Update noise.
|
||||||
|
ctrlnoise[i] = (
|
||||||
|
rate * ctrlnoise[i] + scale * mujoco.mju_standardNormal(None))
|
||||||
|
|
||||||
|
# Apply noise.
|
||||||
|
d.ctrl[i] = ctrlnoise[i]
|
||||||
|
|
||||||
|
# Requested slow-down factor.
|
||||||
|
slowdown = 100 / PERCENT_REALTIME[simulate.real_time_index]
|
||||||
|
|
||||||
|
# Misalignment: distance from target sim time > MAX_SYNC_MISALIGN.
|
||||||
|
misaligned = abs(elapsedcpu / slowdown -
|
||||||
|
elapsedsim) > MAX_SYNC_MISALIGN
|
||||||
|
|
||||||
|
# Out-of-sync (for any reason): reset sync times, step.
|
||||||
|
if (elapsedsim < 0 or elapsedcpu < 0 or synccpu == 0 or misaligned or
|
||||||
|
simulate.speed_changed):
|
||||||
|
# Re-sync.
|
||||||
|
synccpu = startcpu
|
||||||
|
syncsim = d.time
|
||||||
|
simulate.speed_changed = False
|
||||||
|
|
||||||
|
# Clear old perturbations, apply new.
|
||||||
|
d.xfrc_applied[:, :] = 0
|
||||||
|
simulate.applyposepertubations(0) # Move mocap bodies only.
|
||||||
|
simulate.applyforceperturbations()
|
||||||
|
|
||||||
|
# Run single step, let next iteration deal with timing.
|
||||||
|
mujoco.mj_step(m, d)
|
||||||
|
|
||||||
|
# In-sync: step until ahead of cpu.
|
||||||
|
else:
|
||||||
|
measured = False
|
||||||
|
prevsim = d.time
|
||||||
|
refreshtime = SIM_REFRESH_FRACTION / simulate.refresh_rate
|
||||||
|
# Step while sim lags behind CPU and within refreshtime.
|
||||||
|
while (((d.time - syncsim) * slowdown <
|
||||||
|
(glfw.get_time() - synccpu)) and
|
||||||
|
((glfw.get_time() - startcpu) < refreshtime)):
|
||||||
|
# Measure slowdown before first step.
|
||||||
|
if not measured and elapsedsim:
|
||||||
|
simulate.measured_slowdown = elapsedcpu / elapsedsim
|
||||||
|
measured = True
|
||||||
|
|
||||||
|
# Clear old perturbations, apply new.
|
||||||
|
d.xfrc_applied[:, :] = 0
|
||||||
|
simulate.applyposepertubations(0) # Move mocap bodies only.
|
||||||
|
simulate.applyforceperturbations()
|
||||||
|
|
||||||
|
# Call mj_step.
|
||||||
|
mujoco.mj_step(m, d)
|
||||||
|
|
||||||
|
# Break if reset.
|
||||||
|
if d.time < prevsim:
|
||||||
|
break
|
||||||
|
else: # simulate.run is False: GUI is paused.
|
||||||
|
# Apply pose perturbation.
|
||||||
|
simulate.applyposepertubations(1) # Move mocap and dynamic bodies.
|
||||||
|
|
||||||
|
# Run mj_forward, to update rendering and joint sliders.
|
||||||
|
mujoco.mj_forward(m, d)
|
||||||
|
|
||||||
|
|
||||||
|
def launch(model: Optional[mujoco.MjModel] = None,
|
||||||
|
data: Optional[mujoco.MjData] = None,
|
||||||
|
*,
|
||||||
|
run_physics_thread: bool = True) -> None:
|
||||||
|
"""Launches the Simulate GUI."""
|
||||||
|
if model is None and data is not None:
|
||||||
|
raise ValueError('mjData is specified but mjModel is not')
|
||||||
|
elif model is not None and data is None:
|
||||||
|
data = mujoco.MjData(model)
|
||||||
|
|
||||||
|
# The simulate object encapsulates the UI.
|
||||||
|
simulate = Simulate()
|
||||||
|
|
||||||
|
# Initialize GLFW.
|
||||||
|
if not glfw.init():
|
||||||
|
raise mujoco.FatalError('could not initialize GLFW')
|
||||||
|
|
||||||
|
atexit.register(glfw.terminate)
|
||||||
|
|
||||||
|
if run_physics_thread:
|
||||||
|
physics_thread = threading.Thread(
|
||||||
|
target=_physics_loop, args=(simulate, model, data))
|
||||||
|
physics_thread.start()
|
||||||
|
|
||||||
|
# Load the initial model, if one is given.
|
||||||
|
if model is not None:
|
||||||
|
t = threading.Thread(target=simulate.load, args=(model, data))
|
||||||
|
t.start()
|
||||||
|
del t
|
||||||
|
|
||||||
|
simulate.renderloop()
|
||||||
|
|
||||||
|
if run_physics_thread:
|
||||||
|
physics_thread.join()
|
||||||
|
|
||||||
|
|
||||||
|
def launch_repl(model: mujoco.MjModel, data: mujoco.MjData) -> None:
|
||||||
|
"""EXPERIMENTAL FEATURE: Launches the Simulate GUI in REPL mode."""
|
||||||
|
if typing.TYPE_CHECKING:
|
||||||
|
launch(model, data, run_physics_thread=False)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
import IPython # pylint: disable=g-import-not-at-top
|
||||||
|
has_ipython = True
|
||||||
|
except ImportError:
|
||||||
|
has_ipython = False
|
||||||
|
|
||||||
|
def start_shell(global_variables):
|
||||||
|
if has_ipython and IPython.get_ipython() is not None:
|
||||||
|
locals().update(global_variables)
|
||||||
|
IPython.embed()
|
||||||
|
else:
|
||||||
|
code.InteractiveConsole(locals=global_variables).interact()
|
||||||
|
|
||||||
|
repl_thread = threading.Thread(
|
||||||
|
target=start_shell, args=(inspect.stack()[1][0].f_globals,))
|
||||||
|
repl_thread.start()
|
||||||
|
launch(model, data, run_physics_thread=False)
|
||||||
|
repl_thread.join()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from absl import app # pylint: disable=g-import-not-at-top
|
||||||
|
from absl import flags # pylint: disable=g-import-not-at-top
|
||||||
|
|
||||||
|
_MJCF_PATH = flags.DEFINE_string('mjcf', None, 'Path to MJCF file.')
|
||||||
|
|
||||||
|
def main(argv) -> None:
|
||||||
|
del argv
|
||||||
|
if _MJCF_PATH.value is not None:
|
||||||
|
model = mujoco.MjModel.from_xml_path(os.path.expanduser(_MJCF_PATH.value))
|
||||||
|
launch(model)
|
||||||
|
else:
|
||||||
|
launch()
|
||||||
|
|
||||||
|
app.run(main)
|
||||||
+1
-1
@@ -70,7 +70,6 @@ def get_external_lib_patterns():
|
|||||||
else:
|
else:
|
||||||
return ['libmujoco.so.*']
|
return ['libmujoco.so.*']
|
||||||
|
|
||||||
|
|
||||||
def get_plugin_lib_patterns():
|
def get_plugin_lib_patterns():
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
return ['*.dll']
|
return ['*.dll']
|
||||||
@@ -322,6 +321,7 @@ setup(
|
|||||||
CMakeExtension('mujoco._functions'),
|
CMakeExtension('mujoco._functions'),
|
||||||
CMakeExtension('mujoco._render'),
|
CMakeExtension('mujoco._render'),
|
||||||
CMakeExtension('mujoco._rollout'),
|
CMakeExtension('mujoco._rollout'),
|
||||||
|
CMakeExtension('mujoco._simulate'),
|
||||||
CMakeExtension('mujoco._structs'),
|
CMakeExtension('mujoco._structs'),
|
||||||
],
|
],
|
||||||
python_requires='>=3.7',
|
python_requires='>=3.7',
|
||||||
|
|||||||
+119
-109
@@ -41,6 +41,9 @@ if(APPLE)
|
|||||||
enable_language(OBJCXX)
|
enable_language(OBJCXX)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(SIMULATE_BUILD_EXECUTABLE "Build the simulate executable binary." ON)
|
||||||
|
option(SIMULATE_GLFW_DYNAMIC_SYMBOLS "Whether to resolve GLFW symbols dynamically." OFF)
|
||||||
|
|
||||||
# Check if we are building as standalone project.
|
# Check if we are building as standalone project.
|
||||||
set(SIMULATE_STANDALONE OFF)
|
set(SIMULATE_STANDALONE OFF)
|
||||||
set(_INSTALL_SIMULATE ON)
|
set(_INSTALL_SIMULATE ON)
|
||||||
@@ -114,7 +117,8 @@ target_sources(
|
|||||||
target_include_directories(libsimulate PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(libsimulate PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_compile_definitions(libsimulate PUBLIC MJSIMULATE_STATIC)
|
target_compile_definitions(libsimulate PUBLIC MJSIMULATE_STATIC)
|
||||||
target_compile_options(libsimulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS})
|
target_compile_options(libsimulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS})
|
||||||
target_link_libraries(libsimulate PUBLIC glfw lodepng mujoco::mujoco)
|
target_link_libraries(libsimulate PUBLIC lodepng mujoco::mujoco)
|
||||||
|
target_include_directories(libsimulate PUBLIC $<TARGET_PROPERTY:glfw,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||||
target_link_options(libsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
|
target_link_options(libsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
@@ -122,126 +126,132 @@ if(APPLE)
|
|||||||
target_link_libraries(libsimulate PUBLIC "-framework Cocoa")
|
target_link_libraries(libsimulate PUBLIC "-framework Cocoa")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(SIMULATE_GLFW_DYNAMIC_SYMBOLS)
|
||||||
|
target_compile_definitions(libsimulate PUBLIC mjGLFW_DYNAMIC_SYMBOLS)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Build simulate executable
|
# Build simulate executable
|
||||||
if(APPLE)
|
if(SIMULATE_BUILD_EXECUTABLE)
|
||||||
set(SIMULATE_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../dist/mujoco.icns)
|
if(APPLE)
|
||||||
elseif(WIN32)
|
set(SIMULATE_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../dist/mujoco.icns)
|
||||||
set(SIMULATE_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../dist/simulate.rc)
|
elseif(WIN32)
|
||||||
else()
|
set(SIMULATE_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../dist/simulate.rc)
|
||||||
set(SIMULATE_RESOURCE_FILES "")
|
else()
|
||||||
endif()
|
set(SIMULATE_RESOURCE_FILES "")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(simulate main.cc array_safety.h ${SIMULATE_RESOURCE_FILES})
|
add_executable(simulate main.cc array_safety.h ${SIMULATE_RESOURCE_FILES})
|
||||||
target_compile_options(simulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS})
|
target_compile_options(simulate PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS})
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET simulate
|
TARGET simulate
|
||||||
PRE_BUILD
|
PRE_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../dist/mujoco.ico
|
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../dist/mujoco.ico
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_SOURCE_DIR}/mujoco.ico
|
COMMAND ${CMAKE_COMMAND} -E rm ${CMAKE_CURRENT_SOURCE_DIR}/mujoco.ico
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
simulate
|
|
||||||
libsimulate
|
|
||||||
mujoco::mujoco
|
|
||||||
glfw
|
|
||||||
Threads::Threads
|
|
||||||
lodepng
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_options(simulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
|
|
||||||
|
|
||||||
if(APPLE AND MUJOCO_BUILD_MACOS_FRAMEWORKS)
|
|
||||||
set_target_properties(
|
|
||||||
simulate
|
simulate
|
||||||
PROPERTIES INSTALL_RPATH @executable_path/../Frameworks
|
libsimulate
|
||||||
BUILD_WITH_INSTALL_RPATH TRUE
|
mujoco::mujoco
|
||||||
RESOURCE ${SIMULATE_RESOURCE_FILES}
|
glfw
|
||||||
MACOSX_BUNDLE TRUE
|
Threads::Threads
|
||||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../dist/Info.plist.simulate.in
|
lodepng
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "MuJoCo"
|
|
||||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.mujoco.mujoco"
|
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
|
||||||
MACOSX_BUNDLE_INFO_STRING ${PROJECT_VERSION}
|
|
||||||
MACOSX_BUNDLE_LONG_VERSION_STRING ${PROJECT_VERSION}
|
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}
|
|
||||||
MACOSX_BUNDLE_ICON_FILE "mujoco.icns"
|
|
||||||
MACOSX_BUNDLE_COPYRIGHT "Copyright 2021 DeepMind Technologies Limited."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
macro(embed_in_bundle target)
|
target_link_options(simulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
|
||||||
add_dependencies(${target} simulate)
|
|
||||||
|
if(APPLE AND MUJOCO_BUILD_MACOS_FRAMEWORKS)
|
||||||
set_target_properties(
|
set_target_properties(
|
||||||
${target}
|
simulate
|
||||||
PROPERTIES INSTALL_RPATH @executable_path/../Frameworks
|
PROPERTIES INSTALL_RPATH @executable_path/../Frameworks
|
||||||
BUILD_WITH_INSTALL_RPATH TRUE
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
RUNTIME_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:simulate>
|
RESOURCE ${SIMULATE_RESOURCE_FILES}
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../dist/Info.plist.simulate.in
|
||||||
|
MACOSX_BUNDLE_BUNDLE_NAME "MuJoCo"
|
||||||
|
MACOSX_BUNDLE_GUI_IDENTIFIER "org.mujoco.mujoco"
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_INFO_STRING ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_LONG_VERSION_STRING ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_ICON_FILE "mujoco.icns"
|
||||||
|
MACOSX_BUNDLE_COPYRIGHT "Copyright 2021 DeepMind Technologies Limited."
|
||||||
)
|
)
|
||||||
endmacro()
|
|
||||||
|
|
||||||
# Embed mujoco.framework inside the App bundle ane move the icon file over too.
|
macro(embed_in_bundle target)
|
||||||
add_custom_command(
|
add_dependencies(${target} simulate)
|
||||||
TARGET simulate
|
set_target_properties(
|
||||||
POST_BUILD
|
${target}
|
||||||
COMMAND mkdir -p $<TARGET_FILE_DIR:simulate>/../Frameworks
|
PROPERTIES INSTALL_RPATH @executable_path/../Frameworks
|
||||||
COMMAND rm -rf $<TARGET_FILE_DIR:simulate>/../Frameworks/mujoco.framework
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
COMMAND cp -a $<TARGET_FILE_DIR:mujoco::mujoco>/../../../mujoco.framework
|
RUNTIME_OUTPUT_DIRECTORY $<TARGET_FILE_DIR:simulate>
|
||||||
$<TARGET_FILE_DIR:simulate>/../Frameworks/
|
|
||||||
# Delete the symlink and the TBD, otherwise we can't sign and notarize.
|
|
||||||
COMMAND rm -rf $<TARGET_FILE_DIR:simulate>/../Frameworks/mujoco.framework/mujoco.tbd
|
|
||||||
COMMAND rm -rf
|
|
||||||
$<TARGET_FILE_DIR:simulate>/../Frameworks/mujoco.framework/Versions/A/libmujoco.dylib
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Do not install if macOS Bundles are created as RPATH is managed manually there.
|
|
||||||
if(APPLE AND MUJOCO_BUILD_MACOS_FRAMEWORKS)
|
|
||||||
set(_INSTALL_SIMULATE OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(_INSTALL_SIMULATE)
|
|
||||||
|
|
||||||
include(TargetAddRpath)
|
|
||||||
|
|
||||||
# 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}"
|
|
||||||
DEPENDS
|
|
||||||
MUJOCO_ENABLE_RPATH
|
|
||||||
)
|
|
||||||
|
|
||||||
install(
|
|
||||||
TARGETS simulate
|
|
||||||
EXPORT ${PROJECT_NAME}
|
|
||||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate
|
|
||||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
|
||||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
|
||||||
BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate
|
|
||||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT simulate
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT MUJOCO_SIMULATE_USE_SYSTEM_GLFW)
|
|
||||||
# We downloaded GLFW. Depending if it is a static or shared LIBRARY we might
|
|
||||||
# need to install it.
|
|
||||||
get_target_property(MJ_GLFW_LIBRARY_TYPE glfw TYPE)
|
|
||||||
if(MJ_GLFW_LIBRARY_TYPE STREQUAL SHARED_LIBRARY)
|
|
||||||
install(
|
|
||||||
TARGETS glfw
|
|
||||||
EXPORT ${PROJECT_NAME}
|
|
||||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate
|
|
||||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
|
||||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
|
||||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT simulate
|
|
||||||
)
|
)
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Embed mujoco.framework inside the App bundle ane move the icon file over too.
|
||||||
|
add_custom_command(
|
||||||
|
TARGET simulate
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND mkdir -p $<TARGET_FILE_DIR:simulate>/../Frameworks
|
||||||
|
COMMAND rm -rf $<TARGET_FILE_DIR:simulate>/../Frameworks/mujoco.framework
|
||||||
|
COMMAND cp -a $<TARGET_FILE_DIR:mujoco::mujoco>/../../../mujoco.framework
|
||||||
|
$<TARGET_FILE_DIR:simulate>/../Frameworks/
|
||||||
|
# Delete the symlink and the TBD, otherwise we can't sign and notarize.
|
||||||
|
COMMAND rm -rf $<TARGET_FILE_DIR:simulate>/../Frameworks/mujoco.framework/mujoco.tbd
|
||||||
|
COMMAND rm -rf
|
||||||
|
$<TARGET_FILE_DIR:simulate>/../Frameworks/mujoco.framework/Versions/A/libmujoco.dylib
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Do not install if macOS Bundles are created as RPATH is managed manually there.
|
||||||
|
if(APPLE AND MUJOCO_BUILD_MACOS_FRAMEWORKS)
|
||||||
|
set(_INSTALL_SIMULATE OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(_INSTALL_SIMULATE)
|
||||||
|
|
||||||
|
include(TargetAddRpath)
|
||||||
|
|
||||||
|
# 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}"
|
||||||
|
DEPENDS
|
||||||
|
MUJOCO_ENABLE_RPATH
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS simulate
|
||||||
|
EXPORT ${PROJECT_NAME}
|
||||||
|
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate
|
||||||
|
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
||||||
|
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
||||||
|
BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate
|
||||||
|
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT simulate
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT MUJOCO_SIMULATE_USE_SYSTEM_GLFW)
|
||||||
|
# We downloaded GLFW. Depending if it is a static or shared LIBRARY we might
|
||||||
|
# need to install it.
|
||||||
|
get_target_property(MJ_GLFW_LIBRARY_TYPE glfw TYPE)
|
||||||
|
if(MJ_GLFW_LIBRARY_TYPE STREQUAL SHARED_LIBRARY)
|
||||||
|
install(
|
||||||
|
TARGETS glfw
|
||||||
|
EXPORT ${PROJECT_NAME}
|
||||||
|
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate
|
||||||
|
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
||||||
|
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
|
||||||
|
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT simulate
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ const struct Glfw& Glfw(void* dlhandle) {
|
|||||||
|
|
||||||
// go/keep-sorted start
|
// go/keep-sorted start
|
||||||
mjGLFW_INITIALIZE_SYMBOL(glfwCreateWindow);
|
mjGLFW_INITIALIZE_SYMBOL(glfwCreateWindow);
|
||||||
|
mjGLFW_INITIALIZE_SYMBOL(glfwDestroyWindow);
|
||||||
mjGLFW_INITIALIZE_SYMBOL(glfwGetCursorPos);
|
mjGLFW_INITIALIZE_SYMBOL(glfwGetCursorPos);
|
||||||
mjGLFW_INITIALIZE_SYMBOL(glfwGetFramebufferSize);
|
mjGLFW_INITIALIZE_SYMBOL(glfwGetFramebufferSize);
|
||||||
mjGLFW_INITIALIZE_SYMBOL(glfwGetKey);
|
mjGLFW_INITIALIZE_SYMBOL(glfwGetKey);
|
||||||
@@ -115,10 +116,6 @@ const struct Glfw& Glfw(void* dlhandle) {
|
|||||||
|
|
||||||
#undef mjGLFW_INITIALIZE_SYMBOL
|
#undef mjGLFW_INITIALIZE_SYMBOL
|
||||||
|
|
||||||
#if defined(mjGLFW_DYNAMIC_SYMBOLS) && !defined(_MSC_VER)
|
|
||||||
dlclose(dlhandle);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return glfw;
|
return glfw;
|
||||||
}();
|
}();
|
||||||
return glfw;
|
return glfw;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ struct Glfw {
|
|||||||
#define mjGLFW_DECLARE_SYMBOL(func) decltype(&::func) func
|
#define mjGLFW_DECLARE_SYMBOL(func) decltype(&::func) func
|
||||||
// go/keep-sorted start
|
// go/keep-sorted start
|
||||||
mjGLFW_DECLARE_SYMBOL(glfwCreateWindow);
|
mjGLFW_DECLARE_SYMBOL(glfwCreateWindow);
|
||||||
|
mjGLFW_DECLARE_SYMBOL(glfwDestroyWindow);
|
||||||
mjGLFW_DECLARE_SYMBOL(glfwGetCursorPos);
|
mjGLFW_DECLARE_SYMBOL(glfwGetCursorPos);
|
||||||
mjGLFW_DECLARE_SYMBOL(glfwGetFramebufferSize);
|
mjGLFW_DECLARE_SYMBOL(glfwGetFramebufferSize);
|
||||||
mjGLFW_DECLARE_SYMBOL(glfwGetKey);
|
mjGLFW_DECLARE_SYMBOL(glfwGetKey);
|
||||||
|
|||||||
+9
-3
@@ -337,7 +337,10 @@ void PhysicsLoop(mj::Simulate& sim) {
|
|||||||
mjData* dnew = nullptr;
|
mjData* dnew = nullptr;
|
||||||
if (mnew) dnew = mj_makeData(mnew);
|
if (mnew) dnew = mj_makeData(mnew);
|
||||||
if (dnew) {
|
if (dnew) {
|
||||||
sim.load(sim.dropfilename, mnew, dnew, true);
|
sim.load(sim.dropfilename, mnew, dnew);
|
||||||
|
|
||||||
|
mj_deleteData(d);
|
||||||
|
mj_deleteModel(m);
|
||||||
|
|
||||||
m = mnew;
|
m = mnew;
|
||||||
d = dnew;
|
d = dnew;
|
||||||
@@ -356,7 +359,10 @@ void PhysicsLoop(mj::Simulate& sim) {
|
|||||||
mjData* dnew = nullptr;
|
mjData* dnew = nullptr;
|
||||||
if (mnew) dnew = mj_makeData(mnew);
|
if (mnew) dnew = mj_makeData(mnew);
|
||||||
if (dnew) {
|
if (dnew) {
|
||||||
sim.load(sim.filename, mnew, dnew, true);
|
sim.load(sim.filename, mnew, dnew);
|
||||||
|
|
||||||
|
mj_deleteData(d);
|
||||||
|
mj_deleteModel(m);
|
||||||
|
|
||||||
m = mnew;
|
m = mnew;
|
||||||
d = dnew;
|
d = dnew;
|
||||||
@@ -482,7 +488,7 @@ void PhysicsThread(mj::Simulate* sim, const char* filename) {
|
|||||||
m = LoadModel(filename, *sim);
|
m = LoadModel(filename, *sim);
|
||||||
if (m) d = mj_makeData(m);
|
if (m) d = mj_makeData(m);
|
||||||
if (d) {
|
if (d) {
|
||||||
sim->load(filename, m, d, true);
|
sim->load(filename, m, d);
|
||||||
mj_forward(m, d);
|
mj_forward(m, d);
|
||||||
|
|
||||||
// allocate ctrlnoise
|
// allocate ctrlnoise
|
||||||
|
|||||||
+10
-14
@@ -1575,11 +1575,9 @@ void Simulate::applyforceperturbations() {
|
|||||||
//------------------------- Tell the render thread to load a file and wait -------------------------
|
//------------------------- Tell the render thread to load a file and wait -------------------------
|
||||||
void Simulate::load(const char* file,
|
void Simulate::load(const char* file,
|
||||||
mjModel* mnew,
|
mjModel* mnew,
|
||||||
mjData* dnew,
|
mjData* dnew) {
|
||||||
bool delete_old_m_d) {
|
|
||||||
this->mnew = mnew;
|
this->mnew = mnew;
|
||||||
this->dnew = dnew;
|
this->dnew = dnew;
|
||||||
this->delete_old_m_d = delete_old_m_d;
|
|
||||||
mju::strcpy_arr(this->filename, file);
|
mju::strcpy_arr(this->filename, file);
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -1595,16 +1593,6 @@ void Simulate::load(const char* file,
|
|||||||
|
|
||||||
//------------------------------------- load mjb or xml model --------------------------------------
|
//------------------------------------- load mjb or xml model --------------------------------------
|
||||||
void Simulate::loadmodel() {
|
void Simulate::loadmodel() {
|
||||||
if (this->delete_old_m_d) {
|
|
||||||
// delete old model if requested
|
|
||||||
if (this->d) {
|
|
||||||
mj_deleteData(d);
|
|
||||||
}
|
|
||||||
if (this->m) {
|
|
||||||
mj_deleteModel(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->m = this->mnew;
|
this->m = this->mnew;
|
||||||
this->d = this->dnew;
|
this->d = this->dnew;
|
||||||
|
|
||||||
@@ -1618,7 +1606,8 @@ void Simulate::loadmodel() {
|
|||||||
this->pert.skinselect = -1;
|
this->pert.skinselect = -1;
|
||||||
|
|
||||||
// align and scale view unless reloading the same file
|
// align and scale view unless reloading the same file
|
||||||
if (mju::strcmp_arr(this->filename, this->previous_filename)) {
|
if (this->filename[0] &&
|
||||||
|
mju::strcmp_arr(this->filename, this->previous_filename)) {
|
||||||
alignscale(this);
|
alignscale(this);
|
||||||
mju::strcpy_arr(this->previous_filename, this->filename);
|
mju::strcpy_arr(this->previous_filename, this->filename);
|
||||||
}
|
}
|
||||||
@@ -1980,6 +1969,13 @@ void Simulate::renderloop() {
|
|||||||
this->clearcallback();
|
this->clearcallback();
|
||||||
mjv_freeScene(&this->scn);
|
mjv_freeScene(&this->scn);
|
||||||
mjr_freeContext(&this->con);
|
mjr_freeContext(&this->con);
|
||||||
|
|
||||||
|
Glfw().glfwDestroyWindow(this->window);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------ setup the glfw dispatch table -------------------------------
|
||||||
|
void setglfwdlhandle(void* dlhandle) {
|
||||||
|
Glfw(dlhandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
+5
-2
@@ -54,7 +54,7 @@ class MJSIMULATEAPI Simulate {
|
|||||||
|
|
||||||
// Request that the Simulate UI thread render a new model
|
// Request that the Simulate UI thread render a new model
|
||||||
// optionally delete the old model and data when done
|
// optionally delete the old model and data when done
|
||||||
void load(const char* file, mjModel* m, mjData* d, bool delete_old_m_d);
|
void load(const char* file, mjModel* m, mjData* d);
|
||||||
|
|
||||||
// functions below are used by the renderthread
|
// functions below are used by the renderthread
|
||||||
// load mjb or xml model that has been requested by load()
|
// load mjb or xml model that has been requested by load()
|
||||||
@@ -79,7 +79,6 @@ class MJSIMULATEAPI Simulate {
|
|||||||
// model and data to be visualized
|
// model and data to be visualized
|
||||||
mjModel* mnew = nullptr;
|
mjModel* mnew = nullptr;
|
||||||
mjData* dnew = nullptr;
|
mjData* dnew = nullptr;
|
||||||
bool delete_old_m_d = false;
|
|
||||||
|
|
||||||
mjModel* m = nullptr;
|
mjModel* m = nullptr;
|
||||||
mjData* d = nullptr;
|
mjData* d = nullptr;
|
||||||
@@ -228,6 +227,10 @@ class MJSIMULATEAPI Simulate {
|
|||||||
char info_content[Simulate::kMaxFilenameLength] = {0};
|
char info_content[Simulate::kMaxFilenameLength] = {0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// setup the glfw dispatch table
|
||||||
|
// if set, must be called prior to other Simulate functions
|
||||||
|
MJSIMULATEAPI void setglfwdlhandle(void* dlhandle);
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user