Update mujoco to version 3.2.7 for cpp simulate
This commit is contained in:
parent
464fd5e154
commit
f5b7837f83
|
@ -1,6 +1,6 @@
|
|||
<mxfile host="65bd71144e" scale="5" border="0">
|
||||
<diagram id="VpdAtZ29HSXdMf1KcTSp" name="第 1 页">
|
||||
<mxGraphModel dx="882" dy="593" grid="1" gridSize="5" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="none" math="0" shadow="0">
|
||||
<mxGraphModel dx="836" dy="447" grid="1" gridSize="5" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="none" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
|
@ -22,10 +22,10 @@
|
|||
<mxCell id="5" value="unitree_ros2" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="115" y="430" width="125" height="35" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="mujcoco cpp <br>simulate" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
||||
<mxCell id="6" value="mujoco cpp <br>simulate" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="446.26" y="372.5" width="110" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="mujcoco python<br>simulate" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
||||
<mxCell id="7" value="mujoco python<br>simulate" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
|
||||
<mxGeometry x="445.63" y="325" width="110" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" value="Unitree Robots" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||
|
@ -53,7 +53,7 @@
|
|||
<mxCell id="11" value="<font style="font-size: 16px;">Unitree API</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="111.25" y="290" width="132.5" height="35" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="<font style="font-size: 16px;">Unitree mujcoco</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxCell id="13" value="<font style="font-size: 16px;">Unitree mujoco</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="435" y="290" width="132.5" height="35" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
|
|
BIN
doc/func.png
BIN
doc/func.png
Binary file not shown.
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 155 KiB |
|
@ -11,6 +11,7 @@
|
|||
- `example`: Example programs
|
||||
|
||||
## Supported Unitree sdk2 Messages:
|
||||
**Current version only supports low-level development, mainly used for sim to real verification of controller**
|
||||
- `LowCmd`: Motor control commands
|
||||
- `LowState`: Motor state information
|
||||
- `SportModeState`: Robot position and velocity data
|
||||
|
@ -35,6 +36,7 @@ Note:
|
|||
## C++ Simulator (simulate)
|
||||
### 1. Dependencies
|
||||
#### unitree_sdk2
|
||||
It is recommended to install `unitree_sdk2` in `/opt/unitree_robotics` path.
|
||||
```bash
|
||||
git clone https://github.com/unitreerobotics/unitree_sdk2.git
|
||||
cd unitree_sdk2/
|
||||
|
@ -44,7 +46,8 @@ cmake .. -DCMAKE_INSTALL_PREFIX=/opt/unitree_robotics
|
|||
sudo make install
|
||||
```
|
||||
For more details, see: https://github.com/unitreerobotics/unitree_sdk2
|
||||
#### mujoco >= 3.0.0
|
||||
#### mujoco
|
||||
Current version is tested in mujoco-3.2.7
|
||||
```bash
|
||||
sudo apt install libglfw3-dev libxinerama-dev libxcursor-dev libxi-dev
|
||||
```
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
- `example`: 例程
|
||||
|
||||
## 支持的 Unitree sdk2 消息:
|
||||
**当前版本仅支持底层开发,主要用于控制器的 sim to real 验证**
|
||||
- `LowCmd`: 电机控制指令
|
||||
- `LowState`:电机状态
|
||||
- `SportModeState`:机器人位置和速度
|
||||
|
@ -36,6 +37,7 @@
|
|||
## c++ 仿真器 (simulate)
|
||||
### 1. 依赖
|
||||
#### unitree_sdk2
|
||||
推荐将 `unitree_sdk2` 安装在 `/opt/unitree_robotics` 路径下。
|
||||
```bash
|
||||
git clone https://github.com/unitreerobotics/unitree_sdk2.git
|
||||
cd unitree_sdk2/
|
||||
|
@ -45,7 +47,8 @@ cmake .. -DCMAKE_INSTALL_PREFIX=/opt/unitree_robotics
|
|||
sudo make install
|
||||
```
|
||||
详细见:https://github.com/unitreerobotics/unitree_sdk2
|
||||
#### mujoco >= 3.0.0
|
||||
#### mujoco
|
||||
当前版本基于 mujoco-3.2.7 测试
|
||||
```bash
|
||||
sudo apt install libglfw3-dev libxinerama-dev libxcursor-dev libxi-dev
|
||||
```
|
||||
|
|
|
@ -11,7 +11,7 @@ find_package(mujoco REQUIRED)
|
|||
find_package(unitree_sdk2 REQUIRED)
|
||||
|
||||
|
||||
FILE (GLOB SIM_SRC
|
||||
file(GLOB SIM_SRC
|
||||
src/joystick/joystick.cc
|
||||
src/mujoco/*.cc
|
||||
src/unitree_sdk2_bridge/*.cc)
|
||||
|
@ -32,4 +32,4 @@ target_link_libraries(test unitree_sdk2)
|
|||
|
||||
add_executable(jstest src/joystick/jstest.cc src/joystick/joystick.cc)
|
||||
|
||||
SET(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
|
|
|
@ -4,7 +4,7 @@ robot_scene: "scene.xml" # Robot scene, /unitree_robots/[robot]/scene.xml
|
|||
domain_id: 1 # Domain id
|
||||
interface: "lo" # Interface
|
||||
|
||||
use_joystick: 1 # Simulate Unitree WirelessController using a gamepad
|
||||
use_joystick: 0 # Simulate Unitree WirelessController using a gamepad
|
||||
joystick_type: "xbox" # support "xbox" and "switch" gamepad layout
|
||||
joystick_device: "/dev/input/js0" # Device path
|
||||
joystick_bits: 16 # Some game controllers may only have 8-bit accuracy
|
||||
|
|
|
@ -30,28 +30,35 @@
|
|||
//
|
||||
// They do not perform runtime bound checks.
|
||||
|
||||
namespace mujoco {
|
||||
namespace sample_util {
|
||||
namespace mujoco
|
||||
{
|
||||
namespace sample_util
|
||||
{
|
||||
|
||||
// returns sizeof(arr)
|
||||
// use instead of sizeof() to avoid unintended array-to-pointer decay
|
||||
template <typename T, int N>
|
||||
static constexpr std::size_t sizeof_arr(const T(&arr)[N]) {
|
||||
static constexpr std::size_t sizeof_arr(const T (&arr)[N])
|
||||
{
|
||||
return sizeof(arr);
|
||||
}
|
||||
|
||||
// like std::strcmp but it will not read beyond the bound of either lhs or rhs
|
||||
template <std::size_t N1, std::size_t N2>
|
||||
static inline int strcmp_arr(const char (&lhs)[N1], const char (&rhs)[N2]) {
|
||||
static inline int strcmp_arr(const char (&lhs)[N1], const char (&rhs)[N2])
|
||||
{
|
||||
return std::strncmp(lhs, rhs, std::min(N1, N2));
|
||||
}
|
||||
|
||||
// like std::strlen but it will not read beyond the bound of str
|
||||
// if str is not null-terminated, returns sizeof(str)
|
||||
template <std::size_t N>
|
||||
static inline std::size_t strlen_arr(const char (&str)[N]) {
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (str[i] == '\0') {
|
||||
static inline std::size_t strlen_arr(const char (&str)[N])
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
{
|
||||
if (str[i] == '\0')
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +68,8 @@ static inline std::size_t strlen_arr(const char (&str)[N]) {
|
|||
// like std::sprintf but will not write beyond the bound of dest
|
||||
// dest is guaranteed to be null-terminated
|
||||
template <std::size_t N>
|
||||
static inline int sprintf_arr(char (&dest)[N], const char* format, ...) {
|
||||
static inline int sprintf_arr(char (&dest)[N], const char *format, ...)
|
||||
{
|
||||
std::va_list vargs;
|
||||
va_start(vargs, format);
|
||||
int retval = std::vsnprintf(dest, N, format, vargs);
|
||||
|
@ -72,12 +80,15 @@ static inline int sprintf_arr(char (&dest)[N], const char* format, ...) {
|
|||
// like std::strcat but will not write beyond the bound of dest
|
||||
// dest is guaranteed to be null-terminated
|
||||
template <std::size_t N>
|
||||
static inline char* strcat_arr(char (&dest)[N], const char* src) {
|
||||
static inline char *strcat_arr(char (&dest)[N], const char *src)
|
||||
{
|
||||
const std::size_t dest_len = strlen_arr(dest);
|
||||
const std::size_t dest_size = sizeof_arr(dest);
|
||||
for (std::size_t i = dest_len; i < dest_size; ++i) {
|
||||
for (std::size_t i = dest_len; i < dest_size; ++i)
|
||||
{
|
||||
dest[i] = src[i - dest_len];
|
||||
if (!dest[i]) {
|
||||
if (!dest[i])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -88,10 +99,12 @@ static inline char* strcat_arr(char (&dest)[N], const char* src) {
|
|||
// like std::strcpy but won't write beyond the bound of dest
|
||||
// dest is guaranteed to be null-terminated
|
||||
template <std::size_t N>
|
||||
static inline char* strcpy_arr(char (&dest)[N], const char* src) {
|
||||
static inline char *strcpy_arr(char (&dest)[N], const char *src)
|
||||
{
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; src[i] && i < N - 1; ++i) {
|
||||
for (; src[i] && i < N - 1; ++i)
|
||||
{
|
||||
dest[i] = src[i];
|
||||
}
|
||||
dest[i] = '\0';
|
||||
|
|
|
@ -26,12 +26,17 @@
|
|||
#include "glfw_corevideo.h"
|
||||
#endif
|
||||
|
||||
namespace mujoco {
|
||||
namespace {
|
||||
int MaybeGlfwInit() {
|
||||
static const int is_initialized = []() {
|
||||
namespace mujoco
|
||||
{
|
||||
namespace
|
||||
{
|
||||
int MaybeGlfwInit()
|
||||
{
|
||||
static const int is_initialized = []()
|
||||
{
|
||||
auto success = Glfw().glfwInit();
|
||||
if (success == GLFW_TRUE) {
|
||||
if (success == GLFW_TRUE)
|
||||
{
|
||||
std::atexit(Glfw().glfwTerminate);
|
||||
}
|
||||
return success;
|
||||
|
@ -39,13 +44,16 @@ int MaybeGlfwInit() {
|
|||
return is_initialized;
|
||||
}
|
||||
|
||||
GlfwAdapter& GlfwAdapterFromWindow(GLFWwindow* window) {
|
||||
GlfwAdapter &GlfwAdapterFromWindow(GLFWwindow *window)
|
||||
{
|
||||
return *static_cast<GlfwAdapter *>(Glfw().glfwGetWindowUserPointer(window));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
GlfwAdapter::GlfwAdapter() {
|
||||
if (MaybeGlfwInit() != GLFW_TRUE) {
|
||||
GlfwAdapter::GlfwAdapter()
|
||||
{
|
||||
if (MaybeGlfwInit() != GLFW_TRUE)
|
||||
{
|
||||
mju_error("could not initialize GLFW");
|
||||
}
|
||||
|
||||
|
@ -60,7 +68,8 @@ GlfwAdapter::GlfwAdapter() {
|
|||
window_ = Glfw().glfwCreateWindow((2 * vidmode_.width) / 3,
|
||||
(2 * vidmode_.height) / 3,
|
||||
"MuJoCo", nullptr, nullptr);
|
||||
if (!window_) {
|
||||
if (!window_)
|
||||
{
|
||||
mju_error("could not create window");
|
||||
}
|
||||
|
||||
|
@ -71,93 +80,99 @@ GlfwAdapter::GlfwAdapter() {
|
|||
// set callbacks
|
||||
Glfw().glfwSetWindowUserPointer(window_, this);
|
||||
Glfw().glfwSetDropCallback(
|
||||
window_, +[](GLFWwindow* window, int count, const char** paths) {
|
||||
GlfwAdapterFromWindow(window).OnFilesDrop(count, paths);
|
||||
});
|
||||
window_, +[](GLFWwindow *window, int count, const char **paths)
|
||||
{ GlfwAdapterFromWindow(window).OnFilesDrop(count, paths); });
|
||||
Glfw().glfwSetKeyCallback(
|
||||
window_, +[](GLFWwindow* window, int key, int scancode, int act, int mods) {
|
||||
GlfwAdapterFromWindow(window).OnKey(key, scancode, act);
|
||||
});
|
||||
window_, +[](GLFWwindow *window, int key, int scancode, int act, int mods)
|
||||
{ GlfwAdapterFromWindow(window).OnKey(key, scancode, act); });
|
||||
Glfw().glfwSetMouseButtonCallback(
|
||||
window_, +[](GLFWwindow* window, int button, int act, int mods) {
|
||||
GlfwAdapterFromWindow(window).OnMouseButton(button, act);
|
||||
});
|
||||
window_, +[](GLFWwindow *window, int button, int act, int mods)
|
||||
{ GlfwAdapterFromWindow(window).OnMouseButton(button, act); });
|
||||
Glfw().glfwSetCursorPosCallback(
|
||||
window_, +[](GLFWwindow* window, double x, double y) {
|
||||
GlfwAdapterFromWindow(window).OnMouseMove(x, y);
|
||||
});
|
||||
window_, +[](GLFWwindow *window, double x, double y)
|
||||
{ GlfwAdapterFromWindow(window).OnMouseMove(x, y); });
|
||||
Glfw().glfwSetScrollCallback(
|
||||
window_, +[](GLFWwindow* window, double xoffset, double yoffset) {
|
||||
GlfwAdapterFromWindow(window).OnScroll(xoffset, yoffset);
|
||||
});
|
||||
window_, +[](GLFWwindow *window, double xoffset, double yoffset)
|
||||
{ GlfwAdapterFromWindow(window).OnScroll(xoffset, yoffset); });
|
||||
Glfw().glfwSetWindowRefreshCallback(
|
||||
window_, +[](GLFWwindow* window) {
|
||||
window_, +[](GLFWwindow *window)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
auto& core_video = GlfwAdapterFromWindow(window).core_video_;
|
||||
if (core_video.has_value()) {
|
||||
core_video->UpdateDisplayLink();
|
||||
}
|
||||
#endif
|
||||
GlfwAdapterFromWindow(window).OnWindowRefresh();
|
||||
});
|
||||
GlfwAdapterFromWindow(window).OnWindowRefresh(); });
|
||||
Glfw().glfwSetWindowSizeCallback(
|
||||
window_, +[](GLFWwindow* window, int width, int height) {
|
||||
GlfwAdapterFromWindow(window).OnWindowResize(width, height);
|
||||
});
|
||||
window_, +[](GLFWwindow *window, int width, int height)
|
||||
{ GlfwAdapterFromWindow(window).OnWindowResize(width, height); });
|
||||
|
||||
// make context current
|
||||
Glfw().glfwMakeContextCurrent(window_);
|
||||
}
|
||||
|
||||
GlfwAdapter::~GlfwAdapter() {
|
||||
GlfwAdapter::~GlfwAdapter()
|
||||
{
|
||||
FreeMjrContext();
|
||||
Glfw().glfwMakeContextCurrent(nullptr);
|
||||
Glfw().glfwDestroyWindow(window_);
|
||||
}
|
||||
|
||||
std::pair<double, double> GlfwAdapter::GetCursorPosition() const {
|
||||
std::pair<double, double> GlfwAdapter::GetCursorPosition() const
|
||||
{
|
||||
double x, y;
|
||||
Glfw().glfwGetCursorPos(window_, &x, &y);
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
double GlfwAdapter::GetDisplayPixelsPerInch() const {
|
||||
double GlfwAdapter::GetDisplayPixelsPerInch() const
|
||||
{
|
||||
int width_mm, height_mm;
|
||||
Glfw().glfwGetMonitorPhysicalSize(
|
||||
Glfw().glfwGetPrimaryMonitor(), &width_mm, &height_mm);
|
||||
return 25.4 * vidmode_.width / width_mm;
|
||||
}
|
||||
|
||||
std::pair<int, int> GlfwAdapter::GetFramebufferSize() const {
|
||||
std::pair<int, int> GlfwAdapter::GetFramebufferSize() const
|
||||
{
|
||||
int width, height;
|
||||
Glfw().glfwGetFramebufferSize(window_, &width, &height);
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
std::pair<int, int> GlfwAdapter::GetWindowSize() const {
|
||||
std::pair<int, int> GlfwAdapter::GetWindowSize() const
|
||||
{
|
||||
int width, height;
|
||||
Glfw().glfwGetWindowSize(window_, &width, &height);
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsGPUAccelerated() const {
|
||||
bool GlfwAdapter::IsGPUAccelerated() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlfwAdapter::PollEvents() {
|
||||
void GlfwAdapter::PollEvents()
|
||||
{
|
||||
Glfw().glfwPollEvents();
|
||||
}
|
||||
|
||||
void GlfwAdapter::SetClipboardString(const char* text) {
|
||||
void GlfwAdapter::SetClipboardString(const char *text)
|
||||
{
|
||||
Glfw().glfwSetClipboardString(window_, text);
|
||||
}
|
||||
|
||||
void GlfwAdapter::SetVSync(bool enabled){
|
||||
void GlfwAdapter::SetVSync(bool enabled)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
Glfw().glfwSwapInterval(0);
|
||||
if (enabled && !core_video_.has_value()) {
|
||||
if (enabled && !core_video_.has_value())
|
||||
{
|
||||
core_video_.emplace(window_);
|
||||
} else if (!enabled && core_video_.has_value()) {
|
||||
}
|
||||
else if (!enabled && core_video_.has_value())
|
||||
{
|
||||
core_video_.reset();
|
||||
}
|
||||
#else
|
||||
|
@ -165,33 +180,40 @@ void GlfwAdapter::SetVSync(bool enabled){
|
|||
#endif
|
||||
}
|
||||
|
||||
void GlfwAdapter::SetWindowTitle(const char* title) {
|
||||
void GlfwAdapter::SetWindowTitle(const char *title)
|
||||
{
|
||||
Glfw().glfwSetWindowTitle(window_, title);
|
||||
}
|
||||
|
||||
bool GlfwAdapter::ShouldCloseWindow() const {
|
||||
bool GlfwAdapter::ShouldCloseWindow() const
|
||||
{
|
||||
return Glfw().glfwWindowShouldClose(window_);
|
||||
}
|
||||
|
||||
void GlfwAdapter::SwapBuffers() {
|
||||
void GlfwAdapter::SwapBuffers()
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
if (core_video_.has_value()) {
|
||||
if (core_video_.has_value())
|
||||
{
|
||||
core_video_->WaitForDisplayRefresh();
|
||||
}
|
||||
#endif
|
||||
Glfw().glfwSwapBuffers(window_);
|
||||
}
|
||||
|
||||
void GlfwAdapter::ToggleFullscreen() {
|
||||
void GlfwAdapter::ToggleFullscreen()
|
||||
{
|
||||
// currently full screen: switch to windowed
|
||||
if (Glfw().glfwGetWindowMonitor(window_)) {
|
||||
if (Glfw().glfwGetWindowMonitor(window_))
|
||||
{
|
||||
// restore window from saved data
|
||||
Glfw().glfwSetWindowMonitor(window_, nullptr, window_pos_.first, window_pos_.second,
|
||||
window_size_.first, window_size_.second, 0);
|
||||
}
|
||||
|
||||
// currently windowed: switch to full screen
|
||||
else {
|
||||
else
|
||||
{
|
||||
// save window data
|
||||
Glfw().glfwGetWindowPos(window_, &window_pos_.first, &window_pos_.second);
|
||||
Glfw().glfwGetWindowSize(window_, &window_size_.first,
|
||||
|
@ -204,38 +226,41 @@ void GlfwAdapter::ToggleFullscreen() {
|
|||
}
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsLeftMouseButtonPressed() const {
|
||||
bool GlfwAdapter::IsLeftMouseButtonPressed() const
|
||||
{
|
||||
return Glfw().glfwGetMouseButton(window_, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsMiddleMouseButtonPressed() const {
|
||||
bool GlfwAdapter::IsMiddleMouseButtonPressed() const
|
||||
{
|
||||
return Glfw().glfwGetMouseButton(window_, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsRightMouseButtonPressed() const {
|
||||
bool GlfwAdapter::IsRightMouseButtonPressed() const
|
||||
{
|
||||
return Glfw().glfwGetMouseButton(window_, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsAltKeyPressed() const {
|
||||
bool GlfwAdapter::IsAltKeyPressed() const
|
||||
{
|
||||
return Glfw().glfwGetKey(window_, GLFW_KEY_LEFT_ALT) == GLFW_PRESS ||
|
||||
Glfw().glfwGetKey(window_, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsCtrlKeyPressed() const {
|
||||
bool GlfwAdapter::IsCtrlKeyPressed() const
|
||||
{
|
||||
return Glfw().glfwGetKey(window_, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS ||
|
||||
Glfw().glfwGetKey(window_, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsShiftKeyPressed() const {
|
||||
bool GlfwAdapter::IsShiftKeyPressed() const
|
||||
{
|
||||
return Glfw().glfwGetKey(window_, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS ||
|
||||
Glfw().glfwGetKey(window_, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsKeyPressed(int key) const {
|
||||
return Glfw().glfwGetKey(window_, key) == GLFW_PRESS;
|
||||
}
|
||||
|
||||
bool GlfwAdapter::IsMouseButtonDownEvent(int act) const {
|
||||
bool GlfwAdapter::IsMouseButtonDownEvent(int act) const
|
||||
{
|
||||
return act == GLFW_PRESS;
|
||||
}
|
||||
|
||||
|
@ -243,12 +268,18 @@ bool GlfwAdapter::IsKeyDownEvent(int act) const { return act == GLFW_PRESS; }
|
|||
|
||||
int GlfwAdapter::TranslateKeyCode(int key) const { return key; }
|
||||
|
||||
mjtButton GlfwAdapter::TranslateMouseButton(int button) const {
|
||||
if (button == GLFW_MOUSE_BUTTON_LEFT) {
|
||||
mjtButton GlfwAdapter::TranslateMouseButton(int button) const
|
||||
{
|
||||
if (button == GLFW_MOUSE_BUTTON_LEFT)
|
||||
{
|
||||
return mjBUTTON_LEFT;
|
||||
} else if (button == GLFW_MOUSE_BUTTON_RIGHT) {
|
||||
}
|
||||
else if (button == GLFW_MOUSE_BUTTON_RIGHT)
|
||||
{
|
||||
return mjBUTTON_RIGHT;
|
||||
} else if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
|
||||
}
|
||||
else if (button == GLFW_MOUSE_BUTTON_MIDDLE)
|
||||
{
|
||||
return mjBUTTON_MIDDLE;
|
||||
}
|
||||
return mjBUTTON_NONE;
|
||||
|
|
|
@ -26,8 +26,10 @@
|
|||
#include "glfw_corevideo.h"
|
||||
#endif
|
||||
|
||||
namespace mujoco {
|
||||
class GlfwAdapter : public PlatformUIAdapter {
|
||||
namespace mujoco
|
||||
{
|
||||
class GlfwAdapter : public PlatformUIAdapter
|
||||
{
|
||||
public:
|
||||
GlfwAdapter();
|
||||
~GlfwAdapter() override;
|
||||
|
@ -53,8 +55,6 @@ class GlfwAdapter : public PlatformUIAdapter {
|
|||
bool IsCtrlKeyPressed() const override;
|
||||
bool IsShiftKeyPressed() const override;
|
||||
|
||||
bool IsKeyPressed(int key) const override;
|
||||
|
||||
bool IsMouseButtonDownEvent(int act) const override;
|
||||
bool IsKeyDownEvent(int act) const override;
|
||||
|
||||
|
|
|
@ -33,8 +33,10 @@ typedef void* CVDisplayLinkRef;
|
|||
|
||||
// Workaround for perpertually broken OpenGL VSync on macOS,
|
||||
// most recently https://github.com/glfw/glfw/issues/2249.
|
||||
namespace mujoco {
|
||||
class GlfwCoreVideo {
|
||||
namespace mujoco
|
||||
{
|
||||
class GlfwCoreVideo
|
||||
{
|
||||
public:
|
||||
GlfwCoreVideo(GLFWwindow *window);
|
||||
~GlfwCoreVideo();
|
||||
|
@ -53,5 +55,4 @@ class GlfwCoreVideo {
|
|||
};
|
||||
} // namespace mujoco
|
||||
|
||||
|
||||
#endif // MUJOCO_SIMULATE_GLFW_COREVIDEO_H_
|
||||
|
|
|
@ -26,16 +26,19 @@
|
|||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
namespace mujoco {
|
||||
namespace mujoco
|
||||
{
|
||||
|
||||
// return dispatch table for glfw functions
|
||||
const struct Glfw& Glfw(void* dlhandle) {
|
||||
const struct Glfw &Glfw(void *dlhandle)
|
||||
{
|
||||
{
|
||||
// set static init_dlhandle
|
||||
static const void *init_dlhandle = dlhandle;
|
||||
|
||||
// check that not already initialized
|
||||
if (dlhandle && dlhandle != init_dlhandle) {
|
||||
if (dlhandle && dlhandle != init_dlhandle)
|
||||
{
|
||||
std::cerr << "dlhandle is specified when GLFW dispatch table is already "
|
||||
"initialized\n";
|
||||
abort();
|
||||
|
@ -50,8 +53,10 @@ const struct Glfw& Glfw(void* dlhandle) {
|
|||
// load glfw dynamically
|
||||
#ifdef mjGLFW_DYNAMIC_SYMBOLS
|
||||
#ifdef _MSC_VER
|
||||
if (!dlhandle) dlhandle = LoadLibraryA("glfw3.dll");
|
||||
if (!dlhandle) {
|
||||
if (!dlhandle)
|
||||
dlhandle = LoadLibraryA("glfw3.dll");
|
||||
if (!dlhandle)
|
||||
{
|
||||
std::cerr << "cannot obtain a shared object handle\n";
|
||||
abort();
|
||||
}
|
||||
|
@ -59,8 +64,10 @@ const struct Glfw& Glfw(void* dlhandle) {
|
|||
glfw.func = reinterpret_cast<decltype(glfw.func)>( \
|
||||
GetProcAddress(reinterpret_cast<HMODULE>(dlhandle), #func))
|
||||
#else
|
||||
if (!dlhandle) dlhandle = dlopen("nullptr", RTLD_GLOBAL | RTLD_NOW);
|
||||
if (!dlhandle) {
|
||||
if (!dlhandle)
|
||||
dlhandle = dlopen("nullptr", RTLD_GLOBAL | RTLD_NOW);
|
||||
if (!dlhandle)
|
||||
{
|
||||
std::cerr << "cannot obtain a shared object handle\n";
|
||||
abort();
|
||||
}
|
||||
|
@ -73,7 +80,8 @@ const struct Glfw& Glfw(void* dlhandle) {
|
|||
|
||||
// set pointers in dispatch table
|
||||
#define mjGLFW_INITIALIZE_SYMBOL(func) \
|
||||
if (!(mjGLFW_RESOLVE_SYMBOL(func))) { \
|
||||
if (!(mjGLFW_RESOLVE_SYMBOL(func))) \
|
||||
{ \
|
||||
std::cerr << "cannot dlsym " #func "\n"; \
|
||||
abort(); \
|
||||
}
|
||||
|
|
|
@ -22,11 +22,13 @@
|
|||
#include <GLFW/glfw3native.h>
|
||||
#endif
|
||||
|
||||
namespace mujoco {
|
||||
namespace mujoco
|
||||
{
|
||||
// Dynamic dispatch table for GLFW functions required by Simulate.
|
||||
// This allows us to use GLFW without introducing a link-time dependency on the
|
||||
// library, which is useful e.g. when using GLFW via Python.
|
||||
struct Glfw {
|
||||
struct Glfw
|
||||
{
|
||||
#define mjGLFW_DECLARE_SYMBOL(func) decltype(&::func) func
|
||||
// go/keep-sorted start
|
||||
mjGLFW_DECLARE_SYMBOL(glfwCreateWindow);
|
||||
|
|
|
@ -16,17 +16,22 @@
|
|||
|
||||
#include <chrono>
|
||||
|
||||
namespace mujoco {
|
||||
PlatformUIAdapter::PlatformUIAdapter() {
|
||||
namespace mujoco
|
||||
{
|
||||
PlatformUIAdapter::PlatformUIAdapter()
|
||||
{
|
||||
mjr_defaultContext(&con_);
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::FreeMjrContext() {
|
||||
void PlatformUIAdapter::FreeMjrContext()
|
||||
{
|
||||
mjr_freeContext(&con_);
|
||||
}
|
||||
|
||||
bool PlatformUIAdapter::RefreshMjrContext(const mjModel* m, int fontscale) {
|
||||
if (m != last_model_ || fontscale != last_fontscale_) {
|
||||
bool PlatformUIAdapter::RefreshMjrContext(const mjModel *m, int fontscale)
|
||||
{
|
||||
if (m != last_model_ || fontscale != last_fontscale_)
|
||||
{
|
||||
mjr_makeContext(m, &con_, fontscale);
|
||||
last_model_ = m;
|
||||
last_fontscale_ = fontscale;
|
||||
|
@ -35,17 +40,20 @@ bool PlatformUIAdapter::RefreshMjrContext(const mjModel* m, int fontscale) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool PlatformUIAdapter::EnsureContextSize() {
|
||||
bool PlatformUIAdapter::EnsureContextSize()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::OnFilesDrop(int count, const char** paths) {
|
||||
void PlatformUIAdapter::OnFilesDrop(int count, const char **paths)
|
||||
{
|
||||
state_.type = mjEVENT_FILESDROP;
|
||||
state_.dropcount = count;
|
||||
state_.droppaths = paths;
|
||||
|
||||
// application-specific processing
|
||||
if (event_callback_) {
|
||||
if (event_callback_)
|
||||
{
|
||||
event_callback_(&state_);
|
||||
}
|
||||
|
||||
|
@ -54,9 +62,11 @@ void PlatformUIAdapter::OnFilesDrop(int count, const char** paths) {
|
|||
state_.droppaths = nullptr;
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::OnKey(int key, int scancode, int act) {
|
||||
void PlatformUIAdapter::OnKey(int key, int scancode, int act)
|
||||
{
|
||||
// translate API-specific key code
|
||||
int mj_key = TranslateKeyCode(key);
|
||||
|
||||
if (act == GLFW_PRESS)
|
||||
{
|
||||
key_7_pressed_ = (key == GLFW_KEY_7);
|
||||
|
@ -69,8 +79,10 @@ void PlatformUIAdapter::OnKey(int key, int scancode, int act) {
|
|||
key_8_pressed_ = false;
|
||||
key_9_pressed_ = false;
|
||||
}
|
||||
|
||||
// release: nothing to do
|
||||
if (!IsKeyDownEvent(act)) {
|
||||
if (!IsKeyDownEvent(act))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -81,17 +93,20 @@ void PlatformUIAdapter::OnKey(int key, int scancode, int act) {
|
|||
state_.type = mjEVENT_KEY;
|
||||
state_.key = mj_key;
|
||||
state_.keytime = std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
// application-specific processing
|
||||
if (event_callback_) {
|
||||
if (event_callback_)
|
||||
{
|
||||
event_callback_(&state_);
|
||||
}
|
||||
|
||||
last_key_ = mj_key;
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::OnMouseButton(int button, int act) {
|
||||
void PlatformUIAdapter::OnMouseButton(int button, int act)
|
||||
{
|
||||
// translate API-specific mouse button code
|
||||
mjtButton mj_button = TranslateMouseButton(button);
|
||||
|
||||
|
@ -99,23 +114,32 @@ void PlatformUIAdapter::OnMouseButton(int button, int act) {
|
|||
UpdateMjuiState();
|
||||
|
||||
// swap left and right if Alt
|
||||
if (state_.alt) {
|
||||
if (mj_button == mjBUTTON_LEFT) {
|
||||
if (state_.alt)
|
||||
{
|
||||
if (mj_button == mjBUTTON_LEFT)
|
||||
{
|
||||
mj_button = mjBUTTON_RIGHT;
|
||||
} else if (mj_button == mjBUTTON_RIGHT) {
|
||||
}
|
||||
else if (mj_button == mjBUTTON_RIGHT)
|
||||
{
|
||||
mj_button = mjBUTTON_LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
// press
|
||||
if (IsMouseButtonDownEvent(act)) {
|
||||
if (IsMouseButtonDownEvent(act))
|
||||
{
|
||||
double now = std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
// detect doubleclick: 250 ms
|
||||
if (mj_button == state_.button && now - state_.buttontime < 0.25) {
|
||||
if (mj_button == state_.button && now - state_.buttontime < 0.25)
|
||||
{
|
||||
state_.doubleclick = 1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
state_.doubleclick = 0;
|
||||
}
|
||||
|
||||
|
@ -125,32 +149,38 @@ void PlatformUIAdapter::OnMouseButton(int button, int act) {
|
|||
state_.buttontime = now;
|
||||
|
||||
// start dragging
|
||||
if (state_.mouserect) {
|
||||
if (state_.mouserect)
|
||||
{
|
||||
state_.dragbutton = state_.button;
|
||||
state_.dragrect = state_.mouserect;
|
||||
}
|
||||
}
|
||||
|
||||
// release
|
||||
else {
|
||||
else
|
||||
{
|
||||
state_.type = mjEVENT_RELEASE;
|
||||
}
|
||||
|
||||
// application-specific processing
|
||||
if (event_callback_) {
|
||||
if (event_callback_)
|
||||
{
|
||||
event_callback_(&state_);
|
||||
}
|
||||
|
||||
// stop dragging after application processing
|
||||
if (state_.type == mjEVENT_RELEASE) {
|
||||
if (state_.type == mjEVENT_RELEASE)
|
||||
{
|
||||
state_.dragrect = 0;
|
||||
state_.dragbutton = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::OnMouseMove(double x, double y) {
|
||||
void PlatformUIAdapter::OnMouseMove(double x, double y)
|
||||
{
|
||||
// no buttons down: nothing to do
|
||||
if (!state_.left && !state_.right && !state_.middle) {
|
||||
if (!state_.left && !state_.right && !state_.middle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -161,12 +191,14 @@ void PlatformUIAdapter::OnMouseMove(double x, double y) {
|
|||
state_.type = mjEVENT_MOVE;
|
||||
|
||||
// application-specific processing
|
||||
if (event_callback_) {
|
||||
if (event_callback_)
|
||||
{
|
||||
event_callback_(&state_);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::OnScroll(double xoffset, double yoffset) {
|
||||
void PlatformUIAdapter::OnScroll(double xoffset, double yoffset)
|
||||
{
|
||||
// update state
|
||||
UpdateMjuiState();
|
||||
|
||||
|
@ -178,28 +210,34 @@ void PlatformUIAdapter::OnScroll(double xoffset, double yoffset) {
|
|||
state_.sy = yoffset * buffer_window_ratio;
|
||||
|
||||
// application-specific processing
|
||||
if (event_callback_) {
|
||||
if (event_callback_)
|
||||
{
|
||||
event_callback_(&state_);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::OnWindowRefresh() {
|
||||
void PlatformUIAdapter::OnWindowRefresh()
|
||||
{
|
||||
state_.type = mjEVENT_REDRAW;
|
||||
|
||||
// application-specific processing
|
||||
if (event_callback_) {
|
||||
if (event_callback_)
|
||||
{
|
||||
event_callback_(&state_);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::OnWindowResize(int width, int height) {
|
||||
void PlatformUIAdapter::OnWindowResize(int width, int height)
|
||||
{
|
||||
auto [buf_width, buf_height] = GetFramebufferSize();
|
||||
state_.rect[0].width = buf_width;
|
||||
state_.rect[0].height = buf_height;
|
||||
if (state_.nrect < 1) state_.nrect = 1;
|
||||
if (state_.nrect < 1)
|
||||
state_.nrect = 1;
|
||||
|
||||
// update window layout
|
||||
if (layout_callback_) {
|
||||
if (layout_callback_)
|
||||
{
|
||||
layout_callback_(&state_);
|
||||
}
|
||||
|
||||
|
@ -214,12 +252,14 @@ void PlatformUIAdapter::OnWindowResize(int width, int height) {
|
|||
state_.dragrect = 0;
|
||||
|
||||
// application-specific processing
|
||||
if (event_callback_) {
|
||||
if (event_callback_)
|
||||
{
|
||||
event_callback_(&state_);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformUIAdapter::UpdateMjuiState() {
|
||||
void PlatformUIAdapter::UpdateMjuiState()
|
||||
{
|
||||
// mouse buttons
|
||||
state_.left = IsLeftMouseButtonPressed();
|
||||
state_.right = IsRightMouseButtonPressed();
|
||||
|
@ -231,7 +271,8 @@ void PlatformUIAdapter::UpdateMjuiState() {
|
|||
state_.alt = IsAltKeyPressed();
|
||||
|
||||
// swap left and right if Alt
|
||||
if (state_.alt) {
|
||||
if (state_.alt)
|
||||
{
|
||||
int tmp = state_.left;
|
||||
state_.left = state_.right;
|
||||
state_.right = tmp;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
|
||||
namespace mujoco
|
||||
|
@ -71,8 +70,6 @@ namespace mujoco
|
|||
virtual bool IsCtrlKeyPressed() const = 0;
|
||||
virtual bool IsShiftKeyPressed() const = 0;
|
||||
|
||||
virtual bool IsKeyPressed(int key) const = 0;
|
||||
|
||||
virtual bool IsMouseButtonDownEvent(int act) const = 0;
|
||||
virtual bool IsKeyDownEvent(int act) const = 0;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
@ -132,7 +133,7 @@ namespace
|
|||
|
||||
// file section of UI
|
||||
const mjuiDef defFile[] = {
|
||||
{mjITEM_SECTION, "File", 1, nullptr, "AF"},
|
||||
{mjITEM_SECTION, "File", mjPRESERVE, nullptr, "AF"},
|
||||
{mjITEM_BUTTON, "Save xml", 2, nullptr, ""},
|
||||
{mjITEM_BUTTON, "Save mjb", 2, nullptr, ""},
|
||||
{mjITEM_BUTTON, "Print model", 2, nullptr, "CM"},
|
||||
|
@ -651,13 +652,13 @@ namespace
|
|||
// prepare info text
|
||||
mju::strcpy_arr(title, "Time\nSize\nCPU\nSolver \nFPS\nMemory");
|
||||
mju::sprintf_arr(content,
|
||||
"%-9.3f\n%d (%d con)\n%.3f\n%.1f (%d it)\n%s\n%.2g of %s",
|
||||
"%-9.3f\n%d (%d con)\n%.3f\n%.1f (%d it)\n%s\n%.1f%% of %s",
|
||||
d->time,
|
||||
d->nefc, d->ncon,
|
||||
sim->run ? d->timer[mjTIMER_STEP].duration / mjMAX(1, d->timer[mjTIMER_STEP].number) : d->timer[mjTIMER_FORWARD].duration / mjMAX(1, d->timer[mjTIMER_FORWARD].number),
|
||||
solerr, solver_niter,
|
||||
fps,
|
||||
d->maxuse_arena / (double)(d->narena),
|
||||
100 * d->maxuse_arena / (double)(d->narena),
|
||||
mju_writeNumBytes(d->narena));
|
||||
|
||||
// add Energy if enabled
|
||||
|
@ -728,16 +729,16 @@ namespace
|
|||
//---------------------------------- UI construction -----------------------------------------------
|
||||
|
||||
// make physics section of UI
|
||||
void MakePhysicsSection(mj::Simulate *sim, int oldstate)
|
||||
void MakePhysicsSection(mj::Simulate *sim)
|
||||
{
|
||||
mjOption *opt = sim->is_passive_ ? &sim->scnstate_.model.opt : &sim->m_->opt;
|
||||
mjuiDef defPhysics[] = {
|
||||
{mjITEM_SECTION, "Physics", oldstate, nullptr, "AP"},
|
||||
{mjITEM_SECTION, "Physics", mjPRESERVE, nullptr, "AP"},
|
||||
{mjITEM_SELECT, "Integrator", 2, &(opt->integrator), "Euler\nRK4\nimplicit\nimplicitfast"},
|
||||
{mjITEM_SELECT, "Cone", 2, &(opt->cone), "Pyramidal\nElliptic"},
|
||||
{mjITEM_SELECT, "Jacobian", 2, &(opt->jacobian), "Dense\nSparse\nAuto"},
|
||||
{mjITEM_SELECT, "Solver", 2, &(opt->solver), "PGS\nCG\nNewton"},
|
||||
{mjITEM_SEPARATOR, "Algorithmic Parameters", 1},
|
||||
{mjITEM_SEPARATOR, "Algorithmic Parameters", mjPRESERVE},
|
||||
{mjITEM_EDITNUM, "Timestep", 2, &(opt->timestep), "1 0 1"},
|
||||
{mjITEM_EDITINT, "Iterations", 2, &(opt->iterations), "1 0 1000"},
|
||||
{mjITEM_EDITNUM, "Tolerance", 2, &(opt->tolerance), "1 0 1"},
|
||||
|
@ -745,38 +746,38 @@ namespace
|
|||
{mjITEM_EDITNUM, "LS Tol", 2, &(opt->ls_tolerance), "1 0 0.1"},
|
||||
{mjITEM_EDITINT, "Noslip Iter", 2, &(opt->noslip_iterations), "1 0 1000"},
|
||||
{mjITEM_EDITNUM, "Noslip Tol", 2, &(opt->noslip_tolerance), "1 0 1"},
|
||||
{mjITEM_EDITINT, "MPR Iter", 2, &(opt->mpr_iterations), "1 0 1000"},
|
||||
{mjITEM_EDITNUM, "MPR Tol", 2, &(opt->mpr_tolerance), "1 0 1"},
|
||||
{mjITEM_EDITINT, "CCD Iter", 2, &(opt->ccd_iterations), "1 0 1000"},
|
||||
{mjITEM_EDITNUM, "CCD Tol", 2, &(opt->ccd_tolerance), "1 0 1"},
|
||||
{mjITEM_EDITNUM, "API Rate", 2, &(opt->apirate), "1 0 1000"},
|
||||
{mjITEM_EDITINT, "SDF Iter", 2, &(opt->sdf_iterations), "1 1 20"},
|
||||
{mjITEM_EDITINT, "SDF Init", 2, &(opt->sdf_initpoints), "1 1 100"},
|
||||
{mjITEM_SEPARATOR, "Physical Parameters", 1},
|
||||
{mjITEM_SEPARATOR, "Physical Parameters", mjPRESERVE},
|
||||
{mjITEM_EDITNUM, "Gravity", 2, opt->gravity, "3"},
|
||||
{mjITEM_EDITNUM, "Wind", 2, opt->wind, "3"},
|
||||
{mjITEM_EDITNUM, "Magnetic", 2, opt->magnetic, "3"},
|
||||
{mjITEM_EDITNUM, "Density", 2, &(opt->density), "1"},
|
||||
{mjITEM_EDITNUM, "Viscosity", 2, &(opt->viscosity), "1"},
|
||||
{mjITEM_EDITNUM, "Imp Ratio", 2, &(opt->impratio), "1"},
|
||||
{mjITEM_SEPARATOR, "Disable Flags", 1},
|
||||
{mjITEM_SEPARATOR, "Disable Flags", mjPRESERVE},
|
||||
{mjITEM_END}};
|
||||
mjuiDef defEnableFlags[] = {
|
||||
{mjITEM_SEPARATOR, "Enable Flags", 1},
|
||||
{mjITEM_SEPARATOR, "Enable Flags", mjPRESERVE},
|
||||
{mjITEM_END}};
|
||||
mjuiDef defOverride[] = {
|
||||
{mjITEM_SEPARATOR, "Contact Override", 1},
|
||||
{mjITEM_SEPARATOR, "Contact Override", mjPRESERVE},
|
||||
{mjITEM_EDITNUM, "Margin", 2, &(opt->o_margin), "1"},
|
||||
{mjITEM_EDITNUM, "Sol Imp", 2, &(opt->o_solimp), "5"},
|
||||
{mjITEM_EDITNUM, "Sol Ref", 2, &(opt->o_solref), "2"},
|
||||
{mjITEM_EDITNUM, "Friction", 2, &(opt->o_friction), "5"},
|
||||
{mjITEM_END}};
|
||||
mjuiDef defDisableActuator[] = {
|
||||
{mjITEM_SEPARATOR, "Actuator Group Enable", 1},
|
||||
{mjITEM_CHECKBYTE, "Act Group 0", 2, sim->enableactuator + 0, " 0"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 1", 2, sim->enableactuator + 1, " 1"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 2", 2, sim->enableactuator + 2, " 2"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 3", 2, sim->enableactuator + 3, " 3"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 4", 2, sim->enableactuator + 4, " 4"},
|
||||
{mjITEM_CHECKBYTE, "Act Group 5", 2, sim->enableactuator + 5, " 5"},
|
||||
{mjITEM_SEPARATOR, "Actuator Group Enable", mjPRESERVE},
|
||||
{mjITEM_CHECKBYTE, "Act Group 0", 2, sim->enableactuator + 0, ""},
|
||||
{mjITEM_CHECKBYTE, "Act Group 1", 2, sim->enableactuator + 1, ""},
|
||||
{mjITEM_CHECKBYTE, "Act Group 2", 2, sim->enableactuator + 2, ""},
|
||||
{mjITEM_CHECKBYTE, "Act Group 3", 2, sim->enableactuator + 3, ""},
|
||||
{mjITEM_CHECKBYTE, "Act Group 4", 2, sim->enableactuator + 4, ""},
|
||||
{mjITEM_CHECKBYTE, "Act Group 5", 2, sim->enableactuator + 5, ""},
|
||||
{mjITEM_END}};
|
||||
|
||||
// add physics
|
||||
|
@ -804,41 +805,38 @@ namespace
|
|||
|
||||
// add actuator group enable/disable
|
||||
mjui_add(&sim->ui0, defDisableActuator);
|
||||
|
||||
// make some subsections closed by default
|
||||
for (int i = 0; i < sim->ui0.sect[SECT_PHYSICS].nitem; i++)
|
||||
{
|
||||
mjuiItem *it = sim->ui0.sect[SECT_PHYSICS].item + i;
|
||||
|
||||
// close less useful subsections
|
||||
if (it->type == mjITEM_SEPARATOR)
|
||||
{
|
||||
if (mju::strcmp_arr(it->name, "Actuator Group Enable") &&
|
||||
mju::strcmp_arr(it->name, "Contact Override") &&
|
||||
mju::strcmp_arr(it->name, "Physical Parameters"))
|
||||
{
|
||||
it->state = mjSEPCLOSED + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make rendering section of UI
|
||||
void MakeRenderingSection(mj::Simulate *sim, const mjModel *m, int oldstate)
|
||||
void MakeRenderingSection(mj::Simulate *sim, const mjModel *m)
|
||||
{
|
||||
mjuiDef defRendering[] = {
|
||||
{mjITEM_SECTION,
|
||||
"Rendering",
|
||||
oldstate,
|
||||
nullptr,
|
||||
"AR"},
|
||||
{mjITEM_SELECT,
|
||||
"Camera",
|
||||
2,
|
||||
&(sim->camera),
|
||||
"Free\nTracking"},
|
||||
{mjITEM_SELECT,
|
||||
"Label",
|
||||
2,
|
||||
&(sim->opt.label),
|
||||
{mjITEM_SECTION, "Rendering", mjPRESERVE, nullptr, "AR"},
|
||||
{mjITEM_SELECT, "Camera", 2, &(sim->camera), "Free\nTracking"},
|
||||
{mjITEM_SELECT, "Label", 2, &(sim->opt.label),
|
||||
"None\nBody\nJoint\nGeom\nSite\nCamera\nLight\nTendon\n"
|
||||
"Actuator\nConstraint\nFlex\nSkin\nSelection\nSel Pnt\nContact\nForce\nIsland"},
|
||||
{mjITEM_SELECT,
|
||||
"Frame",
|
||||
2,
|
||||
&(sim->opt.frame),
|
||||
{mjITEM_SELECT, "Frame", 2, &(sim->opt.frame),
|
||||
"None\nBody\nGeom\nSite\nCamera\nLight\nContact\nWorld"},
|
||||
{mjITEM_BUTTON,
|
||||
"Copy camera",
|
||||
2,
|
||||
nullptr,
|
||||
""},
|
||||
{mjITEM_SEPARATOR,
|
||||
"Model Elements",
|
||||
1},
|
||||
{mjITEM_BUTTON, "Copy camera", 2, nullptr, ""},
|
||||
{mjITEM_SEPARATOR, "Model Elements", 1},
|
||||
{mjITEM_END}};
|
||||
mjuiDef defOpenGL[] = {
|
||||
{mjITEM_SEPARATOR, "OpenGL Effects", 1},
|
||||
|
@ -877,17 +875,8 @@ namespace
|
|||
{mjITEM_END}};
|
||||
for (int i = 0; i < mjNVISFLAG; i++)
|
||||
{
|
||||
// set name, remove "&"
|
||||
// set name
|
||||
mju::strcpy_arr(defFlag[0].name, mjVISSTRING[i][0]);
|
||||
for (int j = 0; j < strlen(mjVISSTRING[i][0]); j++)
|
||||
{
|
||||
if (mjVISSTRING[i][0][j] == '&')
|
||||
{
|
||||
mju_strncpy(
|
||||
defFlag[0].name + j, mjVISSTRING[i][0] + j + 1, mju::sizeof_arr(defFlag[0].name) - j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set shortcut and data
|
||||
if (mjVISSTRING[i][2][0])
|
||||
|
@ -913,7 +902,10 @@ namespace
|
|||
mjui_add(&sim->ui0, defOpenGL);
|
||||
for (int i = 0; i < mjNRNDFLAG; i++)
|
||||
{
|
||||
// set name
|
||||
mju::strcpy_arr(defFlag[0].name, mjRNDSTRING[i][0]);
|
||||
|
||||
// set shortcut and data
|
||||
if (mjRNDSTRING[i][2][0])
|
||||
{
|
||||
mju::sprintf_arr(defFlag[0].other, " %s", mjRNDSTRING[i][2]);
|
||||
|
@ -928,27 +920,29 @@ namespace
|
|||
}
|
||||
|
||||
// make visualization section of UI
|
||||
void MakeVisualizationSection(mj::Simulate *sim, const mjModel *m, int oldstate)
|
||||
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;
|
||||
|
||||
mjuiDef defVisualization[] = {
|
||||
{mjITEM_SECTION, "Visualization", oldstate, nullptr, "AV"},
|
||||
{mjITEM_SECTION, "Visualization", mjPRESERVE, nullptr, "AV"},
|
||||
{mjITEM_SEPARATOR, "Headlight", 1},
|
||||
{mjITEM_RADIO, "Active", 5, &(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"},
|
||||
{mjITEM_SEPARATOR, "Initial Free Camera", 1},
|
||||
{mjITEM_SEPARATOR, "Free Camera", 1},
|
||||
{mjITEM_RADIO, "Orthographic", 2, &(vis->global.orthographic), "No\nYes"},
|
||||
{mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"},
|
||||
{mjITEM_EDITNUM, "Center", 2, &(stat->center), "3"},
|
||||
{mjITEM_EDITFLOAT, "Azimuth", 2, &(vis->global.azimuth), "1"},
|
||||
{mjITEM_EDITFLOAT, "Elevation", 2, &(vis->global.elevation), "1"},
|
||||
{mjITEM_BUTTON, "Align", 2, nullptr, "CA"},
|
||||
{mjITEM_SEPARATOR, "Global", 1},
|
||||
{mjITEM_EDITNUM, "Extent", 2, &(stat->extent), "1"},
|
||||
{mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"},
|
||||
{mjITEM_RADIO, "Inertia", 5, &(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"},
|
||||
{mjITEM_EDITFLOAT, "Rot stiffness", 2, &(vis->map.stiffnessrot), "1"},
|
||||
|
@ -962,8 +956,8 @@ namespace
|
|||
{mjITEM_EDITFLOAT, "Haze", 2, &(vis->map.haze), "1"},
|
||||
{mjITEM_EDITFLOAT, "Shadow clip", 2, &(vis->map.shadowclip), "1"},
|
||||
{mjITEM_EDITFLOAT, "Shadow scale", 2, &(vis->map.shadowscale), "1"},
|
||||
{mjITEM_SEPARATOR, "Scale", 1},
|
||||
{mjITEM_EDITNUM, "All [meansize]", 2, &(stat->meansize), "1"},
|
||||
{mjITEM_SEPARATOR, "Scale", mjPRESERVE},
|
||||
{mjITEM_EDITNUM, "All (meansize)", 2, &(stat->meansize), "1"},
|
||||
{mjITEM_EDITFLOAT, "Force width", 2, &(vis->scale.forcewidth), "1"},
|
||||
{mjITEM_EDITFLOAT, "Contact width", 2, &(vis->scale.contactwidth), "1"},
|
||||
{mjITEM_EDITFLOAT, "Contact height", 2, &(vis->scale.contactheight), "1"},
|
||||
|
@ -980,17 +974,43 @@ namespace
|
|||
{mjITEM_EDITFLOAT, "Frame width", 2, &(vis->scale.framewidth), "1"},
|
||||
{mjITEM_EDITFLOAT, "Constraint", 2, &(vis->scale.constraint), "1"},
|
||||
{mjITEM_EDITFLOAT, "Slider-crank", 2, &(vis->scale.slidercrank), "1"},
|
||||
{mjITEM_SEPARATOR, "RGBA", mjPRESERVE},
|
||||
{mjITEM_EDITFLOAT, "fog", 2, &(vis->rgba.fog), "4"},
|
||||
{mjITEM_EDITFLOAT, "haze", 2, &(vis->rgba.haze), "4"},
|
||||
{mjITEM_EDITFLOAT, "force", 2, &(vis->rgba.force), "4"},
|
||||
{mjITEM_EDITFLOAT, "inertia", 2, &(vis->rgba.inertia), "4"},
|
||||
{mjITEM_EDITFLOAT, "joint", 2, &(vis->rgba.joint), "4"},
|
||||
{mjITEM_EDITFLOAT, "actuator", 2, &(vis->rgba.actuator), "4"},
|
||||
{mjITEM_EDITFLOAT, "actnegative", 2, &(vis->rgba.actuatornegative), "4"},
|
||||
{mjITEM_EDITFLOAT, "actpositive", 2, &(vis->rgba.actuatorpositive), "4"},
|
||||
{mjITEM_EDITFLOAT, "com", 2, &(vis->rgba.com), "4"},
|
||||
{mjITEM_EDITFLOAT, "camera", 2, &(vis->rgba.camera), "4"},
|
||||
{mjITEM_EDITFLOAT, "light", 2, &(vis->rgba.light), "4"},
|
||||
{mjITEM_EDITFLOAT, "selectpoint", 2, &(vis->rgba.selectpoint), "4"},
|
||||
{mjITEM_EDITFLOAT, "connect", 2, &(vis->rgba.connect), "4"},
|
||||
{mjITEM_EDITFLOAT, "contactpoint", 2, &(vis->rgba.contactpoint), "4"},
|
||||
{mjITEM_EDITFLOAT, "contactforce", 2, &(vis->rgba.contactforce), "4"},
|
||||
{mjITEM_EDITFLOAT, "contactfriction", 2, &(vis->rgba.contactfriction), "4"},
|
||||
{mjITEM_EDITFLOAT, "contacttorque", 2, &(vis->rgba.contacttorque), "4"},
|
||||
{mjITEM_EDITFLOAT, "contactgap", 2, &(vis->rgba.contactgap), "4"},
|
||||
{mjITEM_EDITFLOAT, "rangefinder", 2, &(vis->rgba.rangefinder), "4"},
|
||||
{mjITEM_EDITFLOAT, "constraint", 2, &(vis->rgba.constraint), "4"},
|
||||
{mjITEM_EDITFLOAT, "slidercrank", 2, &(vis->rgba.slidercrank), "4"},
|
||||
{mjITEM_EDITFLOAT, "crankbroken", 2, &(vis->rgba.crankbroken), "4"},
|
||||
{mjITEM_EDITFLOAT, "frustum", 2, &(vis->rgba.frustum), "4"},
|
||||
{mjITEM_EDITFLOAT, "bv", 2, &(vis->rgba.bv), "4"},
|
||||
{mjITEM_EDITFLOAT, "bvactive", 2, &(vis->rgba.bvactive), "4"},
|
||||
{mjITEM_END}};
|
||||
|
||||
// add rendering standard
|
||||
// add visualization section
|
||||
mjui_add(&sim->ui0, defVisualization);
|
||||
}
|
||||
|
||||
// make group section of UI
|
||||
void MakeGroupSection(mj::Simulate *sim, int oldstate)
|
||||
void MakeGroupSection(mj::Simulate *sim)
|
||||
{
|
||||
mjuiDef defGroup[] = {
|
||||
{mjITEM_SECTION, "Group enable", oldstate, nullptr, "AG"},
|
||||
{mjITEM_SECTION, "Group enable", mjPRESERVE, nullptr, "AG"},
|
||||
{mjITEM_SEPARATOR, "Geom groups", 1},
|
||||
{mjITEM_CHECKBYTE, "Geom 0", 2, sim->opt.geomgroup, " 0"},
|
||||
{mjITEM_CHECKBYTE, "Geom 1", 2, sim->opt.geomgroup + 1, " 1"},
|
||||
|
@ -1047,10 +1067,10 @@ namespace
|
|||
}
|
||||
|
||||
// make joint section of UI
|
||||
void MakeJointSection(mj::Simulate *sim, int oldstate)
|
||||
void MakeJointSection(mj::Simulate *sim)
|
||||
{
|
||||
mjuiDef defJoint[] = {
|
||||
{mjITEM_SECTION, "Joint", oldstate, nullptr, "AJ"},
|
||||
{mjITEM_SECTION, "Joint", mjPRESERVE, nullptr, "AJ"},
|
||||
{mjITEM_END}};
|
||||
mjuiDef defSlider[] = {
|
||||
{mjITEM_SLIDERNUM, "", 2, nullptr, "0 1"},
|
||||
|
@ -1111,10 +1131,10 @@ namespace
|
|||
}
|
||||
|
||||
// make control section of UI
|
||||
void MakeControlSection(mj::Simulate *sim, int oldstate)
|
||||
void MakeControlSection(mj::Simulate *sim)
|
||||
{
|
||||
mjuiDef defControl[] = {
|
||||
{mjITEM_SECTION, "Control", oldstate, nullptr, "AC"},
|
||||
{mjITEM_SECTION, "Control", mjPRESERVE, nullptr, "AC"},
|
||||
{mjITEM_BUTTON, "Clear all", 2},
|
||||
{mjITEM_END}};
|
||||
mjuiDef defSlider[] = {
|
||||
|
@ -1180,39 +1200,17 @@ namespace
|
|||
// make model-dependent UI sections
|
||||
void MakeUiSections(mj::Simulate *sim, const mjModel *m, const mjData *d)
|
||||
{
|
||||
// get section open-close state, UI 0
|
||||
int oldstate0[NSECT0];
|
||||
for (int i = 0; i < NSECT0; i++)
|
||||
{
|
||||
oldstate0[i] = 0;
|
||||
if (sim->ui0.nsect > i)
|
||||
{
|
||||
oldstate0[i] = sim->ui0.sect[i].state;
|
||||
}
|
||||
}
|
||||
|
||||
// get section open-close state, UI 1
|
||||
int oldstate1[NSECT1];
|
||||
for (int i = 0; i < NSECT1; i++)
|
||||
{
|
||||
oldstate1[i] = 0;
|
||||
if (sim->ui1.nsect > i)
|
||||
{
|
||||
oldstate1[i] = sim->ui1.sect[i].state;
|
||||
}
|
||||
}
|
||||
|
||||
// clear model-dependent sections of UI
|
||||
sim->ui0.nsect = SECT_PHYSICS;
|
||||
sim->ui1.nsect = 0;
|
||||
|
||||
// make
|
||||
MakePhysicsSection(sim, oldstate0[SECT_PHYSICS]);
|
||||
MakeRenderingSection(sim, m, oldstate0[SECT_RENDERING]);
|
||||
MakeVisualizationSection(sim, m, oldstate0[SECT_VISUALIZATION]);
|
||||
MakeGroupSection(sim, oldstate0[SECT_GROUP]);
|
||||
MakeJointSection(sim, oldstate1[SECT_JOINT]);
|
||||
MakeControlSection(sim, oldstate1[SECT_CONTROL]);
|
||||
MakePhysicsSection(sim);
|
||||
MakeRenderingSection(sim, m);
|
||||
MakeVisualizationSection(sim, m);
|
||||
MakeGroupSection(sim);
|
||||
MakeJointSection(sim);
|
||||
MakeControlSection(sim);
|
||||
}
|
||||
|
||||
//---------------------------------- utility functions ---------------------------------------------
|
||||
|
@ -1245,7 +1243,9 @@ namespace
|
|||
// millisecond timer, for MuJoCo built-in profiler
|
||||
mjtNum Timer()
|
||||
{
|
||||
return Milliseconds(mj::Simulate::Clock::now().time_since_epoch()).count();
|
||||
static auto start = mj::Simulate::Clock::now();
|
||||
auto elapsed = Milliseconds(mj::Simulate::Clock::now() - start);
|
||||
return elapsed.count();
|
||||
}
|
||||
|
||||
// clear all times
|
||||
|
@ -1383,7 +1383,7 @@ namespace
|
|||
return sim->m_ || sim->is_passive_;
|
||||
|
||||
case 3: // require model and nkey
|
||||
return !sim->is_passive_ && sim->nkey_;
|
||||
return (sim->m_ || sim->is_passive_) && sim->nkey_;
|
||||
|
||||
case 4: // require model and paused
|
||||
return sim->m_ && !sim->run;
|
||||
|
@ -1424,10 +1424,24 @@ namespace
|
|||
rect[3].height = rect[0].height;
|
||||
}
|
||||
|
||||
// modify UI
|
||||
void UiModify(mjUI *ui, mjuiState *state, mjrContext *con)
|
||||
{
|
||||
mjui_resize(ui, con);
|
||||
mjr_addAux(ui->auxid, ui->width, ui->maxheight, ui->spacing.samples, con);
|
||||
|
||||
// remake aux buffer only if missing or different
|
||||
int id = ui->auxid;
|
||||
if (con->auxFBO[id] == 0 ||
|
||||
con->auxFBO_r[id] == 0 ||
|
||||
con->auxColor[id] == 0 ||
|
||||
con->auxColor_r[id] == 0 ||
|
||||
con->auxWidth[id] != ui->width ||
|
||||
con->auxHeight[id] != ui->maxheight ||
|
||||
con->auxSamples[id] != ui->spacing.samples)
|
||||
{
|
||||
mjr_addAux(id, ui->width, ui->maxheight, ui->spacing.samples, con);
|
||||
}
|
||||
|
||||
UiLayout(state);
|
||||
mjui_update(-1, -1, ui, state, con);
|
||||
}
|
||||
|
@ -1648,7 +1662,7 @@ namespace
|
|||
if (it->name[0] == 'J' && it->name[1] == 'o')
|
||||
{
|
||||
sim->ui1.nsect = SECT_JOINT;
|
||||
MakeJointSection(sim, sim->ui1.sect[SECT_JOINT].state);
|
||||
MakeJointSection(sim);
|
||||
sim->ui1.nsect = NSECT1;
|
||||
UiModify(&sim->ui1, state, &sim->platform_ui->mjr_context());
|
||||
}
|
||||
|
@ -1723,10 +1737,11 @@ namespace
|
|||
mjui0_update_section(sim, SECT_SIMULATION);
|
||||
}
|
||||
|
||||
// not in scrubber: step
|
||||
// not in scrubber: step, add to history buffer
|
||||
else
|
||||
{
|
||||
mj_step(sim->m_, sim->d_);
|
||||
sim->AddToHistory();
|
||||
}
|
||||
|
||||
UpdateProfiler(sim, sim->m_, sim->d_);
|
||||
|
@ -2027,6 +2042,10 @@ namespace mujoco
|
|||
{
|
||||
return;
|
||||
}
|
||||
if (this->exitrequest.load())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool update_profiler = this->profiler && (this->pause_update || this->run);
|
||||
bool update_sensor = this->sensor && (this->pause_update || this->run);
|
||||
|
@ -2108,7 +2127,7 @@ namespace mujoco
|
|||
X(impratio);
|
||||
X(tolerance);
|
||||
X(noslip_tolerance);
|
||||
X(mpr_tolerance);
|
||||
X(ccd_tolerance);
|
||||
X(gravity);
|
||||
X(wind);
|
||||
X(magnetic);
|
||||
|
@ -2124,7 +2143,7 @@ namespace mujoco
|
|||
X(solver);
|
||||
X(iterations);
|
||||
X(noslip_iterations);
|
||||
X(mpr_iterations);
|
||||
X(ccd_iterations);
|
||||
X(disableflags);
|
||||
X(enableflags);
|
||||
X(disableactuator);
|
||||
|
@ -2182,6 +2201,7 @@ namespace mujoco
|
|||
{
|
||||
mj_resetData(m_, d_);
|
||||
mj_forward(m_, d_);
|
||||
load_error[0] = '\0';
|
||||
update_profiler = true;
|
||||
update_sensor = true;
|
||||
scrub_index = 0;
|
||||
|
@ -2211,14 +2231,7 @@ namespace mujoco
|
|||
|
||||
if (pending_.load_key)
|
||||
{
|
||||
int i = this->key;
|
||||
d_->time = m_->key_time[i];
|
||||
mju_copy(d_->qpos, m_->key_qpos + i * m_->nq, m_->nq);
|
||||
mju_copy(d_->qvel, m_->key_qvel + i * m_->nv, m_->nv);
|
||||
mju_copy(d_->act, m_->key_act + i * m_->na, m_->na);
|
||||
mju_copy(d_->mocap_pos, m_->key_mpos + i * 3 * m_->nmocap, 3 * m_->nmocap);
|
||||
mju_copy(d_->mocap_quat, m_->key_mquat + i * 4 * m_->nmocap, 4 * m_->nmocap);
|
||||
mju_copy(d_->ctrl, m_->key_ctrl + i * m_->nu, m_->nu);
|
||||
mj_resetDataKeyframe(m_, d_, this->key);
|
||||
mj_forward(m_, d_);
|
||||
update_profiler = true;
|
||||
update_sensor = true;
|
||||
|
@ -2227,14 +2240,7 @@ namespace mujoco
|
|||
|
||||
if (pending_.save_key)
|
||||
{
|
||||
int i = this->key;
|
||||
m_->key_time[i] = d_->time;
|
||||
mju_copy(m_->key_qpos + i * m_->nq, d_->qpos, m_->nq);
|
||||
mju_copy(m_->key_qvel + i * m_->nv, d_->qvel, m_->nv);
|
||||
mju_copy(m_->key_act + i * m_->na, d_->act, m_->na);
|
||||
mju_copy(m_->key_mpos + i * 3 * m_->nmocap, d_->mocap_pos, 3 * m_->nmocap);
|
||||
mju_copy(m_->key_mquat + i * 4 * m_->nmocap, d_->mocap_quat, 4 * m_->nmocap);
|
||||
mju_copy(m_->key_ctrl + i * m_->nu, d_->ctrl, m_->nu);
|
||||
mj_setKeyframe(m_, d_, this->key);
|
||||
pending_.save_key = false;
|
||||
}
|
||||
|
||||
|
@ -2355,6 +2361,21 @@ namespace mujoco
|
|||
}
|
||||
}
|
||||
|
||||
// pick up rendering flags changed via user_scn
|
||||
if (user_scn)
|
||||
{
|
||||
for (int i = 0; i < mjNRNDFLAG; ++i)
|
||||
{
|
||||
if (user_scn->flags[i] != user_scn_flags_prev_[i])
|
||||
{
|
||||
scn.flags[i] = user_scn->flags[i];
|
||||
pending_.ui_update_rendering = true;
|
||||
}
|
||||
}
|
||||
Copy(user_scn->flags, scn.flags);
|
||||
Copy(user_scn_flags_prev_, user_scn->flags);
|
||||
}
|
||||
|
||||
mjopt_prev_ = scnstate_.model.opt;
|
||||
warn_vgeomfull_prev_ = scnstate_.data.warning[mjWARN_VGEOMFULL].number;
|
||||
}
|
||||
|
@ -2518,13 +2539,13 @@ namespace mujoco
|
|||
// allocate history buffer: smaller of {2000 states, 100 MB}
|
||||
if (!this->is_passive_)
|
||||
{
|
||||
constexpr int kHistoryLength = 2000;
|
||||
constexpr int kMaxHistoryBytes = 1e8;
|
||||
|
||||
// get state size, size of history buffer
|
||||
state_size_ = mj_stateSize(this->m_, mjSTATE_INTEGRATION);
|
||||
int state_bytes = state_size_ * sizeof(mjtNum);
|
||||
int history_bytes = mjMIN(state_bytes * kHistoryLength, kMaxHistoryBytes);
|
||||
int history_length = mjMIN(INT_MAX / state_bytes, 2000);
|
||||
int history_bytes = mjMIN(state_bytes * history_length, kMaxHistoryBytes);
|
||||
nhistory_ = history_bytes / state_bytes;
|
||||
|
||||
// allocate history buffer, reset cursor and UI slider
|
||||
|
@ -2562,6 +2583,12 @@ namespace mujoco
|
|||
this->scn.flags[mjRND_REFLECTION] = 0;
|
||||
}
|
||||
|
||||
if (this->user_scn)
|
||||
{
|
||||
Copy(this->user_scn->flags, this->scn.flags);
|
||||
Copy(this->user_scn_flags_prev_, this->scn.flags);
|
||||
}
|
||||
|
||||
// clear perturbation state
|
||||
this->pert.active = 0;
|
||||
this->pert.select = 0;
|
||||
|
@ -2773,7 +2800,7 @@ namespace mujoco
|
|||
if (this->ui1_enable && this->ui1.sect[SECT_CONTROL].state)
|
||||
{
|
||||
this->ui1.nsect = SECT_CONTROL;
|
||||
MakeControlSection(this, this->ui1.sect[SECT_CONTROL].state);
|
||||
MakeControlSection(this);
|
||||
this->ui1.nsect = NSECT1;
|
||||
UiModify(&this->ui1, &this->uistate, &this->platform_ui->mjr_context());
|
||||
}
|
||||
|
@ -2802,7 +2829,19 @@ namespace mujoco
|
|||
// show pause/loading label
|
||||
if (!this->run || this->loadrequest)
|
||||
{
|
||||
const char *label = this->loadrequest ? "LOADING..." : "PAUSE";
|
||||
char label[30] = {'\0'};
|
||||
if (this->loadrequest)
|
||||
{
|
||||
std::snprintf(label, sizeof(label), "LOADING...");
|
||||
}
|
||||
else if (this->scrub_index == 0)
|
||||
{
|
||||
std::snprintf(label, sizeof(label), "PAUSE");
|
||||
}
|
||||
else
|
||||
{
|
||||
std::snprintf(label, sizeof(label), "PAUSE (%d)", this->scrub_index);
|
||||
}
|
||||
mjr_overlay(mjFONT_BIG, mjGRID_TOP, smallrect, label, nullptr,
|
||||
&this->platform_ui->mjr_context());
|
||||
}
|
||||
|
@ -2900,10 +2939,14 @@ namespace mujoco
|
|||
// file dialog does not automatically open that location. Thus, we defer to a default
|
||||
// "screenshot.png" for now.
|
||||
// const std::string path = GetSavePath("screenshot.png");
|
||||
// if (!path.empty()) {
|
||||
// if (lodepng::encode(path, rgb.get(), w, h, LCT_RGB)) {
|
||||
// if (!path.empty())
|
||||
// {
|
||||
// if (lodepng::encode(path, rgb.get(), w, h, LCT_RGB))
|
||||
// {
|
||||
// mju_error("could not save screenshot");
|
||||
// } else {
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// std::printf("saved screenshot: %s\n", path.c_str());
|
||||
// }
|
||||
// }
|
||||
|
@ -2971,12 +3014,15 @@ namespace mujoco
|
|||
this->platform_ui->SetEventCallback(UiEvent);
|
||||
this->platform_ui->SetLayoutCallback(UiLayout);
|
||||
|
||||
// populate uis with standard sections
|
||||
// populate uis with standard sections, open some sections initially
|
||||
this->ui0.userdata = this;
|
||||
this->ui1.userdata = this;
|
||||
mjui_add(&this->ui0, defFile);
|
||||
mjui_add(&this->ui0, this->def_option);
|
||||
mjui_add(&this->ui0, this->def_simulation);
|
||||
this->ui0.sect[0].state = 1;
|
||||
this->ui0.sect[1].state = 1;
|
||||
this->ui0.sect[2].state = 1;
|
||||
mjui_add(&this->ui0, this->def_watch);
|
||||
UiModify(&this->ui0, &this->uistate, &this->platform_ui->mjr_context());
|
||||
UiModify(&this->ui1, &this->uistate, &this->platform_ui->mjr_context());
|
||||
|
@ -3077,11 +3123,9 @@ namespace mujoco
|
|||
}
|
||||
}
|
||||
|
||||
if (!is_passive_)
|
||||
{
|
||||
const MutexLock lock(this->mtx);
|
||||
mjv_freeScene(&this->scn);
|
||||
}
|
||||
else
|
||||
if (is_passive_)
|
||||
{
|
||||
mjv_freeSceneState(&scnstate_);
|
||||
}
|
||||
|
@ -3105,6 +3149,44 @@ namespace mujoco
|
|||
mj_getState(m_, d_, state, mjSTATE_INTEGRATION);
|
||||
}
|
||||
|
||||
// inject Brownian noise
|
||||
void Simulate::InjectNoise()
|
||||
{
|
||||
// no noise, return
|
||||
if (ctrl_noise_std <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// convert rate and scale to discrete time (Ornstein–Uhlenbeck)
|
||||
mjtNum rate = mju_exp(-m_->opt.timestep / ctrl_noise_rate);
|
||||
mjtNum scale = ctrl_noise_std * mju_sqrt(1 - rate * rate);
|
||||
|
||||
for (int i = 0; i < m_->nu; i++)
|
||||
{
|
||||
mjtNum bottom = 0, top = 0, midpoint = 0, halfrange = 1;
|
||||
if (m_->actuator_ctrllimited[i])
|
||||
{
|
||||
bottom = m_->actuator_ctrlrange[2 * i];
|
||||
top = m_->actuator_ctrlrange[2 * i + 1];
|
||||
midpoint = 0.5 * (top + bottom); // target of exponential decay
|
||||
halfrange = 0.5 * (top - bottom); // scales noise
|
||||
}
|
||||
|
||||
// exponential convergence to midpoint at ctrl_noise_rate
|
||||
d_->ctrl[i] = rate * d_->ctrl[i] + (1 - rate) * midpoint;
|
||||
|
||||
// add noise
|
||||
d_->ctrl[i] += scale * halfrange * mju_standardNormal(nullptr);
|
||||
|
||||
// clip to range if limited
|
||||
if (m_->actuator_ctrllimited[i])
|
||||
{
|
||||
d_->ctrl[i] = mju_clip(d_->ctrl[i], bottom, top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Simulate::UpdateHField(int hfieldid)
|
||||
{
|
||||
MutexLock lock(this->mtx);
|
||||
|
@ -3140,7 +3222,6 @@ namespace mujoco
|
|||
cond_upload_.wait(lock, [this]()
|
||||
{ return texture_upload_ == -1; });
|
||||
}
|
||||
|
||||
ElasticBand::ElasticBand() {};
|
||||
ElasticBand::~ElasticBand() {};
|
||||
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <mujoco/mjui.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
|
@ -48,7 +46,6 @@ namespace mujoco
|
|||
bool enable_ = true;
|
||||
std::vector<double> f_ = {0, 0, 0};
|
||||
};
|
||||
|
||||
// The viewer itself doesn't require a reentrant mutex, however we use it in
|
||||
// order to provide a Python sync API that doesn't require separate locking
|
||||
// (since sync is by far the most common operation), but that also won't
|
||||
|
@ -106,6 +103,9 @@ namespace mujoco
|
|||
// add state to history buffer
|
||||
void AddToHistory();
|
||||
|
||||
// inject control noise
|
||||
void InjectNoise();
|
||||
|
||||
// constants
|
||||
static constexpr int kMaxFilenameLength = 1000;
|
||||
|
||||
|
@ -150,6 +150,7 @@ namespace mujoco
|
|||
mjOption mjopt_prev_;
|
||||
mjvOption opt_prev_;
|
||||
mjvCamera cam_prev_;
|
||||
|
||||
int warn_vgeomfull_prev_;
|
||||
|
||||
// pending GUI-driven actions, to be applied at the next call to Sync
|
||||
|
@ -267,6 +268,7 @@ namespace mujoco
|
|||
|
||||
// additional user-defined visualization geoms (used in passive mode)
|
||||
mjvScene *user_scn = nullptr;
|
||||
mjtByte user_scn_flags_prev_[mjNRNDFLAG];
|
||||
|
||||
// OpenGL rendering and UI
|
||||
int refresh_rate = 60;
|
||||
|
@ -280,7 +282,7 @@ namespace mujoco
|
|||
// Constant arrays needed for the option section of UI and the UI interface
|
||||
// TODO setting the size here is not ideal
|
||||
const mjuiDef def_option[13] = {
|
||||
{mjITEM_SECTION, "Option", 1, nullptr, "AO"},
|
||||
{mjITEM_SECTION, "Option", mjPRESERVE, nullptr, "AO"},
|
||||
{mjITEM_CHECKINT, "Help", 2, &this->help, " #290"},
|
||||
{mjITEM_CHECKINT, "Info", 2, &this->info, " #291"},
|
||||
{mjITEM_CHECKINT, "Profiler", 2, &this->profiler, " #292"},
|
||||
|
@ -300,7 +302,7 @@ namespace mujoco
|
|||
|
||||
// simulation section of UI
|
||||
const mjuiDef def_simulation[14] = {
|
||||
{mjITEM_SECTION, "Simulation", 1, nullptr, "AS"},
|
||||
{mjITEM_SECTION, "Simulation", mjPRESERVE, nullptr, "AS"},
|
||||
{mjITEM_RADIO, "", 5, &this->run, "Pause\nRun"},
|
||||
{mjITEM_BUTTON, "Reset", 2, nullptr, " #259"},
|
||||
{mjITEM_BUTTON, "Reload", 5, nullptr, "CL"},
|
||||
|
@ -309,15 +311,15 @@ namespace mujoco
|
|||
{mjITEM_SLIDERINT, "Key", 3, &this->key, "0 0"},
|
||||
{mjITEM_BUTTON, "Load key", 3},
|
||||
{mjITEM_BUTTON, "Save key", 3},
|
||||
{mjITEM_SLIDERNUM, "Noise scale", 5, &this->ctrl_noise_std, "0 2"},
|
||||
{mjITEM_SLIDERNUM, "Noise rate", 5, &this->ctrl_noise_rate, "0 2"},
|
||||
{mjITEM_SLIDERNUM, "Noise scale", 5, &this->ctrl_noise_std, "0 1"},
|
||||
{mjITEM_SLIDERNUM, "Noise rate", 5, &this->ctrl_noise_rate, "0 4"},
|
||||
{mjITEM_SEPARATOR, "History", 1},
|
||||
{mjITEM_SLIDERINT, "", 5, &this->scrub_index, "0 0"},
|
||||
{mjITEM_END}};
|
||||
|
||||
// watch section of UI
|
||||
const mjuiDef def_watch[5] = {
|
||||
{mjITEM_SECTION, "Watch", 0, nullptr, "AW"},
|
||||
{mjITEM_SECTION, "Watch", mjPRESERVE, nullptr, "AW"},
|
||||
{mjITEM_EDITTXT, "Field", 2, this->field, "qpos"},
|
||||
{mjITEM_EDITINT, "Index", 2, &this->index, "1"},
|
||||
{mjITEM_STATIC, "Value", 2, nullptr, " "},
|
||||
|
@ -336,7 +338,6 @@ namespace mujoco
|
|||
ElasticBand elastic_band_;
|
||||
int use_elastic_band_ = 0;
|
||||
};
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue