simulate python: fix cmake, glfw usage, update Python version of physics loop

This commit is contained in:
Levi Burner
2022-10-04 14:37:06 -04:00
parent 0b6bf42840
commit 95a4b1c551
12 changed files with 219 additions and 174 deletions
+21 -23
View File
@@ -62,7 +62,6 @@ add_compile_options("${MUJOCO_HARDEN_COMPILE_OPTIONS}")
add_link_options("${MUJOCO_HARDEN_LINK_OPTIONS}") add_link_options("${MUJOCO_HARDEN_LINK_OPTIONS}")
find_package(Python3 COMPONENTS Interpreter Development) find_package(Python3 COMPONENTS Interpreter Development)
find_package(glfw3 3.3 REQUIRED)
include(FindOrFetch) include(FindOrFetch)
@@ -116,37 +115,37 @@ if(NOT TARGET mujoco)
endif() endif()
endif() endif()
# ==================== MJSIMULATE LIBRARY ========================================== # ==================== LIBSIMULATE LIBRARY ==========================================
if(NOT TARGET mjsimulate) if(NOT TARGET libsimulate)
find_library(MJSIMULATE_LIBRARY mjsimulate mjsimulate HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED) find_library(LIBSIMULATE_LIBRARY libsimulate libsimulate HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED)
find_path(MJSIMULATE_INCLUDE mujoco/simulate.h mujoco/uitools.h mujoco/array_safety.h HINTS ${MUJOCO_INCLUDE_DIR} REQUIRED) find_path(LIBSIMULATE_INCLUDE mujoco/simulate.h mujoco/uitools.h mujoco/array_safety.h HINTS ${MUJOCO_INCLUDE_DIR} REQUIRED)
message("MuJoCo Simulate is at ${MJSIMULATE_LIBRARY}") message("MuJoCo Simulate is at ${LIBSIMULATE_LIBRARY}")
message("MuJoCo Simulate headers are at ${MJSIMULATE_INCLUDE}") message("MuJoCo Simulate headers are at ${LIBSIMULATE_INCLUDE}")
add_library(mjsimulate SHARED IMPORTED) add_library(libsimulate SHARED IMPORTED)
if(WIN32) if(WIN32)
set_target_properties(mjsimulate PROPERTIES IMPORTED_IMPLIB "${MJSIMULATE_LIBRARY}") set_target_properties(libsimulate PROPERTIES IMPORTED_IMPLIB "${LIBSIMULATE_LIBRARY}")
else() else()
set_target_properties(mjsimulate PROPERTIES IMPORTED_LOCATION "${MJSIMULATE_LIBRARY}") set_target_properties(libsimulate PROPERTIES IMPORTED_LOCATION "${LIBSIMULATE_LIBRARY}")
endif() endif()
target_include_directories(mjsimulate INTERFACE "${MJSIMULATE_INCLUDE}") target_include_directories(libsimulate INTERFACE "${LIBSIMULATE_INCLUDE}")
if(APPLE) if(APPLE)
execute_process( execute_process(
COMMAND otool -XD ${MJSIMULATE_LIBRARY} COMMAND otool -XD ${LIBSIMULATE_LIBRARY}
COMMAND head -n 1 COMMAND head -n 1
COMMAND xargs dirname COMMAND xargs dirname
COMMAND xargs echo -n COMMAND xargs echo -n
OUTPUT_VARIABLE MJSIMULATE_INSTALL_NAME_DIR OUTPUT_VARIABLE LIBSIMULATE_INSTALL_NAME_DIR
) )
set_target_properties(mjsimulate PROPERTIES INSTALL_NAME_DIR "${MJSIMULATE_INSTALL_NAME_DIR}") set_target_properties(libsimulate PROPERTIES INSTALL_NAME_DIR "${LIBSIMULATE_INSTALL_NAME_DIR}")
elseif(UNIX) elseif(UNIX)
execute_process( execute_process(
COMMAND objdump -p ${MJSIMULATE_LIBRARY} COMMAND objdump -p ${LIBSIMULATE_LIBRARY}
COMMAND grep SONAME COMMAND grep SONAME
COMMAND grep -Po [^\\s]+$ COMMAND grep -Po [^\\s]+$
COMMAND xargs echo -n COMMAND xargs echo -n
OUTPUT_VARIABLE MJSIMULATE_SONAME OUTPUT_VARIABLE LIBSIMULATE_SONAME
) )
set_target_properties(mjsimulate PROPERTIES IMPORTED_SONAME "${MJSIMULATE_SONAME}") set_target_properties(libsimulate PROPERTIES IMPORTED_SONAME "${LIBSIMULATE_SONAME}")
endif() endif()
endif() endif()
@@ -266,7 +265,7 @@ target_link_libraries(errors_header INTERFACE crossplatform func_wrap mujoco)
add_library(raw INTERFACE) add_library(raw INTERFACE)
target_sources(raw INTERFACE raw.h) target_sources(raw INTERFACE raw.h)
set_target_properties(raw PROPERTIES PUBLIC_HEADER raw.h) set_target_properties(raw PROPERTIES PUBLIC_HEADER raw.h)
target_link_libraries(raw INTERFACE mujoco mjsimulate) target_link_libraries(raw INTERFACE mujoco)
add_library(structs_header INTERFACE) add_library(structs_header INTERFACE)
target_sources( target_sources(
@@ -344,7 +343,7 @@ target_link_libraries(
) )
mujoco_pybind11_module(_constants constants.cc) mujoco_pybind11_module(_constants constants.cc)
target_link_libraries(_constants PRIVATE mujoco mjsimulate) target_link_libraries(_constants PRIVATE mujoco)
mujoco_pybind11_module(_enums enums.cc) mujoco_pybind11_module(_enums enums.cc)
target_link_libraries( target_link_libraries(
@@ -408,10 +407,9 @@ target_link_libraries(
mujoco_pybind11_module(_simulate simulate.cc) mujoco_pybind11_module(_simulate simulate.cc)
target_link_libraries( target_link_libraries(
_simulate _simulate
PRIVATE mjsimulate PRIVATE libsimulate
mujoco mujoco
raw raw
glfw
structs_header) structs_header)
target_link_options(_simulate PRIVATE -Wl,-no-as-needed) target_link_options(_simulate PRIVATE -Wl,-no-as-needed)
@@ -426,7 +424,7 @@ set(LIBRARIES_FOR_WHEEL
"$<TARGET_FILE:_simulate>" "$<TARGET_FILE:_simulate>"
"$<TARGET_FILE:_structs>" "$<TARGET_FILE:_structs>"
"$<TARGET_FILE:mujoco>" "$<TARGET_FILE:mujoco>"
"$<TARGET_FILE:mjsimulate>" "$<TARGET_FILE:libsimulate>"
) )
if(MUJOCO_PYTHON_MAKE_WHEEL) if(MUJOCO_PYTHON_MAKE_WHEEL)
@@ -455,6 +453,6 @@ if(MUJOCO_PYTHON_MAKE_WHEEL)
_simulate _simulate
_structs _structs
mujoco mujoco
mjsimulate libsimulate
) )
endif() endif()
-4
View File
@@ -15,7 +15,6 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/simulate.h>
#include <mujoco/mjvisualize.h> #include <mujoco/mjvisualize.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#include <pybind11/cast.h> #include <pybind11/cast.h>
@@ -79,9 +78,6 @@ PYBIND11_MODULE(_constants, pymodule) {
// from mujoco.h // from mujoco.h
X(mjVERSION_HEADER); X(mjVERSION_HEADER);
// from simulate.h
X(mujoco::Simulate::kMaxFilenameLength);
#undef X #undef X
pymodule.attr("mjDISABLESTRING") = MakeTuple(mjDISABLESTRING); pymodule.attr("mjDISABLESTRING") = MakeTuple(mjDISABLESTRING);
pymodule.attr("mjENABLESTRING") = MakeTuple(mjENABLESTRING); pymodule.attr("mjENABLESTRING") = MakeTuple(mjENABLESTRING);
-4
View File
@@ -19,7 +19,6 @@
#include <mujoco/mjmodel.h> #include <mujoco/mjmodel.h>
#include <mujoco/mjrender.h> #include <mujoco/mjrender.h>
#include <mujoco/mjvisualize.h> #include <mujoco/mjvisualize.h>
#include <mujoco/simulate.h>
// Type aliases for MuJoCo C structs to allow us refer to consistently refer // Type aliases for MuJoCo C structs to allow us refer to consistently refer
// to them under the "raw" namespace. // to them under the "raw" namespace.
@@ -56,9 +55,6 @@ using MjvOption = ::mjvOption;
using MjvScene = ::mjvScene; using MjvScene = ::mjvScene;
using MjvFigure = ::mjvFigure; using MjvFigure = ::mjvFigure;
// From simulate.h
using Simulate = ::mujoco::Simulate;
} // namespace mujoco::raw } // namespace mujoco::raw
#endif // MUJOCO_PYTHON_RAW_H_ #endif // MUJOCO_PYTHON_RAW_H_
+32 -38
View File
@@ -34,107 +34,101 @@ namespace {
namespace py = ::pybind11; namespace py = ::pybind11;
const auto simulate_doc = R"(
Python wrapper for the Simulate class
)";
// We define SimulateWrapper here instead of in structs because
// we do not want to make _structs dependent on gflw
PYBIND11_MODULE(_simulate, pymodule) { PYBIND11_MODULE(_simulate, pymodule) {
namespace py = ::pybind11; namespace py = ::pybind11;
py::class_<mujoco::raw::Simulate>(pymodule, "Simulate") py::class_<mujoco::Simulate>(pymodule, "Simulate")
.def(py::init<>()) .def(py::init<>())
.def("renderloop", .def("renderloop",
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
simulate.renderloop(); simulate.renderloop();
}, },
py::call_guard<py::gil_scoped_release>()) py::call_guard<py::gil_scoped_release>())
.def("load", .def("load",
[](mujoco::raw::Simulate& simulate, std::string filename, const MjModelWrapper& m, MjDataWrapper& d, bool delete_old_m_d) { [](mujoco::Simulate& simulate, std::string filename, const MjModelWrapper& m, MjDataWrapper& d) {
const raw::MjModel* m_ptr = m.get(); const raw::MjModel* m_ptr = m.get();
raw::MjData* d_ptr = d.get(); raw::MjData* d_ptr = d.get();
simulate.load(filename.c_str(), (mjModel*)m_ptr, d_ptr, delete_old_m_d); simulate.load(filename.c_str(), (mjModel*)m_ptr, d_ptr);
}, },
py::call_guard<py::gil_scoped_release>()) py::call_guard<py::gil_scoped_release>())
.def("applyposepertubations", &mujoco::raw::Simulate::applyposepertubations) .def("applyposepertubations", &mujoco::Simulate::applyposepertubations)
.def("applyforceperturbations", &mujoco::raw::Simulate::applyforceperturbations) .def("applyforceperturbations", &mujoco::Simulate::applyforceperturbations)
.def("lock", // TODO wrap mutex properly as as seperate pybind11 object? .def("lock", // TODO wrap mutex properly as as seperate pybind11 object?
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
simulate.mtx.lock(); simulate.mtx.lock();
}, },
py::call_guard<py::gil_scoped_release>()) py::call_guard<py::gil_scoped_release>())
.def("unlock", .def("unlock",
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
simulate.mtx.unlock(); simulate.mtx.unlock();
}, },
py::call_guard<py::gil_scoped_release>()) py::call_guard<py::gil_scoped_release>())
.def_readwrite("ctrlnoisestd", &mujoco::raw::Simulate::ctrlnoisestd) .def_readwrite("ctrlnoisestd", &mujoco::Simulate::ctrlnoisestd)
.def_readwrite("ctrlnoiserate", &mujoco::raw::Simulate::ctrlnoiserate) .def_readwrite("ctrlnoiserate", &mujoco::Simulate::ctrlnoiserate)
.def_readwrite("slow_down", &mujoco::raw::Simulate::slow_down)
.def_readwrite("speed_changed", &mujoco::raw::Simulate::speed_changed) .def_readwrite("realtimeindex", &mujoco::Simulate::realTimeIndex)
.def("getrefreshRate", .def_readwrite("speedchanged", &mujoco::Simulate::speedChanged)
[](mujoco::raw::Simulate& simulate) { .def_readwrite("measuredslowdown", &mujoco::Simulate::measuredSlowdown)
return simulate.vmode.refreshRate; .def_readwrite("refreshrate", &mujoco::Simulate::refreshRate)
})
.def_readwrite("busywait", &mujoco::Simulate::busywait)
.def_readwrite("run", &mujoco::Simulate::run)
.def_readwrite("busywait", &mujoco::raw::Simulate::busywait)
.def_readwrite("run", &mujoco::raw::Simulate::run)
//.def_readwrite("exitrequest", &mujoco::raw::Simulate::exitrequest)
.def("getexitrequest", .def("getexitrequest",
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
return simulate.exitrequest.load(); return simulate.exitrequest.load();
} }
) )
.def("setexitrequest", .def("setexitrequest",
[](mujoco::raw::Simulate& simulate, bool exitrequest) { [](mujoco::Simulate& simulate, bool exitrequest) {
simulate.exitrequest.store(exitrequest); simulate.exitrequest.store(exitrequest);
} }
) )
// .def_readwrite("uiloadrequest", &mujoco::raw::Simulate::uiloadrequest)
.def("getuiloadrequest", .def("getuiloadrequest",
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
return simulate.uiloadrequest.load(); return simulate.uiloadrequest.load();
} }
) )
.def("setuiloadrequest", .def("setuiloadrequest",
[](mujoco::raw::Simulate& simulate, int uiloadrequest) { [](mujoco::Simulate& simulate, int uiloadrequest) {
simulate.uiloadrequest.store(uiloadrequest); simulate.uiloadrequest.store(uiloadrequest);
} }
) )
.def("uiloadrequest_fetch_sub", .def("uiloadrequest_fetch_sub",
[](mujoco::raw::Simulate& simulate, int arg) { [](mujoco::Simulate& simulate, int arg) {
simulate.uiloadrequest.fetch_sub(arg); simulate.uiloadrequest.fetch_sub(arg);
} }
) )
// .def_readwrite("droploadrequest", &mujoco::raw::Simulate::droploadrequest)
.def("getdroploadrequest", .def("getdroploadrequest",
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
return simulate.droploadrequest.load(); return simulate.droploadrequest.load();
} }
) )
.def("setdroploadrequest", .def("setdroploadrequest",
[](mujoco::raw::Simulate& simulate, bool droploadrequest) { [](mujoco::Simulate& simulate, bool droploadrequest) {
simulate.droploadrequest.store(droploadrequest); simulate.droploadrequest.store(droploadrequest);
} }
) )
.def("getdropfilename", .def("getdropfilename",
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
return (char*)simulate.dropfilename; return (char*)simulate.dropfilename;
} }
) )
.def("getfilename", .def("getfilename",
[](mujoco::raw::Simulate& simulate) { [](mujoco::Simulate& simulate) {
return (char*)simulate.filename; return (char*)simulate.filename;
} }
) )
.def("setloadError", .def("setloadError",
[](mujoco::raw::Simulate& simulate, std::string& loadError) { [](mujoco::Simulate& simulate, std::string& loadError) {
strncpy(simulate.loadError, loadError.c_str(), simulate.kMaxFilenameLength); strncpy(simulate.loadError, loadError.c_str(), simulate.kMaxFilenameLength);
} }
); );
pymodule.def("setglfwdlhandle", [](std::uintptr_t dlhandle) { mujoco::setglfwdlhandle(reinterpret_cast<void*>(dlhandle)); });
} }
} // namespace } // namespace
+55 -26
View File
@@ -18,6 +18,9 @@ import mujoco
from mujoco import _simulate from mujoco import _simulate
import glfw import glfw
from glfw import _glfw
_simulate.setglfwdlhandle(_glfw._handle)
import numpy as np import numpy as np
import ctypes import ctypes
@@ -29,6 +32,14 @@ class Simulate(_simulate.Simulate):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
# logarithmically spaced realtime slow-down coefficients (percent)
self.percentrealtime = [
100, 80, 66, 50, 40, 33, 25, 20, 16, 13,
10, 8, 6.6, 5.0, 4, 3.3, 2.5, 2, 1.6, 1.3,
1, .8, .66, .5, .4, .33, .25, .2, .16, .13,
.1
]
def _get_refreshRate(self): def _get_refreshRate(self):
return self.getrefreshRate() return self.getrefreshRate()
@@ -107,14 +118,14 @@ def load_and_step_model(m, d, simulate, filename, preload_callback=None, load_ca
if mnew is not None: if mnew is not None:
dnew = mujoco.MjData(mnew) dnew = mujoco.MjData(mnew)
simulate.load(filename, mnew, dnew, False) simulate.load(filename, mnew, dnew)
mujoco.mj_forward(mnew, dnew) mujoco.mj_forward(mnew, dnew)
else: else:
mnew = None mnew = None
dnew = None dnew = None
if load_callback is not None: if load_callback is not None:
load_callback(mnew, dnew, loadError) load_callback(mnew, dnew)
return mnew, dnew, loadError return mnew, dnew, loadError
@@ -132,11 +143,11 @@ def run_physics_loop(simulate, preload_callback=None, load_callback=None, file=N
# constants # constants
syncmisalign = 0.1 # maximum time mis-alignment before re-sync syncmisalign = 0.1 # maximum time mis-alignment before re-sync
refreshfactor = 0.7 # fraction of refresh available for simulation simrefreshfraction = 0.7 # fraction of refresh available for simulation
# cpu-sim synchronization point # cpu-sim synchronization point
cpusync = 0.0 synccpu = 0.0
simsync = 0.0 syncsim = 0.0
# run until asked to exit # run until asked to exit
while not simulate.exitrequest: while not simulate.exitrequest:
@@ -144,7 +155,6 @@ def run_physics_loop(simulate, preload_callback=None, load_callback=None, file=N
simulate.droploadrequest = 0 simulate.droploadrequest = 0
m_, d_, loadError = load_and_step_model(m, d, simulate, simulate.dropfilename, m_, d_, loadError = load_and_step_model(m, d, simulate, simulate.dropfilename,
preload_callback, load_callback) preload_callback, load_callback)
if m_ is not None: if m_ is not None:
m = m_ m = m_
d = d_ d = d_
@@ -178,28 +188,36 @@ def run_physics_loop(simulate, preload_callback=None, load_callback=None, file=N
# running # running
if simulate.run != 0: if simulate.run != 0:
# record cpu time at start of iteration # record cpu time at start of iteration
tmstart = glfw.get_time() startcpu = glfw.get_time()
elapsedcpu = startcpu - synccpu
elapsedsim = d.time - syncsim
# inject noise # inject noise
if simulate.ctrlnoisestd != 0.0: if simulate.ctrlnoisestd != 0.0:
# convert rate and scale to discrete time given current timestep # convert rate and scale to discrete time (Ornstein–Uhlenbeck)
rate = math.exp(-m.opt.timestep / simulate.ctrlnoiserate) rate = math.exp(-m.opt.timestep / simulate.ctrlnoiserate)
scale = simulate.ctrlnoisestd * math.sqrt(1-rate*rate) scale = simulate.ctrlnoisestd * math.sqrt(1-rate*rate)
for i in range(m.nu): for i in range(m.nu):
# update noise # update noise
ctrlnoise[i] = rate * ctrlnoise[i] + scale * mujoco.mju_standardNormal(None) ctrlnoise[i] = rate * ctrlnoise[i] + scale * mujoco.mju_standardNormal(None)
# apply noise # apply noise
d.ctrl[i] = ctrlnoise[i] d.ctrl[i] = ctrlnoise[i]
# out-of-sync (for any reason) # requested slow-down factor
offset = abs((d.time*simulate.slow_down - simsync) - (tmstart - cpusync)) slowdown = 100 / simulate.percentrealtime[simulate.realtimeindex]
if (d.time*simulate.slow_down < simsync or tmstart < cpusync or cpusync == 0.0 or
offset > syncmisalign*simulate.slow_down or simulate.speed_changed): # misalignment condition: distance from target sim time is bigger than syncmisalign
misaligned = abs(elapsedcpu/slowdown - elapsedsim) > syncmisalign
# out-of-sync (for any reason): reset sync times, step
if elapsedsim < 0 or elapsedcpu < 0 or synccpu == 0 or misaligned or simulate.speedchanged:
# re-sync # re-sync
cpusync = tmstart synccpu = startcpu
simsync = d.time*simulate.slow_down syncsim = d.time
simulate.speed_changed = False simulate.speedchanged = False
# clear old perturbations, apply new # clear old perturbations, apply new
d.xfrc_applied[:, :] = 0 d.xfrc_applied[:, :] = 0
@@ -209,21 +227,30 @@ def run_physics_loop(simulate, preload_callback=None, load_callback=None, file=N
# run single step, let next iteration deal with timing # run single step, let next iteration deal with timing
mujoco.mj_step(m, d) mujoco.mj_step(m, d)
# in-sync # in-sync: step until ahead of cpu
else: else:
while ((d.time*simulate.slow_down - simsync) < (glfw.get_time() - cpusync) and measured = False
(glfw.get_time() - tmstart) < (refreshfactor/simulate.refreshRate)): prevsim = d.time
refreshtime = simrefreshfraction/simulate.refreshrate;
# 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.measuredslowdown = elapsedcpu / elapsedsim
measured = True
# clear old perturbations, apply new # clear old perturbations, apply new
d.xfrc_applied[:, :] = 0 d.xfrc_applied[:, :] = 0
simulate.applyposepertubations(0) # move mocap bodies only simulate.applyposepertubations(0) # move mocap bodies only
simulate.applyforceperturbations() simulate.applyforceperturbations()
# run mj_step # call mj_step
prevtm = d.time*simulate.slow_down
mujoco.mj_step(m, d) mujoco.mj_step(m, d)
# break on reset # break if reset
if d.time*simulate.slow_down < prevtm: if d.time < prevsim:
break break
# paused # paused
@@ -237,13 +264,14 @@ def run_physics_loop(simulate, preload_callback=None, load_callback=None, file=N
# end exclusive access # end exclusive access
simulate.unlock() simulate.unlock()
def run_simulate_and_physics(file=None, preload_callback=None, load_callback=None): def run_simulate_and_physics(file=None, preload_callback=None, load_callback=None, init=True, terminate=True):
# simulate object encapsulates the UI # simulate object encapsulates the UI
simulate = Simulate() simulate = Simulate()
# init GLFW # init GLFW
if not glfw.init(): if init:
raise mujoco.FatalError('could not initialize GLFW') if not glfw.init():
raise mujoco.FatalError('could not initialize GLFW')
# if m is not None: # if m is not None:
physics_thread = threading.Thread(target=lambda: run_physics_loop(simulate, preload_callback=preload_callback, load_callback=load_callback, file=file)) physics_thread = threading.Thread(target=lambda: run_physics_loop(simulate, preload_callback=preload_callback, load_callback=load_callback, file=file))
@@ -253,4 +281,5 @@ def run_simulate_and_physics(file=None, preload_callback=None, load_callback=Non
simulate.renderloop() simulate.renderloop()
physics_thread.join() physics_thread.join()
glfw.terminate() if terminate:
glfw.terminate()
+5 -4
View File
@@ -63,11 +63,11 @@ def get_mujoco_lib_pattern():
def get_external_lib_patterns(): def get_external_lib_patterns():
if platform.system() == 'Windows': if platform.system() == 'Windows':
return ['mujoco.dll', 'mjsimulate.dll'] return ['mujoco.dll', 'libsimulate.dll']
elif platform.system() == 'Darwin': elif platform.system() == 'Darwin':
return ['libmujoco.*.dylib', 'libmjsimulate.*.dylib'] return ['libmujoco.*.dylib', 'liblibsimulate.*.dylib']
else: else:
return ['libmujoco.so.*', 'libmjsimulate.so*'] return ['libmujoco.so.*', 'liblibsimulate.so*']
def start_and_end(iterable): def start_and_end(iterable):
it = iter(iterable) it = iter(iterable)
@@ -318,7 +318,8 @@ setup(
'libmujoco*.so.*', 'libmujoco*.so.*',
'mujoco.dll', 'mujoco.dll',
'include/mujoco/*.h', 'include/mujoco/*.h',
'libmjsimulate*.so*', 'liblibsimulate.*.dylib',
'liblibsimulate*.so*',
]), ]),
}, },
) )
+33 -11
View File
@@ -97,8 +97,8 @@ if(NOT TARGET lodepng)
endif() endif()
endif() endif()
# Simulate library # Simulate shared library
add_library(libsimulate STATIC) add_library(libsimulate SHARED simulate.cc uitools.cc glfw_dispatch.cc)
add_library(mujoco::libsimulate ALIAS libsimulate) add_library(mujoco::libsimulate ALIAS libsimulate)
target_sources( target_sources(
@@ -106,19 +106,16 @@ target_sources(
PUBLIC simulate.h PUBLIC simulate.h
array_safety.h array_safety.h
glfw_dispatch.h glfw_dispatch.h
glfw_dispatch.cc
simulate.cc
uitools.h uitools.h
uitools.cc
) )
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 LIBSIMULATE_DLL_EXPORTS mjGLFW_DYNAMIC_SYMBOLS)
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 glfw mujoco::mujoco)
target_link_options(libsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS}) target_link_options(libsimulate PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
set_target_properties( set_target_properties(
mjsimulate PROPERTIES VERSION "${mujoco_VERSION}" PUBLIC_HEADER "simulate.h" libsimulate PROPERTIES VERSION "${mujoco_VERSION}" PUBLIC_HEADER "simulate.h"
) )
if(APPLE) if(APPLE)
@@ -126,6 +123,31 @@ if(APPLE)
target_link_libraries(libsimulate PUBLIC "-framework Cocoa") target_link_libraries(libsimulate PUBLIC "-framework Cocoa")
endif() endif()
# Simulate static library
add_library(libsimulatestatic STATIC simulate.cc uitools.cc glfw_dispatch.cc)
add_library(mujoco::libsimulatestatic ALIAS libsimulatestatic)
target_sources(
libsimulatestatic
PUBLIC simulate.h
array_safety.h
glfw_dispatch.h
uitools.h
)
target_include_directories(libsimulatestatic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(libsimulatestatic PUBLIC LIBSIMULATE_STATIC)
target_compile_options(libsimulatestatic PUBLIC ${MUJOCO_SIMULATE_COMPILE_OPTIONS})
target_link_libraries(libsimulatestatic PUBLIC lodepng glfw mujoco::mujoco)
target_link_options(libsimulatestatic PRIVATE ${MUJOCO_SIMULATE_LINK_OPTIONS})
set_target_properties(
libsimulatestatic PROPERTIES VERSION "${mujoco_VERSION}" PUBLIC_HEADER "simulate.h"
)
if(APPLE)
target_sources(libsimulatestatic PRIVATE macos_save.mm)
target_link_libraries(libsimulatestatic PUBLIC "-framework Cocoa")
endif()
# Build simulate executable # Build simulate executable
if(APPLE) if(APPLE)
set(SIMULATE_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../dist/mujoco.icns) set(SIMULATE_RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../dist/mujoco.icns)
@@ -150,7 +172,7 @@ endif()
target_link_libraries( target_link_libraries(
simulate simulate
libsimulate libsimulatestatic
mujoco::mujoco mujoco::mujoco
glfw glfw
Threads::Threads Threads::Threads
@@ -225,7 +247,7 @@ if(_INSTALL_SIMULATE)
target_add_rpath( target_add_rpath(
TARGETS TARGETS
mjsimulate libsimulate
INSTALL_DIRECTORY INSTALL_DIRECTORY
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}"
LIB_DIRS LIB_DIRS
@@ -245,7 +267,7 @@ if(_INSTALL_SIMULATE)
) )
install( install(
TARGETS mjsimulate TARGETS libsimulate
EXPORT ${PROJECT_NAME} EXPORT ${PROJECT_NAME}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT simulate
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT simulate
+1 -4
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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);
if (d) mj_deleteData(d);
if (m) 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);
if (d) mj_deleteData(d);
if (m) 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
+45 -45
View File
@@ -934,7 +934,7 @@ void copykey(mj::Simulate* sim) {
mju::strcat_arr(clipboard, "'/>"); mju::strcat_arr(clipboard, "'/>");
// copy to clipboard // copy to clipboard
Glfw().glfwSetClipboardString(sim->window, clipboard); Glfw().glfwSetClipboardString(reinterpret_cast<GLFWwindow*>(sim->window), clipboard);
} }
// millisecond timer, for MuJoCo built-in profiler // millisecond timer, for MuJoCo built-in profiler
@@ -974,7 +974,7 @@ void copycamera(mj::Simulate* sim) {
camera[0].up[0], camera[0].up[1], camera[0].up[2]); camera[0].up[0], camera[0].up[1], camera[0].up[2]);
// copy spec into clipboard // copy spec into clipboard
Glfw().glfwSetClipboardString(sim->window, clipboard); Glfw().glfwSetClipboardString(reinterpret_cast<GLFWwindow*>(sim->window), clipboard);
} }
// update UI 0 when MuJoCo structures change (except for joint sliders) // update UI 0 when MuJoCo structures change (except for joint sliders)
@@ -1035,7 +1035,7 @@ void uiLayout(mjuiState* state) {
// rect 0: entire framebuffer // rect 0: entire framebuffer
rect[0].left = 0; rect[0].left = 0;
rect[0].bottom = 0; rect[0].bottom = 0;
Glfw().glfwGetFramebufferSize(sim->window, &rect[0].width, &rect[0].height); Glfw().glfwGetFramebufferSize(reinterpret_cast<GLFWwindow*>(sim->window), &rect[0].width, &rect[0].height);
// rect 1: UI 0 // rect 1: UI 0
rect[1].left = 0; rect[1].left = 0;
@@ -1128,21 +1128,24 @@ void uiEvent(mjuiState* state) {
break; break;
case 9: // Full screen case 9: // Full screen
if (Glfw().glfwGetWindowMonitor(sim->window)) { if (Glfw().glfwGetWindowMonitor(reinterpret_cast<GLFWwindow*>(sim->window))) {
// restore window from saved data // restore window from saved data
Glfw().glfwSetWindowMonitor(sim->window, nullptr, sim->windowpos[0], sim->windowpos[1], Glfw().glfwSetWindowMonitor(reinterpret_cast<GLFWwindow*>(sim->window),
nullptr, sim->windowpos[0], sim->windowpos[1],
sim->windowsize[0], sim->windowsize[1], 0); sim->windowsize[0], sim->windowsize[1], 0);
} }
// currently windowed: switch to full screen // currently windowed: switch to full screen
else { else {
// save window data // save window data
Glfw().glfwGetWindowPos(sim->window, sim->windowpos, sim->windowpos+1); Glfw().glfwGetWindowPos(reinterpret_cast<GLFWwindow*>(sim->window), sim->windowpos, sim->windowpos+1);
Glfw().glfwGetWindowSize(sim->window, sim->windowsize, sim->windowsize+1); Glfw().glfwGetWindowSize(reinterpret_cast<GLFWwindow*>(sim->window), sim->windowsize, sim->windowsize+1);
// switch // switch
Glfw().glfwSetWindowMonitor(sim->window, Glfw().glfwGetPrimaryMonitor(), 0, 0, Glfw().glfwSetWindowMonitor(reinterpret_cast<GLFWwindow*>(sim->window), Glfw().glfwGetPrimaryMonitor(), 0, 0,
sim->vmode.width, sim->vmode.height, sim->vmode.refreshRate); reinterpret_cast<const GLFWvidmode*>(sim->vmode)->width,
reinterpret_cast<const GLFWvidmode*>(sim->vmode)->height,
reinterpret_cast<const GLFWvidmode*>(sim->vmode)->refreshRate);
} }
// reinstante vsync, just in case // reinstante vsync, just in case
@@ -1155,8 +1158,8 @@ void uiEvent(mjuiState* state) {
} }
// modify UI // modify UI
uiModify(sim->window, &sim->ui0, state, &sim->con); uiModify(reinterpret_cast<GLFWwindow*>(sim->window), &sim->ui0, state, &sim->con);
uiModify(sim->window, &sim->ui1, state, &sim->con); uiModify(reinterpret_cast<GLFWwindow*>(sim->window), &sim->ui1, state, &sim->con);
} }
// simulation section // simulation section
@@ -1263,7 +1266,7 @@ void uiEvent(mjuiState* state) {
sim->ui1.nsect = SECT_JOINT; sim->ui1.nsect = SECT_JOINT;
makejoint(sim, sim->ui1.sect[SECT_JOINT].state); makejoint(sim, sim->ui1.sect[SECT_JOINT].state);
sim->ui1.nsect = NSECT1; sim->ui1.nsect = NSECT1;
uiModify(sim->window, &sim->ui1, state, &sim->con); uiModify(reinterpret_cast<GLFWwindow*>(sim->window), &sim->ui1, state, &sim->con);
} }
// remake control section if actuator group changed // remake control section if actuator group changed
@@ -1271,7 +1274,7 @@ void uiEvent(mjuiState* state) {
sim->ui1.nsect = SECT_CONTROL; sim->ui1.nsect = SECT_CONTROL;
makecontrol(sim, sim->ui1.sect[SECT_CONTROL].state); makecontrol(sim, sim->ui1.sect[SECT_CONTROL].state);
sim->ui1.nsect = NSECT1; sim->ui1.nsect = NSECT1;
uiModify(sim->window, &sim->ui1, state, &sim->con); uiModify(reinterpret_cast<GLFWwindow*>(sim->window), &sim->ui1, state, &sim->con);
} }
} }
@@ -1574,11 +1577,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);
{ {
@@ -1594,16 +1595,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;
@@ -1626,10 +1617,10 @@ void Simulate::loadmodel() {
mjv_updateScene(this->m, this->d, &this->vopt, &this->pert, &this->cam, mjCAT_ALL, &this->scn); mjv_updateScene(this->m, this->d, &this->vopt, &this->pert, &this->cam, mjCAT_ALL, &this->scn);
// set window title to model name // set window title to model name
if (this->window && this->m->names) { if (reinterpret_cast<GLFWwindow*>(this->window) && this->m->names) {
char title[200] = "Simulate : "; char title[200] = "Simulate : ";
mju::strcat_arr(title, this->m->names); mju::strcat_arr(title, this->m->names);
Glfw().glfwSetWindowTitle(this->window, title); Glfw().glfwSetWindowTitle(reinterpret_cast<GLFWwindow*>(this->window), title);
} }
// set keyframe range and divisions // set keyframe range and divisions
@@ -1641,8 +1632,8 @@ void Simulate::loadmodel() {
makesections(this); makesections(this);
// full ui update // full ui update
uiModify(this->window, &this->ui0, &this->uistate, &this->con); uiModify(reinterpret_cast<GLFWwindow*>(this->window), &this->ui0, &this->uistate, &this->con);
uiModify(this->window, &this->ui1, &this->uistate, &this->con); uiModify(reinterpret_cast<GLFWwindow*>(this->window), &this->ui1, &this->uistate, &this->con);
updatesettings(this); updatesettings(this);
// clear request // clear request
@@ -1758,7 +1749,7 @@ void Simulate::render() {
} }
// finalize // finalize
Glfw().glfwSwapBuffers(this->window); Glfw().glfwSwapBuffers(reinterpret_cast<GLFWwindow*>(this->window));
return; return;
} }
@@ -1865,13 +1856,13 @@ void Simulate::render() {
} }
// finalize // finalize
Glfw().glfwSwapBuffers(this->window); Glfw().glfwSwapBuffers(reinterpret_cast<GLFWwindow*>(this->window));
} }
// clear callbacks registered in external structures // clear callbacks registered in external structures
void Simulate::clearcallback() { void Simulate::clearcallback() {
uiClearCallback(this->window); uiClearCallback(reinterpret_cast<GLFWwindow*>(this->window));
} }
void Simulate::renderloop() { void Simulate::renderloop() {
@@ -1883,25 +1874,27 @@ void Simulate::renderloop() {
Glfw().glfwWindowHint(GLFW_VISIBLE, 1); Glfw().glfwWindowHint(GLFW_VISIBLE, 1);
// get videomode and save // get videomode and save
this->vmode = *Glfw().glfwGetVideoMode(Glfw().glfwGetPrimaryMonitor()); this->vmode = Glfw().glfwGetVideoMode(Glfw().glfwGetPrimaryMonitor());
// use videomode refreshrate if nonzero // use videomode refreshrate if nonzero
if (this->vmode.refreshRate) this->refreshRate = this->vmode.refreshRate; if (reinterpret_cast<const GLFWvidmode*>(this->vmode)->refreshRate)
this->refreshRate = reinterpret_cast<const GLFWvidmode*>(this->vmode)->refreshRate;
// create window // create window
this->window = Glfw().glfwCreateWindow((2*this->vmode.width)/3, (2*this->vmode.height)/3, this->window = Glfw().glfwCreateWindow((2*reinterpret_cast<const GLFWvidmode*>(this->vmode)->width)/3,
(2*reinterpret_cast<const GLFWvidmode*>(this->vmode)->height)/3,
"Simulate", nullptr, nullptr); "Simulate", nullptr, nullptr);
if (!this->window) { if (!reinterpret_cast<GLFWwindow*>(this->window)) {
Glfw().glfwTerminate(); Glfw().glfwTerminate();
mju_error("could not create window"); mju_error("could not create window");
} }
// save window position and size // save window position and size
Glfw().glfwGetWindowPos(this->window, this->windowpos, this->windowpos+1); Glfw().glfwGetWindowPos(reinterpret_cast<GLFWwindow*>(this->window), this->windowpos, this->windowpos+1);
Glfw().glfwGetWindowSize(this->window, this->windowsize, this->windowsize+1); Glfw().glfwGetWindowSize(reinterpret_cast<GLFWwindow*>(this->window), this->windowsize, this->windowsize+1);
// make context current, set v-sync // make context current, set v-sync
Glfw().glfwMakeContextCurrent(this->window); Glfw().glfwMakeContextCurrent(reinterpret_cast<GLFWwindow*>(this->window));
Glfw().glfwSwapInterval(this->vsync); Glfw().glfwSwapInterval(this->vsync);
// init abstract visualization // init abstract visualization
@@ -1915,7 +1908,7 @@ void Simulate::renderloop() {
mjv_makeScene(nullptr, &this->scn, maxgeom); mjv_makeScene(nullptr, &this->scn, maxgeom);
// select default font // select default font
int fontscale = uiFontScale(this->window); int fontscale = uiFontScale(reinterpret_cast<GLFWwindow*>(this->window));
this->font = fontscale/50 - 1; this->font = fontscale/50 - 1;
// make empty context // make empty context
@@ -1939,7 +1932,7 @@ void Simulate::renderloop() {
// set GLFW callbacks // set GLFW callbacks
this->uistate.userdata = this; this->uistate.userdata = this;
uiSetCallback(this->window, &this->uistate, uiEvent, uiLayout, uiRender, uiDrop); uiSetCallback(reinterpret_cast<GLFWwindow*>(this->window), &this->uistate, uiEvent, uiLayout, uiRender, uiDrop);
// populate uis with standard sections // populate uis with standard sections
this->ui0.userdata = this; this->ui0.userdata = this;
@@ -1948,11 +1941,11 @@ void Simulate::renderloop() {
mjui_add(&this->ui0, this->defOption); mjui_add(&this->ui0, this->defOption);
mjui_add(&this->ui0, this->defSimulation); mjui_add(&this->ui0, this->defSimulation);
mjui_add(&this->ui0, this->defWatch); mjui_add(&this->ui0, this->defWatch);
uiModify(this->window, &this->ui0, &this->uistate, &this->con); uiModify(reinterpret_cast<GLFWwindow*>(this->window), &this->ui0, &this->uistate, &this->con);
uiModify(this->window, &this->ui1, &this->uistate, &this->con); uiModify(reinterpret_cast<GLFWwindow*>(this->window), &this->ui1, &this->uistate, &this->con);
// run event loop // run event loop
while (!Glfw().glfwWindowShouldClose(this->window) && !this->exitrequest.load()) { while (!Glfw().glfwWindowShouldClose(reinterpret_cast<GLFWwindow*>(this->window)) && !this->exitrequest.load()) {
{ {
const std::lock_guard<std::mutex> lock(this->mtx); const std::lock_guard<std::mutex> lock(this->mtx);
@@ -1979,6 +1972,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(reinterpret_cast<GLFWwindow*>(this->window));
}
//------------------------------------ setup the glfw dispatch table -------------------------------
void setglfwdlhandle(void* dlhandle) {
Glfw(dlhandle);
} }
} // namespace mujoco } // namespace mujoco
+17 -12
View File
@@ -23,17 +23,17 @@
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <mujoco/mujoco.h> #include <mujoco/mujoco.h>
#ifdef MJSIMULATE_STATIC #ifdef LIBSIMULATE_STATIC
// static library // static library
#define MJSIMULATEAPI #define LIBSIMULATEAPI
#define MJSIMULATELOCAL #define LIBSIMULATELOCAL
#else #else
#ifdef MJSIMULATE_DLL_EXPORTS #ifdef LIBSIMULATE_DLL_EXPORTS
#define MJSIMULATEAPI MUJOCO_HELPER_DLL_EXPORT #define LIBSIMULATEAPI MUJOCO_HELPER_DLL_EXPORT
#else #else
#define MJSIMULATEAPI MUJOCO_HELPER_DLL_IMPORT #define LIBSIMULATEAPI MUJOCO_HELPER_DLL_IMPORT
#endif #endif
#define MJSIMULATELOCAL MUJOCO_HELPER_DLL_LOCAL #define LIBSIMULATELOCAL MUJOCO_HELPER_DLL_LOCAL
#endif #endif
namespace mujoco { namespace mujoco {
@@ -41,7 +41,7 @@ namespace mujoco {
//-------------------------------- global ----------------------------------------------- //-------------------------------- global -----------------------------------------------
// Simulate states not contained in MuJoCo structures // Simulate states not contained in MuJoCo structures
class MJSIMULATEAPI Simulate { class LIBSIMULATEAPI Simulate {
public: public:
// create object and initialize the simulate ui // create object and initialize the simulate ui
Simulate() = default; Simulate() = default;
@@ -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;
@@ -163,12 +162,14 @@ class MJSIMULATEAPI Simulate {
mjvFigure figsensor = {}; mjvFigure figsensor = {};
// OpenGL rendering and UI // OpenGL rendering and UI
GLFWvidmode vmode = {}; // Use void* for GLFW objects to avoid requiring users of the shared library
// from needing GLFW headers (in particular Python should not need them)
const void* vmode = {}; // const GLFWvidmode*
int refreshRate = 60; int refreshRate = 60;
int windowpos[2] = {0}; int windowpos[2] = {0};
int windowsize[2] = {0}; int windowsize[2] = {0};
mjrContext con = {}; mjrContext con = {};
GLFWwindow* window = nullptr; void* window = nullptr; // GLFWwindow*
mjuiState uistate = {}; mjuiState uistate = {};
mjUI ui0 = {}; mjUI ui0 = {};
mjUI ui1 = {}; mjUI ui1 = {};
@@ -228,6 +229,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
LIBSIMULATEAPI void setglfwdlhandle(void* dlhandle);
} // namespace mujoco } // namespace mujoco
#endif #endif