Merge branch 'google-deepmind:main' into unity/passive-resubmit
This commit is contained in:
@@ -222,7 +222,7 @@ package_wasm() {
|
|||||||
cp wasm/package.npm.json wasm/dist/package.json
|
cp wasm/package.npm.json wasm/dist/package.json
|
||||||
cp wasm/README.md wasm/dist/README.md
|
cp wasm/README.md wasm/dist/README.md
|
||||||
|
|
||||||
VERSION=${GITHUB_REF#refs/tags/}
|
VERSION="${VERSION:-${GITHUB_REF#refs/tags/}}"
|
||||||
npm --prefix wasm/dist version "${VERSION}" --no-git-tag-version
|
npm --prefix wasm/dist version "${VERSION}" --no-git-tag-version
|
||||||
|
|
||||||
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > ~/.npmrc
|
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > ~/.npmrc
|
||||||
|
|||||||
@@ -5,10 +5,25 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '[0-9]*.[0-9]*.[0-9]*'
|
- '[0-9]*.[0-9]*.[0-9]*'
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag_version:
|
||||||
|
description: 'Release tag version to deploy'
|
||||||
|
required: true
|
||||||
|
default: '0.0.0'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
name: Publish MuJoCo WASM package
|
name: Publish MuJoCo WASM package
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
|
env:
|
||||||
|
VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.tag_version || github.ref_name }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
@@ -32,5 +47,5 @@ jobs:
|
|||||||
- name: Package WASM bindings
|
- name: Package WASM bindings
|
||||||
env:
|
env:
|
||||||
NPM_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
NPM_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
|
||||||
GITHUB_REF: ${{ github.ref }}
|
VERSION: ${{ env.VERSION }}
|
||||||
run: bash ./.github/workflows/build_steps.sh package_wasm
|
run: bash ./.github/workflows/build_steps.sh package_wasm
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON)
|
|||||||
|
|
||||||
project(
|
project(
|
||||||
mujoco
|
mujoco
|
||||||
VERSION 3.6.0
|
VERSION 3.7.0
|
||||||
DESCRIPTION "MuJoCo Physics Simulator"
|
DESCRIPTION "MuJoCo Physics Simulator"
|
||||||
HOMEPAGE_URL "https://mujoco.org"
|
HOMEPAGE_URL "https://mujoco.org"
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 3,6,0,0
|
FILEVERSION 3,7,0,0
|
||||||
PRODUCTVERSION 3,6,0,0
|
PRODUCTVERSION 3,7,0,0
|
||||||
FILEOS 0x4
|
FILEOS 0x4
|
||||||
FILETYPE 0x1
|
FILETYPE 0x1
|
||||||
{
|
{
|
||||||
@@ -9,9 +9,9 @@ FILETYPE 0x1
|
|||||||
BLOCK "040904b0"
|
BLOCK "040904b0"
|
||||||
{
|
{
|
||||||
VALUE "ProductName", "MuJoCo"
|
VALUE "ProductName", "MuJoCo"
|
||||||
VALUE "ProductVersion", "3.6.0"
|
VALUE "ProductVersion", "3.7.0"
|
||||||
VALUE "FileDescription", "MuJoCo"
|
VALUE "FileDescription", "MuJoCo"
|
||||||
VALUE "FileVersion", "3.6.0"
|
VALUE "FileVersion", "3.7.0"
|
||||||
VALUE "InternalName", "mujoco.dll"
|
VALUE "InternalName", "mujoco.dll"
|
||||||
VALUE "OriginalFilename", "mujoco.dll"
|
VALUE "OriginalFilename", "mujoco.dll"
|
||||||
VALUE "CompanyName", "Google DeepMind"
|
VALUE "CompanyName", "Google DeepMind"
|
||||||
|
|||||||
Vendored
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
MUJOCO ICON "mujoco.ico"
|
MUJOCO ICON "mujoco.ico"
|
||||||
|
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 3,6,0,0
|
FILEVERSION 3,7,0,0
|
||||||
PRODUCTVERSION 3,6,0,0
|
PRODUCTVERSION 3,7,0,0
|
||||||
FILEOS 0x4
|
FILEOS 0x4
|
||||||
FILETYPE 0x1
|
FILETYPE 0x1
|
||||||
{
|
{
|
||||||
@@ -11,9 +11,9 @@ FILETYPE 0x1
|
|||||||
BLOCK "040904b0"
|
BLOCK "040904b0"
|
||||||
{
|
{
|
||||||
VALUE "ProductName", "MuJoCo"
|
VALUE "ProductName", "MuJoCo"
|
||||||
VALUE "ProductVersion", "3.6.0"
|
VALUE "ProductVersion", "3.7.0"
|
||||||
VALUE "FileDescription", "MuJoCo"
|
VALUE "FileDescription", "MuJoCo"
|
||||||
VALUE "FileVersion", "3.6.0"
|
VALUE "FileVersion", "3.7.0"
|
||||||
VALUE "InternalName", "simulate.exe"
|
VALUE "InternalName", "simulate.exe"
|
||||||
VALUE "OriginalFilename", "simulate.exe"
|
VALUE "OriginalFilename", "simulate.exe"
|
||||||
VALUE "CompanyName", "Google DeepMind"
|
VALUE "CompanyName", "Google DeepMind"
|
||||||
|
|||||||
@@ -388,7 +388,7 @@ Defined in `mujoco.h <https://github.com/google-deepmind/mujoco/blob/main/includ
|
|||||||
- value
|
- value
|
||||||
- description
|
- description
|
||||||
* - ``mjVERSION_HEADER``
|
* - ``mjVERSION_HEADER``
|
||||||
- 3006000
|
- 3007000
|
||||||
- The version of the MuJoCo headers. This is an integer calculated from the version string "S.M.P"
|
- The version of the MuJoCo headers. This is an integer calculated from the version string "S.M.P"
|
||||||
using the formula ``(S * 1e6) + (M * 1e3) + P``. For example, version 4.2.1 is represented as 4002001.
|
using the formula ``(S * 1e6) + (M * 1e3) + P``. For example, version 4.2.1 is represented as 4002001.
|
||||||
The API function :ref:`mj_version` returns a number with the same meaning
|
The API function :ref:`mj_version` returns a number with the same meaning
|
||||||
|
|||||||
@@ -3293,8 +3293,8 @@ void mj_objectVelocity(const mjModel* m, const mjData* d,
|
|||||||
int objtype, int objid, mjtNum res[6], int flg_local);
|
int objtype, int objid, mjtNum res[6], int flg_local);
|
||||||
void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
||||||
int objtype, int objid, mjtNum res[6], int flg_local);
|
int objtype, int objid, mjtNum res[6], int flg_local);
|
||||||
mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2,
|
mjtNum mj_geomDistance(const mjModel* m, mjData* d, int geom1, int geom2, mjtNum distmax,
|
||||||
mjtNum distmax, mjtNum fromto[6]);
|
mjtNum fromto[6]);
|
||||||
void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
||||||
void mj_differentiatePos(const mjModel* m, mjtNum* qvel, mjtNum dt,
|
void mj_differentiatePos(const mjModel* m, mjtNum* qvel, mjtNum dt,
|
||||||
const mjtNum* qpos1, const mjtNum* qpos2);
|
const mjtNum* qpos1, const mjtNum* qpos2);
|
||||||
|
|||||||
@@ -216,6 +216,8 @@ Benchmark an environment with _`testspeed`
|
|||||||
|
|
||||||
mjwarp-testspeed benchmark/humanoid/humanoid.xml
|
mjwarp-testspeed benchmark/humanoid/humanoid.xml
|
||||||
|
|
||||||
|
.. _mjwViewer:
|
||||||
|
|
||||||
Interactive environment simulation with MJWarp
|
Interactive environment simulation with MJWarp
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
@@ -489,7 +491,7 @@ Key features:
|
|||||||
`Warp's BVHs <https://nvidia.github.io/warp/api_reference/_generated/warp.Bvh.html#warp.Bvh>`__.
|
`Warp's BVHs <https://nvidia.github.io/warp/api_reference/_generated/warp.Bvh.html#warp.Bvh>`__.
|
||||||
|
|
||||||
Basic Usage
|
Basic Usage
|
||||||
----------
|
-----------
|
||||||
|
|
||||||
Rendering or raycasting requires a :class:`mjw.RenderContext <mujoco_warp.RenderContext>` which contains BVH structures,
|
Rendering or raycasting requires a :class:`mjw.RenderContext <mujoco_warp.RenderContext>` which contains BVH structures,
|
||||||
rendering specific fields, and output buffers.
|
rendering specific fields, and output buffers.
|
||||||
|
|||||||
+1
-1
@@ -119,7 +119,7 @@ Notice that we pass two extra arguments to ``mjx.make_data``:
|
|||||||
|
|
||||||
* ``naconmax`` defines the maximum number of contacts for all worlds combined.
|
* ``naconmax`` defines the maximum number of contacts for all worlds combined.
|
||||||
* ``njmax`` defines the maximum number of constraints per world. If you are developing a new scene, these parameters
|
* ``njmax`` defines the maximum number of constraints per world. If you are developing a new scene, these parameters
|
||||||
should be tuned by loading them in the :ref:`viewer <MJW_Cli>` and increasing the values accordingly as overflows
|
should be tuned by loading them in the :ref:`viewer <mjwViewer>` and increasing the values accordingly as overflows
|
||||||
occur. Scale ``naconmax`` by the number of environments you'll eventually need in a ``jax.vmap``!
|
occur. Scale ``naconmax`` by the number of environments you'll eventually need in a ``jax.vmap``!
|
||||||
|
|
||||||
Contacts
|
Contacts
|
||||||
|
|||||||
+2
-2
@@ -37,14 +37,14 @@ _____
|
|||||||
|
|
||||||
The MuJoCo app needs to be run at least once before the native library can be used, in order to register the library as
|
The MuJoCo app needs to be run at least once before the native library can be used, in order to register the library as
|
||||||
a trusted binary. Then, copy the dynamic library file from
|
a trusted binary. Then, copy the dynamic library file from
|
||||||
``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.6.0.dylib`` (it can be
|
``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.7.0.dylib`` (it can be
|
||||||
found by browsing the contents of ``MuJoCo.app``) and rename it as ``mujoco.dylib``.
|
found by browsing the contents of ``MuJoCo.app``) and rename it as ``mujoco.dylib``.
|
||||||
|
|
||||||
Linux
|
Linux
|
||||||
_____
|
_____
|
||||||
|
|
||||||
Expand the ``tar.gz`` archive to ``~/.mujoco``. Then copy the dynamic library from
|
Expand the ``tar.gz`` archive to ``~/.mujoco``. Then copy the dynamic library from
|
||||||
``~/.mujoco/mujoco-3.6.0/lib/libmujoco.so.3.6.0`` and rename it as ``libmujoco.so``.
|
``~/.mujoco/mujoco-3.7.0/lib/libmujoco.so.3.7.0`` and rename it as ``libmujoco.so``.
|
||||||
|
|
||||||
Windows
|
Windows
|
||||||
_______
|
_______
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#define MUJOCO_MUJOCO_H_
|
#define MUJOCO_MUJOCO_H_
|
||||||
|
|
||||||
// header version; should match the library version as returned by mj_version()
|
// header version; should match the library version as returned by mj_version()
|
||||||
#define mjVERSION_HEADER 3006000
|
#define mjVERSION_HEADER 3007000
|
||||||
|
|
||||||
// needed to define size_t, fabs and log10
|
// needed to define size_t, fabs and log10
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -609,8 +609,8 @@ MJAPI void mj_objectAcceleration(const mjModel* m, const mjData* d,
|
|||||||
|
|
||||||
// Return smallest signed distance between two geoms and optionally segment from geom1 to geom2.
|
// Return smallest signed distance between two geoms and optionally segment from geom1 to geom2.
|
||||||
// Nullable: fromto
|
// Nullable: fromto
|
||||||
MJAPI mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2,
|
MJAPI mjtNum mj_geomDistance(const mjModel* m, mjData* d, int geom1, int geom2, mjtNum distmax,
|
||||||
mjtNum distmax, mjtNum fromto[6]);
|
mjtNum fromto[6]);
|
||||||
|
|
||||||
// Extract 6D force:torque given contact id, in the contact frame.
|
// Extract 6D force:torque given contact id, in the contact frame.
|
||||||
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
MJAPI void mj_contactForce(const mjModel* m, const mjData* d, int id, mjtNum result[6]);
|
||||||
|
|||||||
+4
-4
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name="mujoco-mjx"
|
name="mujoco-mjx"
|
||||||
version = "3.6.0"
|
version = "3.7.0"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Google DeepMind", email = "mujoco@deepmind.com"},
|
{name = "Google DeepMind", email = "mujoco@deepmind.com"},
|
||||||
]
|
]
|
||||||
@@ -29,7 +29,7 @@ dependencies = [
|
|||||||
"etils[epath]",
|
"etils[epath]",
|
||||||
"jax",
|
"jax",
|
||||||
"jaxlib",
|
"jaxlib",
|
||||||
"mujoco>=3.6.0.dev0",
|
"mujoco>=3.7.0.dev0",
|
||||||
"scipy",
|
"scipy",
|
||||||
"trimesh",
|
"trimesh",
|
||||||
]
|
]
|
||||||
@@ -45,9 +45,9 @@ mjx-viewer = "mujoco.mjx.viewer:main"
|
|||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://github.com/google-deepmind/mujoco/tree/main/mjx"
|
Homepage = "https://github.com/google-deepmind/mujoco/tree/main/mjx"
|
||||||
Documentation = "https://mujoco.readthedocs.io/en/3.6.0"
|
Documentation = "https://mujoco.readthedocs.io/en/3.7.0"
|
||||||
Repository = "https://github.com/google-deepmind/mujoco/tree/main/mjx"
|
Repository = "https://github.com/google-deepmind/mujoco/tree/main/mjx"
|
||||||
Changelog = "https://mujoco.readthedocs.io/en/3.6.0/changelog.html"
|
Changelog = "https://mujoco.readthedocs.io/en/3.7.0/changelog.html"
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
force_single_line = true
|
force_single_line = true
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ if(NOT TARGET mujoco)
|
|||||||
if(MUJOCO_FRAMEWORK)
|
if(MUJOCO_FRAMEWORK)
|
||||||
message("MuJoCo framework is at ${MUJOCO_FRAMEWORK}/mujoco.framework")
|
message("MuJoCo framework is at ${MUJOCO_FRAMEWORK}/mujoco.framework")
|
||||||
set(MUJOCO_LIBRARY
|
set(MUJOCO_LIBRARY
|
||||||
${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.6.0.dylib
|
${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.7.0.dylib
|
||||||
)
|
)
|
||||||
target_compile_options(mujoco INTERFACE -F${MUJOCO_FRAMEWORK})
|
target_compile_options(mujoco INTERFACE -F${MUJOCO_FRAMEWORK})
|
||||||
endif()
|
endif()
|
||||||
@@ -94,7 +94,7 @@ if(NOT TARGET mujoco)
|
|||||||
|
|
||||||
if(NOT MUJOCO_FRAMEWORK)
|
if(NOT MUJOCO_FRAMEWORK)
|
||||||
find_library(
|
find_library(
|
||||||
MUJOCO_LIBRARY mujoco mujoco.3.6.0 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED
|
MUJOCO_LIBRARY mujoco mujoco.3.7.0 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED
|
||||||
)
|
)
|
||||||
find_path(MUJOCO_INCLUDE mujoco/mujoco.h HINTS ${MUJOCO_INCLUDE_DIR} REQUIRED)
|
find_path(MUJOCO_INCLUDE mujoco/mujoco.h HINTS ${MUJOCO_INCLUDE_DIR} REQUIRED)
|
||||||
message("MuJoCo is at ${MUJOCO_LIBRARY}")
|
message("MuJoCo is at ${MUJOCO_LIBRARY}")
|
||||||
|
|||||||
@@ -663,7 +663,7 @@ PYBIND11_MODULE(_functions, pymodule) {
|
|||||||
Def<traits::mj_contactForce>(pymodule);
|
Def<traits::mj_contactForce>(pymodule);
|
||||||
Def<traits::mj_geomDistance>(
|
Def<traits::mj_geomDistance>(
|
||||||
pymodule,
|
pymodule,
|
||||||
[](const raw::MjModel* m, const raw::MjData* d,
|
[](const raw::MjModel* m, raw::MjData* d,
|
||||||
int geom1, int geom2, mjtNum distmax,
|
int geom1, int geom2, mjtNum distmax,
|
||||||
std::optional<Eigen::Ref<EigenArrayXX>> fromto) {
|
std::optional<Eigen::Ref<EigenArrayXX>> fromto) {
|
||||||
if (fromto.has_value() && fromto->size() != 6) {
|
if (fromto.has_value() && fromto->size() != 6) {
|
||||||
|
|||||||
@@ -3626,7 +3626,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
|
|||||||
FunctionParameterDecl(
|
FunctionParameterDecl(
|
||||||
name='d',
|
name='d',
|
||||||
type=PointerType(
|
type=PointerType(
|
||||||
inner_type=ValueType(name='mjData', is_const=True),
|
inner_type=ValueType(name='mjData'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FunctionParameterDecl(
|
FunctionParameterDecl(
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>org.mujoco.mjpython</string>
|
<string>org.mujoco.mjpython</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>3.6.0</string>
|
<string>3.7.0</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>3.6.0</string>
|
<string>3.7.0</string>
|
||||||
<key>CFBundleLongVersionString</key>
|
<key>CFBundleLongVersionString</key>
|
||||||
<string>3.6.0</string>
|
<string>3.7.0</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>3.6.0</string>
|
<string>3.7.0</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>mjpython</string>
|
<string>mjpython</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "mujoco"
|
name = "mujoco"
|
||||||
version = "3.6.0"
|
version = "3.7.0"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Google DeepMind", email = "mujoco@deepmind.com"},
|
{name = "Google DeepMind", email = "mujoco@deepmind.com"},
|
||||||
]
|
]
|
||||||
@@ -34,9 +34,9 @@ dynamic = ["readme", "scripts"]
|
|||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://github.com/google-deepmind/mujoco"
|
Homepage = "https://github.com/google-deepmind/mujoco"
|
||||||
Documentation = "https://mujoco.readthedocs.io/en/3.6.0"
|
Documentation = "https://mujoco.readthedocs.io/en/3.7.0"
|
||||||
Repository = "https://github.com/google-deepmind/mujoco"
|
Repository = "https://github.com/google-deepmind/mujoco"
|
||||||
Changelog = "https://mujoco.readthedocs.io/en/3.6.0/changelog.html"
|
Changelog = "https://mujoco.readthedocs.io/en/3.7.0/changelog.html"
|
||||||
|
|
||||||
[tool.setuptools]
|
[tool.setuptools]
|
||||||
include-package-data = false
|
include-package-data = false
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON)
|
|||||||
|
|
||||||
project(
|
project(
|
||||||
mujoco_samples
|
mujoco_samples
|
||||||
VERSION 3.6.0
|
VERSION 3.7.0
|
||||||
DESCRIPTION "MuJoCo samples binaries"
|
DESCRIPTION "MuJoCo samples binaries"
|
||||||
HOMEPAGE_URL "https://mujoco.org"
|
HOMEPAGE_URL "https://mujoco.org"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ set(MUJOCO_DEP_VERSION_lodepng
|
|||||||
|
|
||||||
project(
|
project(
|
||||||
mujoco_simulate
|
mujoco_simulate
|
||||||
VERSION 3.6.0
|
VERSION 3.7.0
|
||||||
DESCRIPTION "MuJoCo simulate binaries"
|
DESCRIPTION "MuJoCo simulate binaries"
|
||||||
HOMEPAGE_URL "https://mujoco.org"
|
HOMEPAGE_URL "https://mujoco.org"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -43,8 +43,8 @@
|
|||||||
|
|
||||||
//-------------------------- Constants -------------------------------------------------------------
|
//-------------------------- Constants -------------------------------------------------------------
|
||||||
|
|
||||||
#define mjVERSION 3006000
|
#define mjVERSION 3007000
|
||||||
#define mjVERSIONSTRING "3.6.0"
|
#define mjVERSIONSTRING "3.7.0"
|
||||||
|
|
||||||
// names of disable flags
|
// names of disable flags
|
||||||
const char* mjDISABLESTRING[mjNDISABLE] = {
|
const char* mjDISABLESTRING[mjNDISABLE] = {
|
||||||
@@ -524,7 +524,7 @@ void mj_xfrcAccumulate(const mjModel* m, mjData* d, mjtNum* qfrc) {
|
|||||||
//-------------------------- miscellaneous ---------------------------------------------------------
|
//-------------------------- miscellaneous ---------------------------------------------------------
|
||||||
|
|
||||||
// returns the smallest distance between two geoms (using nativeccd)
|
// returns the smallest distance between two geoms (using nativeccd)
|
||||||
static mjtNum mj_geomDistanceCCD(const mjModel* m, const mjData* d, int g1, int g2,
|
static mjtNum mj_geomDistanceCCD(const mjModel* m, mjData* d, int g1, int g2,
|
||||||
mjtNum distmax, mjtNum fromto[6]) {
|
mjtNum distmax, mjtNum fromto[6]) {
|
||||||
mjCCDConfig config;
|
mjCCDConfig config;
|
||||||
mjCCDStatus status;
|
mjCCDStatus status;
|
||||||
@@ -553,7 +553,7 @@ static mjtNum mj_geomDistanceCCD(const mjModel* m, const mjData* d, int g1, int
|
|||||||
|
|
||||||
|
|
||||||
// returns the smallest distance between two geoms
|
// returns the smallest distance between two geoms
|
||||||
mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2, mjtNum distmax,
|
mjtNum mj_geomDistance(const mjModel* m, mjData* d, int geom1, int geom2, mjtNum distmax,
|
||||||
mjtNum fromto[6]) {
|
mjtNum fromto[6]) {
|
||||||
mjContact con[mjMAXCONPAIR];
|
mjContact con[mjMAXCONPAIR];
|
||||||
mjtNum dist = distmax;
|
mjtNum dist = distmax;
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ void mj_xfrcAccumulate(const mjModel* m, mjData* d, mjtNum* qfrc);
|
|||||||
//-------------------------- miscellaneous ---------------------------------------------------------
|
//-------------------------- miscellaneous ---------------------------------------------------------
|
||||||
|
|
||||||
// returns the smallest distance between two geoms
|
// returns the smallest distance between two geoms
|
||||||
MJAPI mjtNum mj_geomDistance(const mjModel* m, const mjData* d, int geom1, int geom2,
|
MJAPI mjtNum mj_geomDistance(const mjModel* m, mjData* d, int geom1, int geom2, mjtNum distmax,
|
||||||
mjtNum distmax, mjtNum fromto[6]);
|
mjtNum fromto[6]);
|
||||||
|
|
||||||
// compute velocity by finite-differencing two positions
|
// compute velocity by finite-differencing two positions
|
||||||
MJAPI void mj_differentiatePos(const mjModel* m, mjtNum* qvel, mjtNum dt,
|
MJAPI void mj_differentiatePos(const mjModel* m, mjtNum* qvel, mjtNum dt,
|
||||||
|
|||||||
@@ -140,6 +140,19 @@ void FilamentContext::Render(const mjrRect& viewport, const mjvScene* scene,
|
|||||||
mju_error("Unexpected context.");
|
mju_error("Unexpected context.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're rendering to the window, and the window size has changed, we need
|
||||||
|
// to reacquire the swap chain.
|
||||||
|
if (scene_swap_chain_target_ == kWindowSwapChain &&
|
||||||
|
(viewport.width != window_width_ || viewport.height != window_height_)) {
|
||||||
|
if (window_width_ != 0 && window_height_ != 0) {
|
||||||
|
engine_->flushAndWait();
|
||||||
|
engine_->destroy(window_swap_chain_);
|
||||||
|
window_swap_chain_ = engine_->createSwapChain(config_.native_window);
|
||||||
|
}
|
||||||
|
window_width_ = viewport.width;
|
||||||
|
window_height_ = viewport.height;
|
||||||
|
}
|
||||||
|
|
||||||
scene_view_->SetViewport(viewport);
|
scene_view_->SetViewport(viewport);
|
||||||
scene_view_->UpdateScene(con, scene);
|
scene_view_->UpdateScene(con, scene);
|
||||||
// Update the UX renderable entity after processing the scene in case there
|
// Update the UX renderable entity after processing the scene in case there
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ class FilamentContext {
|
|||||||
std::unique_ptr<ObjectManager> object_manager_;
|
std::unique_ptr<ObjectManager> object_manager_;
|
||||||
std::unique_ptr<SceneView> scene_view_;
|
std::unique_ptr<SceneView> scene_view_;
|
||||||
std::unique_ptr<GuiView> gui_view_;
|
std::unique_ptr<GuiView> gui_view_;
|
||||||
|
int window_width_ = 0;
|
||||||
|
int window_height_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ class ZipArchiveProvider : public mjpResourceProvider {
|
|||||||
|
|
||||||
// Look for the root XML model in the archive. First look for an XML file
|
// Look for the root XML model in the archive. First look for an XML file
|
||||||
// with the same name as the archive itself. Failing that, look for an XML
|
// with the same name as the archive itself. Failing that, look for an XML
|
||||||
// file with the same name including the parent directory.
|
// file within a subdirectory with the same name as the archive.
|
||||||
const std::filesystem::path path(name_);
|
const std::filesystem::path path(name_);
|
||||||
root_model_ = (path / path.stem()).string() + ".xml";
|
root_model_ = (path / path.stem()).string() + ".xml";
|
||||||
if (!Contains(root_model_)) {
|
if (!Contains(root_model_)) {
|
||||||
root_model_ = (path / path.parent_path() / path.stem()).string() + ".xml";
|
root_model_ = (path / path.stem() / path.stem()).string() + ".xml";
|
||||||
if (!Contains(root_model_)) {
|
if (!Contains(root_model_)) {
|
||||||
SetError(error, error_sz, "Zip error: no root XML file found.");
|
SetError(error, error_sz, "Zip error: no root XML file found.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
+342
-166
@@ -26,6 +26,7 @@
|
|||||||
#include <implot.h>
|
#include <implot.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "experimental/platform/helpers.h"
|
#include "experimental/platform/helpers.h"
|
||||||
|
#include "experimental/platform/interaction.h"
|
||||||
#include "experimental/platform/imgui_widgets.h"
|
#include "experimental/platform/imgui_widgets.h"
|
||||||
|
|
||||||
namespace mujoco::platform {
|
namespace mujoco::platform {
|
||||||
@@ -41,70 +42,70 @@ void SetupTheme(GuiTheme theme) {
|
|||||||
ImVec4* c = s.Colors;
|
ImVec4* c = s.Colors;
|
||||||
if (theme == GuiTheme::kDark) {
|
if (theme == GuiTheme::kDark) {
|
||||||
ImGui::StyleColorsDark(&s);
|
ImGui::StyleColorsDark(&s);
|
||||||
c[ImGuiCol_Text] = ImVec4(1.00, 1.00, 1.00, 1.00);
|
c[ImGuiCol_Text] = ImVec4(1.00, 1.00, 1.00, 1.00);
|
||||||
c[ImGuiCol_TextDisabled] = ImVec4(0.40, 0.40, 0.40, 1.00);
|
c[ImGuiCol_TextDisabled] = ImVec4(0.40, 0.40, 0.40, 1.00);
|
||||||
c[ImGuiCol_ChildBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
c[ImGuiCol_ChildBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
||||||
c[ImGuiCol_WindowBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
c[ImGuiCol_WindowBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
||||||
c[ImGuiCol_PopupBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
c[ImGuiCol_PopupBg] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
||||||
c[ImGuiCol_Border] = ImVec4(0.12, 0.12, 0.12, 0.71);
|
c[ImGuiCol_Border] = ImVec4(0.12, 0.12, 0.12, 0.71);
|
||||||
c[ImGuiCol_BorderShadow] = ImVec4(1.00, 1.00, 1.00, 0.06);
|
c[ImGuiCol_BorderShadow] = ImVec4(1.00, 1.00, 1.00, 0.06);
|
||||||
c[ImGuiCol_FrameBg] = ImVec4(0.42, 0.42, 0.42, 0.54);
|
c[ImGuiCol_FrameBg] = ImVec4(0.42, 0.42, 0.42, 0.54);
|
||||||
c[ImGuiCol_FrameBgHovered] = ImVec4(0.42, 0.42, 0.42, 0.40);
|
c[ImGuiCol_FrameBgHovered] = ImVec4(0.42, 0.42, 0.42, 0.40);
|
||||||
c[ImGuiCol_FrameBgActive] = ImVec4(0.56, 0.56, 0.56, 0.67);
|
c[ImGuiCol_FrameBgActive] = ImVec4(0.56, 0.56, 0.56, 0.67);
|
||||||
c[ImGuiCol_TitleBg] = ImVec4(0.19, 0.19, 0.19, 1.00);
|
c[ImGuiCol_TitleBg] = ImVec4(0.19, 0.19, 0.19, 1.00);
|
||||||
c[ImGuiCol_TitleBgActive] = ImVec4(0.22, 0.22, 0.22, 1.00);
|
c[ImGuiCol_TitleBgActive] = ImVec4(0.22, 0.22, 0.22, 1.00);
|
||||||
c[ImGuiCol_TitleBgCollapsed] = ImVec4(0.17, 0.17, 0.17, 0.90);
|
c[ImGuiCol_TitleBgCollapsed] = ImVec4(0.17, 0.17, 0.17, 0.90);
|
||||||
c[ImGuiCol_MenuBarBg] = ImVec4(0.34, 0.34, 0.34, 1.00);
|
c[ImGuiCol_MenuBarBg] = ImVec4(0.34, 0.34, 0.34, 1.00);
|
||||||
c[ImGuiCol_ScrollbarBg] = ImVec4(0.24, 0.24, 0.24, 0.53);
|
c[ImGuiCol_ScrollbarBg] = ImVec4(0.24, 0.24, 0.24, 0.53);
|
||||||
c[ImGuiCol_ScrollbarGrab] = ImVec4(0.41, 0.41, 0.41, 1.00);
|
c[ImGuiCol_ScrollbarGrab] = ImVec4(0.41, 0.41, 0.41, 1.00);
|
||||||
c[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.52, 0.52, 0.52, 1.00);
|
c[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.52, 0.52, 0.52, 1.00);
|
||||||
c[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.76, 0.76, 0.76, 1.00);
|
c[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.76, 0.76, 0.76, 1.00);
|
||||||
c[ImGuiCol_CheckMark] = ImVec4(0.65, 0.65, 0.65, 1.00);
|
c[ImGuiCol_CheckMark] = ImVec4(0.65, 0.65, 0.65, 1.00);
|
||||||
c[ImGuiCol_SliderGrab] = ImVec4(0.52, 0.52, 0.52, 1.00);
|
c[ImGuiCol_SliderGrab] = ImVec4(0.52, 0.52, 0.52, 1.00);
|
||||||
c[ImGuiCol_SliderGrabActive] = ImVec4(0.64, 0.64, 0.64, 1.00);
|
c[ImGuiCol_SliderGrabActive] = ImVec4(0.64, 0.64, 0.64, 1.00);
|
||||||
c[ImGuiCol_Button] = ImVec4(0.54, 0.54, 0.54, 0.35);
|
c[ImGuiCol_Button] = ImVec4(0.54, 0.54, 0.54, 0.35);
|
||||||
c[ImGuiCol_ButtonHovered] = ImVec4(0.52, 0.52, 0.52, 0.59);
|
c[ImGuiCol_ButtonHovered] = ImVec4(0.52, 0.52, 0.52, 0.59);
|
||||||
c[ImGuiCol_ButtonActive] = ImVec4(0.76, 0.76, 0.76, 1.00);
|
c[ImGuiCol_ButtonActive] = ImVec4(0.76, 0.76, 0.76, 1.00);
|
||||||
c[ImGuiCol_Header] = ImVec4(0.38, 0.38, 0.38, 1.00);
|
c[ImGuiCol_Header] = ImVec4(0.38, 0.38, 0.38, 1.00);
|
||||||
c[ImGuiCol_HeaderHovered] = ImVec4(0.47, 0.47, 0.47, 1.00);
|
c[ImGuiCol_HeaderHovered] = ImVec4(0.47, 0.47, 0.47, 1.00);
|
||||||
c[ImGuiCol_HeaderActive] = ImVec4(0.76, 0.76, 0.76, 0.77);
|
c[ImGuiCol_HeaderActive] = ImVec4(0.76, 0.76, 0.76, 0.77);
|
||||||
c[ImGuiCol_Separator] = ImVec4(0.00, 0.00, 0.00, 0.18);
|
c[ImGuiCol_Separator] = ImVec4(0.00, 0.00, 0.00, 0.18);
|
||||||
c[ImGuiCol_SeparatorHovered] = ImVec4(0.70, 0.67, 0.60, 0.29);
|
c[ImGuiCol_SeparatorHovered] = ImVec4(0.70, 0.67, 0.60, 0.29);
|
||||||
c[ImGuiCol_SeparatorActive] = ImVec4(0.70, 0.67, 0.60, 0.67);
|
c[ImGuiCol_SeparatorActive] = ImVec4(0.70, 0.67, 0.60, 0.67);
|
||||||
c[ImGuiCol_ResizeGrip] = ImVec4(0.26, 0.59, 0.98, 0.25);
|
c[ImGuiCol_ResizeGrip] = ImVec4(0.26, 0.59, 0.98, 0.25);
|
||||||
c[ImGuiCol_ResizeGripHovered] = ImVec4(0.26, 0.59, 0.98, 0.67);
|
c[ImGuiCol_ResizeGripHovered] = ImVec4(0.26, 0.59, 0.98, 0.67);
|
||||||
c[ImGuiCol_ResizeGripActive] = ImVec4(0.26, 0.59, 0.98, 0.95);
|
c[ImGuiCol_ResizeGripActive] = ImVec4(0.26, 0.59, 0.98, 0.95);
|
||||||
c[ImGuiCol_PlotLines] = ImVec4(0.61, 0.61, 0.61, 1.00);
|
c[ImGuiCol_PlotLines] = ImVec4(0.61, 0.61, 0.61, 1.00);
|
||||||
c[ImGuiCol_PlotLinesHovered] = ImVec4(1.00, 0.43, 0.35, 1.00);
|
c[ImGuiCol_PlotLinesHovered] = ImVec4(1.00, 0.43, 0.35, 1.00);
|
||||||
c[ImGuiCol_PlotHistogram] = ImVec4(0.90, 0.70, 0.00, 1.00);
|
c[ImGuiCol_PlotHistogram] = ImVec4(0.90, 0.70, 0.00, 1.00);
|
||||||
c[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00, 0.60, 0.00, 1.00);
|
c[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00, 0.60, 0.00, 1.00);
|
||||||
c[ImGuiCol_TextSelectedBg] = ImVec4(0.73, 0.73, 0.73, 0.35);
|
c[ImGuiCol_TextSelectedBg] = ImVec4(0.73, 0.73, 0.73, 0.35);
|
||||||
c[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80, 0.80, 0.80, 0.35);
|
c[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80, 0.80, 0.80, 0.35);
|
||||||
c[ImGuiCol_DragDropTarget] = ImVec4(1.00, 1.00, 0.00, 0.90);
|
c[ImGuiCol_DragDropTarget] = ImVec4(1.00, 1.00, 0.00, 0.90);
|
||||||
c[ImGuiCol_NavHighlight] = ImVec4(0.26, 0.59, 0.98, 1.00);
|
c[ImGuiCol_NavHighlight] = ImVec4(0.26, 0.59, 0.98, 1.00);
|
||||||
c[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00, 1.00, 1.00, 0.70);
|
c[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00, 1.00, 1.00, 0.70);
|
||||||
c[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80, 0.80, 0.80, 0.20);
|
c[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80, 0.80, 0.80, 0.20);
|
||||||
c[ImGuiCol_DockingEmptyBg] = ImVec4(0.38, 0.38, 0.38, 1.00);
|
c[ImGuiCol_DockingEmptyBg] = ImVec4(0.38, 0.38, 0.38, 1.00);
|
||||||
c[ImGuiCol_Tab] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
c[ImGuiCol_Tab] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
||||||
c[ImGuiCol_TabHovered] = ImVec4(0.40, 0.40, 0.40, 1.00);
|
c[ImGuiCol_TabHovered] = ImVec4(0.40, 0.40, 0.40, 1.00);
|
||||||
c[ImGuiCol_TabActive] = ImVec4(0.33, 0.33, 0.33, 1.00);
|
c[ImGuiCol_TabActive] = ImVec4(0.33, 0.33, 0.33, 1.00);
|
||||||
c[ImGuiCol_TabUnfocused] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
c[ImGuiCol_TabUnfocused] = ImVec4(0.25, 0.25, 0.25, 1.00);
|
||||||
c[ImGuiCol_TabUnfocusedActive] = ImVec4(0.33, 0.33, 0.33, 1.00);
|
c[ImGuiCol_TabUnfocusedActive] = ImVec4(0.33, 0.33, 0.33, 1.00);
|
||||||
c[ImGuiCol_DockingPreview] = ImVec4(0.85, 0.85, 0.85, 0.28);
|
c[ImGuiCol_DockingPreview] = ImVec4(0.85, 0.85, 0.85, 0.28);
|
||||||
c[ImGuiCol_WindowBg].w = 1.0f;
|
c[ImGuiCol_WindowBg].w = 1.0f;
|
||||||
} else if (theme == GuiTheme::kLight) {
|
} else if (theme == GuiTheme::kLight) {
|
||||||
ImGui::StyleColorsLight(&s);
|
ImGui::StyleColorsLight(&s);
|
||||||
ImVec4 white = ImVec4(1.00, 1.00, 1.00, 1.00);
|
ImVec4 white = ImVec4(1.00, 1.00, 1.00, 1.00);
|
||||||
ImVec4 transparent = ImVec4(0.00, 0.00, 0.00, 0.00);
|
ImVec4 transparent = ImVec4(0.00, 0.00, 0.00, 0.00);
|
||||||
ImVec4 dark = ImVec4(0.00, 0.00, 0.00, 0.20);
|
ImVec4 dark = ImVec4(0.00, 0.00, 0.00, 0.20);
|
||||||
ImVec4 darker = ImVec4(0.00, 0.00, 0.00, 0.50);
|
ImVec4 darker = ImVec4(0.00, 0.00, 0.00, 0.50);
|
||||||
ImVec4 background = ImVec4(0.95, 0.95, 0.95, 1.00);
|
ImVec4 background = ImVec4(0.95, 0.95, 0.95, 1.00);
|
||||||
ImVec4 text = ImVec4(0.10, 0.10, 0.10, 1.00);
|
ImVec4 text = ImVec4(0.10, 0.10, 0.10, 1.00);
|
||||||
ImVec4 border = ImVec4(0.60, 0.60, 0.60, 1.00);
|
ImVec4 border = ImVec4(0.60, 0.60, 0.60, 1.00);
|
||||||
ImVec4 grab = ImVec4(0.69, 0.69, 0.69, 1.00);
|
ImVec4 grab = ImVec4(0.69, 0.69, 0.69, 1.00);
|
||||||
ImVec4 header = ImVec4(0.86, 0.86, 0.86, 1.00);
|
ImVec4 header = ImVec4(0.86, 0.86, 0.86, 1.00);
|
||||||
ImVec4 active = ImVec4(0.00, 0.47, 0.84, 1.00);
|
ImVec4 active = ImVec4(0.00, 0.47, 0.84, 1.00);
|
||||||
ImVec4 hover = ImVec4(0.00, 0.47, 0.84, 0.20);
|
ImVec4 hover = ImVec4(0.00, 0.47, 0.84, 0.20);
|
||||||
|
|
||||||
c[ImGuiCol_Text] = text;
|
c[ImGuiCol_Text] = text;
|
||||||
c[ImGuiCol_WindowBg] = background;
|
c[ImGuiCol_WindowBg] = background;
|
||||||
@@ -129,46 +130,46 @@ void SetupTheme(GuiTheme theme) {
|
|||||||
c[ImGuiCol_ScrollbarGrab] = grab;
|
c[ImGuiCol_ScrollbarGrab] = grab;
|
||||||
c[ImGuiCol_ScrollbarGrabHovered] = dark;
|
c[ImGuiCol_ScrollbarGrabHovered] = dark;
|
||||||
c[ImGuiCol_ScrollbarGrabActive] = darker;
|
c[ImGuiCol_ScrollbarGrabActive] = darker;
|
||||||
} else {
|
} else {
|
||||||
ImGui::StyleColorsDark(&s);
|
ImGui::StyleColorsDark(&s);
|
||||||
ImVec4 black = ImVec4(0.00, 0.00, 0.00, 1.0);
|
ImVec4 black = ImVec4(0.00, 0.00, 0.00, 1.0);
|
||||||
ImVec4 window = ImVec4(0.25, 0.25, 0.25, 1.0);
|
ImVec4 window = ImVec4(0.25, 0.25, 0.25, 1.0);
|
||||||
ImVec4 font_active = ImVec4(1.00, 1.00, 1.00, 1.0);
|
ImVec4 font_active = ImVec4(1.00, 1.00, 1.00, 1.0);
|
||||||
ImVec4 font_inactive = ImVec4(0.50, 0.50, 0.50, 1.0);
|
ImVec4 font_inactive = ImVec4(0.50, 0.50, 0.50, 1.0);
|
||||||
ImVec4 thumb = ImVec4(0.12, 0.12, 0.12, 1.0);
|
ImVec4 thumb = ImVec4(0.12, 0.12, 0.12, 1.0);
|
||||||
ImVec4 section = ImVec4(0.40, 0.15, 0.15, 1.0);
|
ImVec4 section = ImVec4(0.40, 0.15, 0.15, 1.0);
|
||||||
ImVec4 button = ImVec4(0.60, 0.40, 0.40, 1.0);
|
ImVec4 button = ImVec4(0.60, 0.40, 0.40, 1.0);
|
||||||
ImVec4 check = ImVec4(0.40, 0.40, 0.70, 1.0);
|
ImVec4 check = ImVec4(0.40, 0.40, 0.70, 1.0);
|
||||||
ImVec4 frame = ImVec4(0.40, 0.30, 0.40, 1.0);
|
ImVec4 frame = ImVec4(0.40, 0.30, 0.40, 1.0);
|
||||||
ImVec4 slider = ImVec4(0.60, 0.40, 0.60, 1.0);
|
ImVec4 slider = ImVec4(0.60, 0.40, 0.60, 1.0);
|
||||||
|
|
||||||
c[ImGuiCol_WindowBg] = window;
|
c[ImGuiCol_WindowBg] = window;
|
||||||
c[ImGuiCol_ChildBg] = black;
|
c[ImGuiCol_ChildBg] = black;
|
||||||
c[ImGuiCol_PopupBg] = window;
|
c[ImGuiCol_PopupBg] = window;
|
||||||
c[ImGuiCol_Text] = font_active;
|
c[ImGuiCol_Text] = font_active;
|
||||||
c[ImGuiCol_TextDisabled] = font_inactive;
|
c[ImGuiCol_TextDisabled] = font_inactive;
|
||||||
c[ImGuiCol_CheckMark] = font_active;
|
c[ImGuiCol_CheckMark] = font_active;
|
||||||
c[ImGuiCol_Header] = section;
|
c[ImGuiCol_Header] = section;
|
||||||
c[ImGuiCol_HeaderHovered] = section;
|
c[ImGuiCol_HeaderHovered] = section;
|
||||||
c[ImGuiCol_HeaderActive] = section;
|
c[ImGuiCol_HeaderActive] = section;
|
||||||
c[ImGuiCol_TitleBgActive] = window;
|
c[ImGuiCol_TitleBgActive] = window;
|
||||||
c[ImGuiCol_ScrollbarBg] = window;
|
c[ImGuiCol_ScrollbarBg] = window;
|
||||||
c[ImGuiCol_ScrollbarGrab] = thumb;
|
c[ImGuiCol_ScrollbarGrab] = thumb;
|
||||||
c[ImGuiCol_ScrollbarGrabHovered] = thumb;
|
c[ImGuiCol_ScrollbarGrabHovered] = thumb;
|
||||||
c[ImGuiCol_ScrollbarGrabActive] = thumb;
|
c[ImGuiCol_ScrollbarGrabActive] = thumb;
|
||||||
c[ImGuiCol_FrameBg] = frame;
|
c[ImGuiCol_FrameBg] = frame;
|
||||||
c[ImGuiCol_FrameBgHovered] = frame;
|
c[ImGuiCol_FrameBgHovered] = frame;
|
||||||
c[ImGuiCol_FrameBgActive] = frame;
|
c[ImGuiCol_FrameBgActive] = frame;
|
||||||
c[ImGuiCol_SliderGrab] = slider;
|
c[ImGuiCol_SliderGrab] = slider;
|
||||||
c[ImGuiCol_SliderGrabActive] = slider;
|
c[ImGuiCol_SliderGrabActive] = slider;
|
||||||
c[ImGuiCol_Button] = window;
|
c[ImGuiCol_Button] = window;
|
||||||
c[ImGuiCol_ButtonHovered] = button;
|
c[ImGuiCol_ButtonHovered] = button;
|
||||||
c[ImGuiCol_ButtonActive] = button;
|
c[ImGuiCol_ButtonActive] = button;
|
||||||
c[ImGuiCol_Tab] = window;
|
c[ImGuiCol_Tab] = window;
|
||||||
c[ImGuiCol_TabHovered] = check;
|
c[ImGuiCol_TabHovered] = check;
|
||||||
c[ImGuiCol_TabSelected] = check;
|
c[ImGuiCol_TabSelected] = check;
|
||||||
c[ImGuiCol_TabDimmed] = window;
|
c[ImGuiCol_TabDimmed] = window;
|
||||||
c[ImGuiCol_TabDimmedSelected] = check;
|
c[ImGuiCol_TabDimmedSelected] = check;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hspacing = 4;
|
int hspacing = 4;
|
||||||
@@ -207,14 +208,11 @@ ImVec4 ConfigureDockingLayout() {
|
|||||||
const float kToolsBarHeight = 48.f * scale;
|
const float kToolsBarHeight = 48.f * scale;
|
||||||
const float kStatusBarHeight = 32.f * scale;
|
const float kStatusBarHeight = 32.f * scale;
|
||||||
|
|
||||||
const ImVec2 dockspace_pos{
|
const ImVec2 dockspace_pos{viewport->WorkPos.x,
|
||||||
viewport->WorkPos.x,
|
viewport->WorkPos.y + kToolsBarHeight};
|
||||||
viewport->WorkPos.y + kToolsBarHeight
|
|
||||||
};
|
|
||||||
const ImVec2 dockspace_size{
|
const ImVec2 dockspace_size{
|
||||||
viewport->WorkSize.x,
|
viewport->WorkSize.x,
|
||||||
viewport->WorkSize.y - kToolsBarHeight - kStatusBarHeight
|
viewport->WorkSize.y - kToolsBarHeight - kStatusBarHeight};
|
||||||
};
|
|
||||||
|
|
||||||
ImGuiID root = ImGui::GetID("Root");
|
ImGuiID root = ImGui::GetID("Root");
|
||||||
const bool first_time = (ImGui::DockBuilderGetNode(root) == nullptr);
|
const bool first_time = (ImGui::DockBuilderGetNode(root) == nullptr);
|
||||||
@@ -228,16 +226,16 @@ ImVec4 ConfigureDockingLayout() {
|
|||||||
ImGuiID main = root;
|
ImGuiID main = root;
|
||||||
|
|
||||||
ImGuiID options = 0;
|
ImGuiID options = 0;
|
||||||
ImGui::DockBuilderSplitNode(main, ImGuiDir_Left, kOptionsRelWidth,
|
ImGui::DockBuilderSplitNode(main, ImGuiDir_Left, kOptionsRelWidth, &options,
|
||||||
&options, &main);
|
&main);
|
||||||
|
|
||||||
ImGuiID inspector = 0;
|
ImGuiID inspector = 0;
|
||||||
ImGui::DockBuilderSplitNode(main, ImGuiDir_Right, kInspectorRelWidth,
|
ImGui::DockBuilderSplitNode(main, ImGuiDir_Right, kInspectorRelWidth,
|
||||||
&inspector, &main);
|
&inspector, &main);
|
||||||
|
|
||||||
ImGuiID stats = 0;
|
ImGuiID stats = 0;
|
||||||
ImGui::DockBuilderSplitNode(options, ImGuiDir_Down, kStatsRelHeight,
|
ImGui::DockBuilderSplitNode(options, ImGuiDir_Down, kStatsRelHeight, &stats,
|
||||||
&stats, &options);
|
&options);
|
||||||
|
|
||||||
ImGuiID properties = 0;
|
ImGuiID properties = 0;
|
||||||
ImGui::DockBuilderSplitNode(inspector, ImGuiDir_Down, kStatsRelHeight,
|
ImGui::DockBuilderSplitNode(inspector, ImGuiDir_Down, kStatsRelHeight,
|
||||||
@@ -265,11 +263,10 @@ ImVec4 ConfigureDockingLayout() {
|
|||||||
ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBringToFrontOnFocus |
|
ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||||
ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBackground;
|
ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBackground;
|
||||||
|
|
||||||
const ImGuiWindowFlags kFixedFlags = ImGuiWindowFlags_NoTitleBar |
|
const ImGuiWindowFlags kFixedFlags =
|
||||||
ImGuiWindowFlags_NoMove |
|
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove |
|
||||||
ImGuiWindowFlags_NoResize |
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
|
||||||
ImGuiWindowFlags_NoScrollbar |
|
ImGuiWindowFlags_NoDocking;
|
||||||
ImGuiWindowFlags_NoDocking;
|
|
||||||
|
|
||||||
// Main workspace area in which we can dock other windows.
|
// Main workspace area in which we can dock other windows.
|
||||||
{
|
{
|
||||||
@@ -292,7 +289,8 @@ ImVec4 ConfigureDockingLayout() {
|
|||||||
style.Var(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
style.Var(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||||
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
|
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
ImGui::SetNextWindowPos(viewport->WorkPos, ImGuiCond_Always);
|
ImGui::SetNextWindowPos(viewport->WorkPos, ImGuiCond_Always);
|
||||||
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kToolsBarHeight), ImGuiCond_Always);
|
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kToolsBarHeight),
|
||||||
|
ImGuiCond_Always);
|
||||||
ImGui::Begin("ToolBar", nullptr, kFixedFlags);
|
ImGui::Begin("ToolBar", nullptr, kFixedFlags);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
@@ -302,8 +300,10 @@ ImVec4 ConfigureDockingLayout() {
|
|||||||
platform::ScopedStyle style;
|
platform::ScopedStyle style;
|
||||||
style.Var(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
style.Var(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||||
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
|
style.Var(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
ImGui::SetNextWindowPos(ImVec2(0, viewport->Size.y - kStatusBarHeight), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(0, viewport->Size.y - kStatusBarHeight),
|
||||||
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kStatusBarHeight), ImGuiCond_Always);
|
ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, kStatusBarHeight),
|
||||||
|
ImGuiCond_Always);
|
||||||
ImGui::Begin("StatusBar", nullptr, kFixedFlags);
|
ImGui::Begin("StatusBar", nullptr, kFixedFlags);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
@@ -317,6 +317,148 @@ ImVec4 ConfigureDockingLayout() {
|
|||||||
return ImVec4(workspace_x, workspace_y, workspace_w, workspace_h);
|
return ImVec4(workspace_x, workspace_y, workspace_w, workspace_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ThemeSelectGui(GuiTheme* theme) {
|
||||||
|
static constexpr const char* ICON_DARKMODE = ICON_FA_CIRCLE;
|
||||||
|
static constexpr const char* ICON_LIGHTMODE = ICON_FA_CIRCLE_O;
|
||||||
|
static constexpr const char* ICON_CLASSICMODE = ICON_FA_ADJUST;
|
||||||
|
const char* theme_icons[] = {ICON_LIGHTMODE, ICON_DARKMODE, ICON_CLASSICMODE};
|
||||||
|
const char* theme_tooltips[] = {"Light Mode", "Dark Mode", "Classic Mode"};
|
||||||
|
const GuiTheme theme_values[] = {
|
||||||
|
GuiTheme::kLight,
|
||||||
|
GuiTheme::kDark,
|
||||||
|
GuiTheme::kClassic,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
int theme_idx = static_cast<int>(*theme);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::CalcTextSize(theme_icons[0]).x +
|
||||||
|
ImGui::GetStyle().FramePadding.x * 2);
|
||||||
|
if (ImGui::BeginCombo("##Theme", theme_icons[theme_idx],
|
||||||
|
ImGuiComboFlags_NoArrowButton)) {
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(theme_icons); n++) {
|
||||||
|
if (ImGui::Selectable(theme_icons[n], (theme_idx == n))) {
|
||||||
|
*theme = theme_values[n];
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("%s", theme_tooltips[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("%s", "Theme");
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LabelSelectionGui(mjvOption* opts) {
|
||||||
|
static constexpr const char* ICON_LABEL = ICON_FA_COMMENT;
|
||||||
|
static constexpr const char* kLabelNames[] = {
|
||||||
|
"None", "Body", "Joint", "Geom", "Site", "Camera",
|
||||||
|
"Light", "Tendon", "Actuator", "Constraint", "Flex", "Skin",
|
||||||
|
"Selection", "Sel Pnt", "Contact", "Force", "Island"};
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
const std::string label_preview =
|
||||||
|
std::string(ICON_LABEL) + " " + kLabelNames[opts->label];
|
||||||
|
if (ImGui::BeginCombo("##Label", label_preview.c_str(),
|
||||||
|
ImGuiComboFlags_NoArrowButton)) {
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(kLabelNames); n++) {
|
||||||
|
if (ImGui::Selectable(kLabelNames[n], (opts->label == n))) {
|
||||||
|
changed = true;
|
||||||
|
opts->label = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("%s", "Label");
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FrameSelectionGui(mjvOption* opts) {
|
||||||
|
static constexpr const char* ICON_FRAME = ICON_FA_ARROWS;
|
||||||
|
static constexpr const char* kFrameNames[] = {
|
||||||
|
"None", "Body", "Geom", "Site", "Camera", "Light", "Contact", "World"};
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
const std::string frame_preview =
|
||||||
|
std::string(ICON_FRAME) + " " + kFrameNames[opts->frame];
|
||||||
|
if (ImGui::BeginCombo("##Frame", frame_preview.c_str(),
|
||||||
|
ImGuiComboFlags_NoArrowButton)) {
|
||||||
|
for (int n = 0; n < IM_ARRAYSIZE(kFrameNames); n++) {
|
||||||
|
if (ImGui::Selectable(kFrameNames[n], (opts->frame == n))) {
|
||||||
|
opts->frame = n;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("%s", "Frame");
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetCameraName(const mjModel* model, const mjvCamera& camera,
|
||||||
|
int index) {
|
||||||
|
static constexpr char kCameraTumbleName[] = "Free: tumble";
|
||||||
|
static constexpr char kCameraWasdName[] = "Free: wasd";
|
||||||
|
static constexpr char kCameraUnnamedName[] = "Unnamed";
|
||||||
|
|
||||||
|
if (index == kTumbleCameraIdx) {
|
||||||
|
return kCameraTumbleName;
|
||||||
|
} else if (index == kFreeCameraIdx) {
|
||||||
|
return kCameraWasdName;
|
||||||
|
} else if (index == kTrackingCameraIdx) {
|
||||||
|
return "Tracking (" + std::to_string(camera.trackbodyid) + ")";
|
||||||
|
} else if (model->names[model->name_camadr[index]]) {
|
||||||
|
return std::string(model->names + model->name_camadr[index]);
|
||||||
|
} else {
|
||||||
|
return kCameraUnnamedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CameraSelectionGui(const mjModel* model, mjData* data, mjvCamera& camera,
|
||||||
|
int& index) {
|
||||||
|
static constexpr const char* ICON_CAMERA = ICON_FA_CAMERA;
|
||||||
|
|
||||||
|
auto select = [&](int type, int idx) {
|
||||||
|
if (ImGui::Selectable(GetCameraName(model, camera, type).c_str(),
|
||||||
|
(type == idx))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
const std::string preview =
|
||||||
|
std::string(ICON_CAMERA) + " " + GetCameraName(model, camera, index);
|
||||||
|
if (ImGui::BeginCombo("##Camera", preview.c_str(),
|
||||||
|
ImGuiComboFlags_NoArrowButton)) {
|
||||||
|
if (select(kTumbleCameraIdx, index)) {
|
||||||
|
index = SetCamera(model, &camera, kTumbleCameraIdx);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (select(kFreeCameraIdx, index)) {
|
||||||
|
index = SetCamera(model, &camera, kFreeCameraIdx);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (select(kTrackingCameraIdx, index)) {
|
||||||
|
index = SetCamera(model, &camera, kTrackingCameraIdx);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
for (int cam = 0; cam < model->ncam; cam++) {
|
||||||
|
if (select(cam, index)) {
|
||||||
|
index = SetCamera(model, &camera, cam);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("%s", "Camera");
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
void SensorGui(const mjModel* model, const mjData* data) {
|
void SensorGui(const mjModel* model, const mjData* data) {
|
||||||
if (model->nsensor == 0) {
|
if (model->nsensor == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -392,11 +534,12 @@ void StateGui(const mjModel* model, mjData* data, std::vector<mjtNum>& state,
|
|||||||
|
|
||||||
ImGui::Unindent(0.5f * ImGui::GetTreeNodeToLabelSpacing());
|
ImGui::Unindent(0.5f * ImGui::GetTreeNodeToLabelSpacing());
|
||||||
// State component names and tooltips.
|
// State component names and tooltips.
|
||||||
static constexpr const char* name_and_tooltip[][2] = {
|
static constexpr const char* name_and_tooltip[mjNSTATE][2] = {
|
||||||
{"TIME", "Time"},
|
{"TIME", "Time"},
|
||||||
{"QPOS", "Position"},
|
{"QPOS", "Position"},
|
||||||
{"QVEL", "Velocity"},
|
{"QVEL", "Velocity"},
|
||||||
{"ACT", "Actuator activation"},
|
{"ACT", "Actuator activation"},
|
||||||
|
{"HISTORY", "History buffers (control, sensor)"},
|
||||||
{"WARMSTART", "Acceleration used for warmstart"},
|
{"WARMSTART", "Acceleration used for warmstart"},
|
||||||
{"CTRL", "Control"},
|
{"CTRL", "Control"},
|
||||||
{"QFRC_APPLIED", "Applied generalized force"},
|
{"QFRC_APPLIED", "Applied generalized force"},
|
||||||
@@ -935,36 +1078,49 @@ void ConvergenceGui(const mjModel* model, mjData* data) {
|
|||||||
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
||||||
ImPlot::SetupFinish();
|
ImPlot::SetupFinish();
|
||||||
|
|
||||||
const int nisland = data->nefc ? mjMAX(1, mjMIN(data->nisland, mjNISLAND)) : 0;
|
const int nisland =
|
||||||
|
data->nefc ? mjMAX(1, mjMIN(data->nisland, mjNISLAND)) : 0;
|
||||||
for (int k = 0; k < nisland; k++) {
|
for (int k = 0; k < nisland; k++) {
|
||||||
mjSolverStat* stats = data->solver + k * mjNSOLVER;
|
mjSolverStat* stats = data->solver + k * mjNSOLVER;
|
||||||
const int npoints =
|
const int npoints =
|
||||||
mjMIN(mjMIN(data->solver_niter[k], mjNSOLVER), mjMAXLINEPNT);
|
mjMIN(mjMIN(data->solver_niter[k], mjNSOLVER), mjMAXLINEPNT);
|
||||||
|
|
||||||
ImPlot::PlotLineG("improvement", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const mjSolverStat* stats = static_cast<const mjSolverStat*>(user_data);
|
"improvement",
|
||||||
const float x = static_cast<float>(i);
|
+[](int i, void* user_data) {
|
||||||
const float y = mju_log10(mju_max(mjMINVAL, stats[i].improvement));
|
const mjSolverStat* stats =
|
||||||
return ImPlotPoint{x, y};
|
static_cast<const mjSolverStat*>(user_data);
|
||||||
}, stats, npoints);
|
const float x = static_cast<float>(i);
|
||||||
|
const float y = mju_log10(mju_max(mjMINVAL, stats[i].improvement));
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
stats, npoints);
|
||||||
|
|
||||||
if (model->opt.solver == mjSOL_PGS) {
|
if (model->opt.solver == mjSOL_PGS) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImPlot::PlotLineG("gradient", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const mjSolverStat* stats = static_cast<const mjSolverStat*>(user_data);
|
"gradient",
|
||||||
const float x = static_cast<float>(i);
|
+[](int i, void* user_data) {
|
||||||
const float y = mju_log10(mju_max(mjMINVAL, stats[i].gradient));
|
const mjSolverStat* stats =
|
||||||
return ImPlotPoint{x, y};
|
static_cast<const mjSolverStat*>(user_data);
|
||||||
}, stats, npoints);
|
const float x = static_cast<float>(i);
|
||||||
|
const float y = mju_log10(mju_max(mjMINVAL, stats[i].gradient));
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
stats, npoints);
|
||||||
|
|
||||||
ImPlot::PlotLineG("lineslope", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const mjSolverStat* stats = static_cast<const mjSolverStat*>(user_data);
|
"lineslope",
|
||||||
const float x = static_cast<float>(i);
|
+[](int i, void* user_data) {
|
||||||
const float y = mju_log10(mju_max(mjMINVAL, stats[i].lineslope));
|
const mjSolverStat* stats =
|
||||||
return ImPlotPoint{x, y};
|
static_cast<const mjSolverStat*>(user_data);
|
||||||
}, stats, npoints);
|
const float x = static_cast<float>(i);
|
||||||
|
const float y = mju_log10(mju_max(mjMINVAL, stats[i].lineslope));
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
stats, npoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImPlot::PopStyleVar();
|
ImPlot::PopStyleVar();
|
||||||
@@ -982,56 +1138,76 @@ void CountsGui(const mjModel* model, mjData* data) {
|
|||||||
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
ImPlot::SetupLegend(ImPlotLocation_NorthEast);
|
||||||
ImPlot::SetupFinish();
|
ImPlot::SetupFinish();
|
||||||
|
|
||||||
const int nisland = data->nefc ? mjMAX(1, mjMIN(data->nisland, mjNISLAND)) : 0;
|
const int nisland =
|
||||||
|
data->nefc ? mjMAX(1, mjMIN(data->nisland, mjNISLAND)) : 0;
|
||||||
for (int k = 0; k < nisland; k++) {
|
for (int k = 0; k < nisland; k++) {
|
||||||
const int npoints =
|
const int npoints =
|
||||||
mjMIN(mjMIN(data->solver_niter[k], mjNSOLVER), mjMAXLINEPNT);
|
mjMIN(mjMIN(data->solver_niter[k], mjNSOLVER), mjMAXLINEPNT);
|
||||||
|
|
||||||
mjSolverStat* stats = data->solver + k*mjNSOLVER;
|
mjSolverStat* stats = data->solver + k * mjNSOLVER;
|
||||||
|
|
||||||
int nefc = nisland == 1 ? data->nefc : data->island_nefc[k];
|
int nefc = nisland == 1 ? data->nefc : data->island_nefc[k];
|
||||||
|
|
||||||
ImPlot::PlotLineG("total", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const float x = static_cast<float>(i);
|
"total",
|
||||||
const float y = *(static_cast<int*>(user_data));
|
+[](int i, void* user_data) {
|
||||||
return ImPlotPoint{x, y};
|
const float x = static_cast<float>(i);
|
||||||
}, &nefc, npoints);
|
const float y = *(static_cast<int*>(user_data));
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
&nefc, npoints);
|
||||||
|
|
||||||
ImPlot::PlotLineG("active", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const mjSolverStat* stats = static_cast<const mjSolverStat*>(user_data);
|
"active",
|
||||||
const float x = static_cast<float>(i);
|
+[](int i, void* user_data) {
|
||||||
const float y = stats[i].nactive;
|
const mjSolverStat* stats =
|
||||||
return ImPlotPoint{x, y};
|
static_cast<const mjSolverStat*>(user_data);
|
||||||
}, stats, npoints);
|
const float x = static_cast<float>(i);
|
||||||
|
const float y = stats[i].nactive;
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
stats, npoints);
|
||||||
|
|
||||||
ImPlot::PlotLineG("changed", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const mjSolverStat* stats = static_cast<const mjSolverStat*>(user_data);
|
"changed",
|
||||||
const float x = static_cast<float>(i);
|
+[](int i, void* user_data) {
|
||||||
const float y = stats[i].nchange;
|
const mjSolverStat* stats =
|
||||||
return ImPlotPoint{x, y};
|
static_cast<const mjSolverStat*>(user_data);
|
||||||
}, stats, npoints);
|
const float x = static_cast<float>(i);
|
||||||
|
const float y = stats[i].nchange;
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
stats, npoints);
|
||||||
|
|
||||||
if (model->opt.solver == mjSOL_PGS) {
|
if (model->opt.solver == mjSOL_PGS) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImPlot::PlotLineG("evals", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const mjSolverStat* stats = static_cast<const mjSolverStat*>(user_data);
|
"evals",
|
||||||
const float x = static_cast<float>(i);
|
+[](int i, void* user_data) {
|
||||||
const float y = stats[i].neval;
|
const mjSolverStat* stats =
|
||||||
return ImPlotPoint{x, y};
|
static_cast<const mjSolverStat*>(user_data);
|
||||||
}, stats, npoints);
|
const float x = static_cast<float>(i);
|
||||||
|
const float y = stats[i].neval;
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
stats, npoints);
|
||||||
|
|
||||||
if (model->opt.solver == mjSOL_CG) {
|
if (model->opt.solver == mjSOL_CG) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImPlot::PlotLineG("updates", +[](int i, void* user_data) {
|
ImPlot::PlotLineG(
|
||||||
const mjSolverStat* stats = static_cast<const mjSolverStat*>(user_data);
|
"updates",
|
||||||
const float x = static_cast<float>(i);
|
+[](int i, void* user_data) {
|
||||||
const float y = stats[i].nupdate;
|
const mjSolverStat* stats =
|
||||||
return ImPlotPoint{x, y};
|
static_cast<const mjSolverStat*>(user_data);
|
||||||
}, stats, npoints);
|
const float x = static_cast<float>(i);
|
||||||
|
const float y = stats[i].nupdate;
|
||||||
|
return ImPlotPoint{x, y};
|
||||||
|
},
|
||||||
|
stats, npoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImPlot::PopStyleVar();
|
ImPlot::PopStyleVar();
|
||||||
|
|||||||
@@ -62,6 +62,19 @@ void SetupTheme(GuiTheme theme);
|
|||||||
// be used to place additional elements (e.g. floating charts).
|
// be used to place additional elements (e.g. floating charts).
|
||||||
ImVec4 ConfigureDockingLayout();
|
ImVec4 ConfigureDockingLayout();
|
||||||
|
|
||||||
|
// UX for selecting the GUI theme.
|
||||||
|
bool ThemeSelectGui(GuiTheme* theme);
|
||||||
|
|
||||||
|
// UX for selecting the visualization label option.
|
||||||
|
bool LabelSelectionGui(mjvOption* opts);
|
||||||
|
|
||||||
|
// UX for selecting the visualization frame option.
|
||||||
|
bool FrameSelectionGui(mjvOption* opts);
|
||||||
|
|
||||||
|
// UX for selecting the camera.
|
||||||
|
bool CameraSelectionGui(const mjModel* model, mjData* data, mjvCamera& camera,
|
||||||
|
int& index);
|
||||||
|
|
||||||
// UX for controlling the physics simulation parameters (e.g. integrator,
|
// UX for controlling the physics simulation parameters (e.g. integrator,
|
||||||
// solver, etc.) in mjModel.
|
// solver, etc.) in mjModel.
|
||||||
void PhysicsGui(mjModel* model, float min_width);
|
void PhysicsGui(mjModel* model, float min_width);
|
||||||
|
|||||||
@@ -93,28 +93,29 @@ static void AddEditorButtons(mjsElement* element, mjsElement** selected_element,
|
|||||||
if (ImGui::SmallButton(ICON_FA_PLUS)) {
|
if (ImGui::SmallButton(ICON_FA_PLUS)) {
|
||||||
ImGui::OpenPopupOnItemClick("BodyAddChild", 0);
|
ImGui::OpenPopupOnItemClick("BodyAddChild", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupContextItem("BodyAddChild")) {
|
||||||
|
mjsBody* body = mjs_asBody(element);
|
||||||
|
auto option = [&](const char* label, mjtObj type) {
|
||||||
|
if (ImGui::Selectable(label)) {
|
||||||
|
*selected_element = editor.AddBodyElement(body, type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
option("Camera", mjOBJ_CAMERA);
|
||||||
|
option("Frame", mjOBJ_FRAME);
|
||||||
|
option("Geom", mjOBJ_GEOM);
|
||||||
|
option("Joint", mjOBJ_JOINT);
|
||||||
|
option("Light", mjOBJ_LIGHT);
|
||||||
|
option("Site", mjOBJ_SITE);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
}
|
}
|
||||||
if (ImGui::SmallButton(ICON_FA_TRASH_CAN)) {
|
if (ImGui::SmallButton(ICON_FA_TRASH_CAN)) {
|
||||||
editor.DeleteActiveElement();
|
editor.DeleteActiveElement();
|
||||||
*selected_element = nullptr;
|
*selected_element = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginPopupContextItem("BodyAddChild")) {
|
|
||||||
mjsBody* body = mjs_asBody(element);
|
|
||||||
auto option = [&](const char* label, mjtObj type) {
|
|
||||||
if (ImGui::Selectable(label)) {
|
|
||||||
*selected_element = editor.AddBodyElement(body, type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
option("Camera", mjOBJ_CAMERA);
|
|
||||||
option("Frame", mjOBJ_FRAME);
|
|
||||||
option("Geom", mjOBJ_GEOM);
|
|
||||||
option("Joint", mjOBJ_JOINT);
|
|
||||||
option("Light", mjOBJ_LIGHT);
|
|
||||||
option("Site", mjOBJ_SITE);
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SelectableElement(mjsElement* element,
|
static void SelectableElement(mjsElement* element,
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ std::unique_ptr<ModelHolder> ModelHolder::FromBuffer(
|
|||||||
return mh;
|
return mh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelHolder::ModelHolder() {
|
||||||
|
mj_defaultVFS(&vfs_);
|
||||||
|
}
|
||||||
|
|
||||||
ModelHolder::~ModelHolder() {
|
ModelHolder::~ModelHolder() {
|
||||||
if (data_) {
|
if (data_) {
|
||||||
mj_deleteData(data_);
|
mj_deleteData(data_);
|
||||||
@@ -94,7 +98,6 @@ void ModelHolder::InitFromSpec(mjSpec* spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ModelHolder::InitFromFile(std::string_view filepath) {
|
void ModelHolder::InitFromFile(std::string_view filepath) {
|
||||||
mj_defaultVFS(&vfs_);
|
|
||||||
if (filepath.ends_with(".mjb")) {
|
if (filepath.ends_with(".mjb")) {
|
||||||
model_ = mj_loadModel(filepath.data(), &vfs_);
|
model_ = mj_loadModel(filepath.data(), &vfs_);
|
||||||
} else {
|
} else {
|
||||||
@@ -108,8 +111,6 @@ void ModelHolder::InitFromFile(std::string_view filepath) {
|
|||||||
void ModelHolder::InitFromBuffer(std::span<const std::byte> buffer,
|
void ModelHolder::InitFromBuffer(std::span<const std::byte> buffer,
|
||||||
std::string_view content_type,
|
std::string_view content_type,
|
||||||
std::string_view filename) {
|
std::string_view filename) {
|
||||||
mj_defaultVFS(&vfs_);
|
|
||||||
|
|
||||||
if (content_type == "text/xml") {
|
if (content_type == "text/xml") {
|
||||||
const char* ptr = reinterpret_cast<const char*>(buffer.data());
|
const char* ptr = reinterpret_cast<const char*>(buffer.data());
|
||||||
spec_ = mj_parseXMLString(ptr, nullptr, error_, sizeof(error_));
|
spec_ = mj_parseXMLString(ptr, nullptr, error_, sizeof(error_));
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class ModelHolder {
|
|||||||
mjModel* ReleaseModel();
|
mjModel* ReleaseModel();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModelHolder() = default;
|
ModelHolder();
|
||||||
void InitFromSpec(mjSpec* spec);
|
void InitFromSpec(mjSpec* spec);
|
||||||
void InitFromFile(std::string_view filepath);
|
void InitFromFile(std::string_view filepath);
|
||||||
void InitFromBuffer(std::span<const std::byte> buffer,
|
void InitFromBuffer(std::span<const std::byte> buffer,
|
||||||
|
|||||||
+13
-116
@@ -78,13 +78,7 @@ static constexpr const char* ICON_VISCOUS_PAUSE = platform::ICON_FA_MAGIC;
|
|||||||
static constexpr const char* ICON_COPY_CAMERA = platform::ICON_FA_COPY;
|
static constexpr const char* ICON_COPY_CAMERA = platform::ICON_FA_COPY;
|
||||||
static constexpr const char* ICON_UNLOAD_MODEL = platform::ICON_FA_EJECT;
|
static constexpr const char* ICON_UNLOAD_MODEL = platform::ICON_FA_EJECT;
|
||||||
static constexpr const char* ICON_RELOAD_MODEL = platform::ICON_FA_REFRESH;
|
static constexpr const char* ICON_RELOAD_MODEL = platform::ICON_FA_REFRESH;
|
||||||
static constexpr const char* ICON_LABEL = platform::ICON_FA_COMMENT;
|
|
||||||
static constexpr const char* ICON_RESET_MODEL = platform::ICON_FA_UNDO;
|
static constexpr const char* ICON_RESET_MODEL = platform::ICON_FA_UNDO;
|
||||||
static constexpr const char* ICON_FRAME = platform::ICON_FA_ARROWS;
|
|
||||||
static constexpr const char* ICON_CAMERA = platform::ICON_FA_CAMERA;
|
|
||||||
static constexpr const char* ICON_DARKMODE = platform::ICON_FA_CIRCLE;
|
|
||||||
static constexpr const char* ICON_LIGHTMODE = platform::ICON_FA_CIRCLE_O;
|
|
||||||
static constexpr const char* ICON_CLASSICMODE = platform::ICON_FA_ADJUST;
|
|
||||||
static constexpr const char* ICON_PREV_FRAME = platform::ICON_FA_CARET_LEFT;
|
static constexpr const char* ICON_PREV_FRAME = platform::ICON_FA_CARET_LEFT;
|
||||||
static constexpr const char* ICON_NEXT_FRAME = platform::ICON_FA_CARET_RIGHT;
|
static constexpr const char* ICON_NEXT_FRAME = platform::ICON_FA_CARET_RIGHT;
|
||||||
static constexpr const char* ICON_CURR_FRAME = platform::ICON_FA_FAST_FORWARD;
|
static constexpr const char* ICON_CURR_FRAME = platform::ICON_FA_FAST_FORWARD;
|
||||||
@@ -93,16 +87,6 @@ static constexpr const char* ICON_RELOAD_SPEC = platform::ICON_FA_REFRESH;
|
|||||||
static constexpr const char* ICON_UNDO_SPEC = platform::ICON_FA_UNDO;
|
static constexpr const char* ICON_UNDO_SPEC = platform::ICON_FA_UNDO;
|
||||||
static constexpr const char* ICON_REDO_SPEC = platform::ICON_FA_REPEAT;
|
static constexpr const char* ICON_REDO_SPEC = platform::ICON_FA_REPEAT;
|
||||||
|
|
||||||
// UI labels for mjtLabel.
|
|
||||||
static constexpr const char* kLabelNames[] = {
|
|
||||||
"None", "Body", "Joint", "Geom", "Site", "Camera",
|
|
||||||
"Light", "Tendon", "Actuator", "Constraint", "Flex", "Skin",
|
|
||||||
"Selection", "Sel Pnt", "Contact", "Force", "Island"};
|
|
||||||
|
|
||||||
// UI labels for mjtFrame.
|
|
||||||
static constexpr const char* kFrameNames[] = {
|
|
||||||
"None", "Body", "Geom", "Site", "Camera", "Light", "Contact", "World"};
|
|
||||||
|
|
||||||
// logarithmically spaced real-time slow-down coefficients (percent)
|
// logarithmically spaced real-time slow-down coefficients (percent)
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static constexpr std::array<const char*, 31> kPercentRealTime = {
|
static constexpr std::array<const char*, 31> kPercentRealTime = {
|
||||||
@@ -846,7 +830,10 @@ void App::BuildGui() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupTheme(ui_.theme);
|
if (!tmp_.style_editor) {
|
||||||
|
platform::SetupTheme(ui_.theme);
|
||||||
|
}
|
||||||
|
|
||||||
const ImVec4 workspace_rect = platform::ConfigureDockingLayout();
|
const ImVec4 workspace_rect = platform::ConfigureDockingLayout();
|
||||||
|
|
||||||
// Place charts in bottom right corner of the workspace.
|
// Place charts in bottom right corner of the workspace.
|
||||||
@@ -1024,14 +1011,6 @@ void App::BuildGui() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::SetupTheme(platform::GuiTheme theme) {
|
|
||||||
if (!tmp_.style_editor) {
|
|
||||||
platform::SetupTheme(theme);
|
|
||||||
ui_.theme = theme;
|
|
||||||
ImGui::GetIO().WantSaveIniSettings = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void App::ModelOptionsGui() {
|
void App::ModelOptionsGui() {
|
||||||
const float min_width = GetExpectedLabelWidth();
|
const float min_width = GetExpectedLabelWidth();
|
||||||
const ImGuiChildFlags child_flags =
|
const ImGuiChildFlags child_flags =
|
||||||
@@ -1449,7 +1428,7 @@ void App::ToolBarGui() {
|
|||||||
const float label_width = GetExpectedLabelWidth();
|
const float label_width = GetExpectedLabelWidth();
|
||||||
const float copy_btn_width = ImGui::CalcTextSize(ICON_COPY_CAMERA).x +
|
const float copy_btn_width = ImGui::CalcTextSize(ICON_COPY_CAMERA).x +
|
||||||
ImGui::GetStyle().FramePadding.x * 2;
|
ImGui::GetStyle().FramePadding.x * 2;
|
||||||
const float theme_width = ImGui::CalcTextSize(ICON_LIGHTMODE).x +
|
const float theme_width = ImGui::CalcTextSize(platform::ICON_FA_CIRCLE_O).x +
|
||||||
ImGui::GetStyle().FramePadding.x * 2;
|
ImGui::GetStyle().FramePadding.x * 2;
|
||||||
const float sp = ImGui::GetStyle().ItemSpacing.x;
|
const float sp = ImGui::GetStyle().ItemSpacing.x;
|
||||||
const float right_width = label_width + sp + label_width + sp +
|
const float right_width = label_width + sp + label_width + sp +
|
||||||
@@ -1576,84 +1555,30 @@ void App::ToolBarGui() {
|
|||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
||||||
(button_size.y - ImGui::GetFrameHeight()) * 0.5f);
|
(button_size.y - ImGui::GetFrameHeight()) * 0.5f);
|
||||||
|
|
||||||
// Camera selection.
|
|
||||||
if (ImGui::Button(ICON_COPY_CAMERA)) {
|
if (ImGui::Button(ICON_COPY_CAMERA)) {
|
||||||
std::string camera_string = platform::CameraToString(data(), &camera_);
|
std::string camera_string = platform::CameraToString(data(), &camera_);
|
||||||
platform::MaybeSaveToClipboard(camera_string);
|
platform::MaybeSaveToClipboard(camera_string);
|
||||||
}
|
}
|
||||||
ImGui::SetItemTooltip("%s", "Copy Camera");
|
ImGui::SetItemTooltip("%s", "Copy Camera");
|
||||||
|
|
||||||
ImGui::SameLine(0, 0);
|
ImGui::SameLine(0, 0);
|
||||||
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
|
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
|
||||||
int camera_idx = ui_.camera_idx - platform::kTumbleCameraIdx;
|
platform::CameraSelectionGui(model(), data(), camera_, ui_.camera_idx);
|
||||||
std::vector<const char*> cameras = GetCameraNames();
|
|
||||||
std::string camera_preview =
|
|
||||||
std::string(ICON_CAMERA) + " " + cameras[camera_idx];
|
|
||||||
if (ImGui::BeginCombo("##Camera", camera_preview.c_str(), combo_flags)) {
|
|
||||||
for (int n = 0; n < cameras.size(); n++) {
|
|
||||||
if (ImGui::Selectable(cameras[n], (camera_idx == n))) {
|
|
||||||
ui_.camera_idx = platform::SetCamera(model(), &camera_,
|
|
||||||
n + platform::kTumbleCameraIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
ImGui::SetItemTooltip("%s", "Camera");
|
|
||||||
|
|
||||||
// Label selection.
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
|
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
|
||||||
std::string label_preview =
|
platform::LabelSelectionGui(&vis_options_);
|
||||||
std::string(ICON_LABEL) + " " + kLabelNames[vis_options_.label];
|
|
||||||
if (ImGui::BeginCombo("##Label", label_preview.c_str(), combo_flags)) {
|
|
||||||
for (int n = 0; n < IM_ARRAYSIZE(kLabelNames); n++) {
|
|
||||||
if (ImGui::Selectable(kLabelNames[n], (vis_options_.label == n))) {
|
|
||||||
vis_options_.label = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
ImGui::SetItemTooltip("%s", "Label");
|
|
||||||
|
|
||||||
// Frame selection.
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
|
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
|
||||||
std::string frame_preview =
|
platform::FrameSelectionGui(&vis_options_);
|
||||||
std::string(ICON_FRAME) + " " + kFrameNames[vis_options_.frame];
|
|
||||||
if (ImGui::BeginCombo("##Frame", frame_preview.c_str(), combo_flags)) {
|
|
||||||
for (int n = 0; n < IM_ARRAYSIZE(kFrameNames); n++) {
|
|
||||||
if (ImGui::Selectable(kFrameNames[n], (vis_options_.frame == n))) {
|
|
||||||
vis_options_.frame = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
|
||||||
ImGui::SetItemTooltip("%s", "Frame");
|
|
||||||
|
|
||||||
// Theme selection.
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
const char* theme_icons[] = {ICON_LIGHTMODE, ICON_DARKMODE,
|
ImGui::SetNextItemWidth(GetExpectedLabelWidth());
|
||||||
ICON_CLASSICMODE};
|
if (platform::ThemeSelectGui(&ui_.theme)) {
|
||||||
const char* theme_tooltips[] = {"Light Mode", "Dark Mode", "Classic Mode"};
|
platform::SetupTheme(ui_.theme);
|
||||||
const platform::GuiTheme theme_values[] = {
|
ImGui::GetIO().WantSaveIniSettings = true;
|
||||||
platform::GuiTheme::kLight,
|
|
||||||
platform::GuiTheme::kDark,
|
|
||||||
platform::GuiTheme::kClassic,
|
|
||||||
};
|
|
||||||
int theme_idx = static_cast<int>(ui_.theme);
|
|
||||||
ImGui::SetNextItemWidth(ImGui::CalcTextSize(theme_icons[0]).x +
|
|
||||||
ImGui::GetStyle().FramePadding.x * 2);
|
|
||||||
if (ImGui::BeginCombo("##Theme", theme_icons[theme_idx], combo_flags)) {
|
|
||||||
for (int n = 0; n < IM_ARRAYSIZE(theme_icons); n++) {
|
|
||||||
if (ImGui::Selectable(theme_icons[n], (theme_idx == n))) {
|
|
||||||
SetupTheme(theme_values[n]);
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
ImGui::SetTooltip("%s", theme_tooltips[n]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndCombo();
|
|
||||||
}
|
}
|
||||||
ImGui::SetItemTooltip("%s", "Theme");
|
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
@@ -2011,34 +1936,6 @@ float App::GetExpectedLabelWidth() {
|
|||||||
return tmp_.expected_label_width;
|
return tmp_.expected_label_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const char*> App::GetCameraNames() {
|
|
||||||
if (tmp_.camera_names.empty()) {
|
|
||||||
tmp_.camera_names.reserve(model()->ncam + 3);
|
|
||||||
|
|
||||||
tmp_.camera_names.push_back("Free: tumble");
|
|
||||||
tmp_.camera_names.push_back("Free: wasd");
|
|
||||||
tmp_.camera_names.push_back("Tracking (-1)");
|
|
||||||
for (int i = 0; i < model()->ncam; i++) {
|
|
||||||
if (model()->names[model()->name_camadr[i]]) {
|
|
||||||
tmp_.camera_names.push_back(model()->names + model()->name_camadr[i]);
|
|
||||||
} else {
|
|
||||||
tmp_.camera_names.push_back("Unnamed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update tracking camera name as this can change over time.
|
|
||||||
tmp_.camera_names[2] =
|
|
||||||
"Tracking (" + std::to_string(camera_.trackbodyid) + ")";
|
|
||||||
|
|
||||||
std::vector<const char*> names;
|
|
||||||
names.reserve(tmp_.camera_names.size());
|
|
||||||
for (const auto& name : tmp_.camera_names) {
|
|
||||||
names.push_back(name.c_str());
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
App::UiState::Dict App::UiState::ToDict() const {
|
App::UiState::Dict App::UiState::ToDict() const {
|
||||||
return {
|
return {
|
||||||
{"theme", std::to_string(static_cast<int>(theme))},
|
{"theme", std::to_string(static_cast<int>(theme))},
|
||||||
|
|||||||
@@ -223,7 +223,6 @@ class App {
|
|||||||
void SpecEditorGui();
|
void SpecEditorGui();
|
||||||
|
|
||||||
float GetExpectedLabelWidth();
|
float GetExpectedLabelWidth();
|
||||||
std::vector<const char*> GetCameraNames();
|
|
||||||
|
|
||||||
mjSpec* spec() { return model_holder_->spec(); }
|
mjSpec* spec() { return model_holder_->spec(); }
|
||||||
mjModel* model() { return model_holder_->model(); }
|
mjModel* model() { return model_holder_->model(); }
|
||||||
|
|||||||
@@ -1185,7 +1185,7 @@ bool mjCFlexcomp::MakeMesh(mjCModel* model, mjsCompiler* compiler, char* error,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy vertices
|
// copy vertices
|
||||||
point = mesh.Vert();
|
point.assign(mesh.Vert().begin(), mesh.Vert().end());
|
||||||
|
|
||||||
if (mesh.HasTexcoord()) {
|
if (mesh.HasTexcoord()) {
|
||||||
texcoord = mesh.Texcoord();
|
texcoord = mesh.Texcoord();
|
||||||
|
|||||||
+122
-115
@@ -492,7 +492,7 @@ void mjCMesh::CacheMesh(mjCCache* cache, const mjResource* resource) {
|
|||||||
|
|
||||||
// calculate estimated size of mesh
|
// calculate estimated size of mesh
|
||||||
std::size_t size = sizeof(mjCMesh)
|
std::size_t size = sizeof(mjCMesh)
|
||||||
+ (sizeof(double) * vert_.size())
|
+ (sizeof(float) * vert_.size())
|
||||||
+ (sizeof(float) * normal_.size())
|
+ (sizeof(float) * normal_.size())
|
||||||
+ (sizeof(float) * texcoord_.size())
|
+ (sizeof(float) * texcoord_.size())
|
||||||
+ (sizeof(int) * face_.size())
|
+ (sizeof(int) * face_.size())
|
||||||
@@ -538,7 +538,7 @@ struct VertexKey {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// convert vertices to double precision and remove repeated vertices if requested
|
// process and remove repeated vertices if requested
|
||||||
void mjCMesh::ProcessVertices(const std::vector<float>& vert, bool remove_repeated) {
|
void mjCMesh::ProcessVertices(const std::vector<float>& vert, bool remove_repeated) {
|
||||||
vert_.clear();
|
vert_.clear();
|
||||||
int nvert = vert.size();
|
int nvert = vert.size();
|
||||||
@@ -647,19 +647,6 @@ void mjCMesh::LoadFromDecoder(mjResource* resource, bool remove_repeated) {
|
|||||||
facenormal_.assign(src_mesh->userfacenormal->begin(), src_mesh->userfacenormal->end());
|
facenormal_.assign(src_mesh->userfacenormal->begin(), src_mesh->userfacenormal->end());
|
||||||
facetexcoord_.assign(src_mesh->userfacetexcoord->begin(), src_mesh->userfacetexcoord->end());
|
facetexcoord_.assign(src_mesh->userfacetexcoord->begin(), src_mesh->userfacetexcoord->end());
|
||||||
|
|
||||||
// correct winding order for left-handed coordinate systems
|
|
||||||
bool righthand = scale[0] * scale[1] * scale[2] > 0;
|
|
||||||
if (!righthand) {
|
|
||||||
for (size_t i = 0; i < face_.size(); i += 3) {
|
|
||||||
std::swap(face_[i + 1], face_[i + 2]);
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < facenormal_.size(); i += 3) {
|
|
||||||
std::swap(facenormal_[i + 1], facenormal_[i + 2]);
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < facetexcoord_.size(); i += 3) {
|
|
||||||
std::swap(facetexcoord_[i + 1], facetexcoord_[i + 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> vert(src_mesh->uservert->begin(), src_mesh->uservert->end());
|
std::vector<float> vert(src_mesh->uservert->begin(), src_mesh->uservert->end());
|
||||||
mj_deleteSpec(mesh_spec);
|
mj_deleteSpec(mesh_spec);
|
||||||
@@ -788,24 +775,24 @@ void mjCMesh::TryCompile(const mjVFS* vfs) {
|
|||||||
// compute mesh properties
|
// compute mesh properties
|
||||||
if (!fromCache) {
|
if (!fromCache) {
|
||||||
Process();
|
Process();
|
||||||
}
|
|
||||||
|
|
||||||
// make octree
|
if (!file_.empty()) {
|
||||||
if (!needsdf) {
|
CacheMesh(cache, resource_);
|
||||||
octree_.Clear(); // this occurs when a non-SDF mesh is loaded from a cached SDF mesh
|
}
|
||||||
} else if (octree_.NumNodes() == 0) {
|
} else {
|
||||||
octree_.SetFace(vert_, face_);
|
// When a mesh is loaded from the cache, has no octree but needs one,
|
||||||
octree_.CreateOctree(aamm_);
|
// we need to compute it here. If inversely it has an octree but we *do not*
|
||||||
|
// need one, we clear it.
|
||||||
// compute sdf coefficients
|
if (!needsdf) {
|
||||||
if (!plugin.active) {
|
octree_.Clear();
|
||||||
octree_.ComputeSdfCoeffs(vert_.data(), nvert(), face_.data(), nface(), tree_);
|
} else if (octree_.NumNodes() == 0) {
|
||||||
|
std::vector<double> dvert(vert_.begin(), vert_.end());
|
||||||
|
octree_.SetFace(dvert, face_);
|
||||||
|
octree_.CreateOctree(aamm_);
|
||||||
|
if (!plugin.active) {
|
||||||
|
octree_.ComputeSdfCoeffs(dvert.data(), nvert(), face_.data(), nface(), tree_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// cache mesh
|
|
||||||
if (!fromCache && !file_.empty()) {
|
|
||||||
CacheMesh(cache, resource_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// close resource
|
// close resource
|
||||||
@@ -818,18 +805,18 @@ void mjCMesh::TryCompile(const mjVFS* vfs) {
|
|||||||
|
|
||||||
|
|
||||||
// get bounding volume
|
// get bounding volume
|
||||||
void mjCMesh::SetBoundingVolume(int faceid) {
|
void mjCMesh::SetBoundingVolume(int faceid, const double* dvert) {
|
||||||
constexpr double kMaxVal = std::numeric_limits<double>::max();
|
constexpr double kMaxVal = std::numeric_limits<double>::max();
|
||||||
double face_aamm[6] = {kMaxVal, kMaxVal, kMaxVal, -kMaxVal, -kMaxVal, -kMaxVal};
|
double face_aamm[6] = {kMaxVal, kMaxVal, kMaxVal, -kMaxVal, -kMaxVal, -kMaxVal};
|
||||||
|
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
int vertid = face_[3*faceid + j];
|
int vertid = face_[3*faceid + j];
|
||||||
face_aamm[0] = std::min(face_aamm[0], vert_[3*vertid + 0]);
|
face_aamm[0] = std::min(face_aamm[0], dvert[3*vertid + 0]);
|
||||||
face_aamm[1] = std::min(face_aamm[1], vert_[3*vertid + 1]);
|
face_aamm[1] = std::min(face_aamm[1], dvert[3*vertid + 1]);
|
||||||
face_aamm[2] = std::min(face_aamm[2], vert_[3*vertid + 2]);
|
face_aamm[2] = std::min(face_aamm[2], dvert[3*vertid + 2]);
|
||||||
face_aamm[3] = std::max(face_aamm[3], vert_[3*vertid + 0]);
|
face_aamm[3] = std::max(face_aamm[3], dvert[3*vertid + 0]);
|
||||||
face_aamm[4] = std::max(face_aamm[4], vert_[3*vertid + 1]);
|
face_aamm[4] = std::max(face_aamm[4], dvert[3*vertid + 1]);
|
||||||
face_aamm[5] = std::max(face_aamm[5], vert_[3*vertid + 2]);
|
face_aamm[5] = std::max(face_aamm[5], dvert[3*vertid + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
face_aabb_.push_back(.5 * (face_aamm[0] + face_aamm[3]));
|
face_aabb_.push_back(.5 * (face_aamm[0] + face_aamm[3]));
|
||||||
@@ -864,9 +851,7 @@ bool mjCMesh::HasTexcoord() const {
|
|||||||
|
|
||||||
|
|
||||||
void mjCMesh::CopyVert(float* arr) const {
|
void mjCMesh::CopyVert(float* arr) const {
|
||||||
for (int i = 0; i < vert_.size(); ++i) {
|
std::copy(vert_.begin(), vert_.end(), arr);
|
||||||
arr[i] = (float)vert_[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1200,16 +1185,15 @@ void mjCMesh::LoadMSH(mjResource* resource, bool remove_repeated) {
|
|||||||
|
|
||||||
|
|
||||||
// compute the volume and center-of-mass of the mesh given the face centroid
|
// compute the volume and center-of-mass of the mesh given the face centroid
|
||||||
double mjCMesh::ComputeVolume(double CoM[3], const double facecen[3]) const {
|
double mjCMesh::ComputeVolume(double CoM[3], const double facecen[3], const double* dvert) const {
|
||||||
double normal[3], center[3], total_volume = 0;
|
double normal[3], center[3], total_volume = 0;
|
||||||
CoM[0] = CoM[1] = CoM[2] = 0;
|
CoM[0] = CoM[1] = CoM[2] = 0;
|
||||||
int nf = (inertia == mjMESH_INERTIA_CONVEX) ? graph_[1] : nface();
|
int nf = (inertia == mjMESH_INERTIA_CONVEX) ? graph_[1] : nface();
|
||||||
const int* f = (inertia == mjMESH_INERTIA_CONVEX) ? GraphFaces() : face_.data();
|
const int* f = (inertia == mjMESH_INERTIA_CONVEX) ? GraphFaces() : face_.data();
|
||||||
|
|
||||||
for (int i = 0; i < nf; i++) {
|
for (int i = 0; i < nf; i++) {
|
||||||
// get area, normal and center
|
double area = triangle(normal, center, &dvert[3*f[3*i]], &dvert[3*f[3*i + 1]],
|
||||||
double area = triangle(normal, center, &vert_[3*f[3*i]], &vert_[3*f[3*i + 1]],
|
&dvert[3*f[3*i + 2]]);
|
||||||
&vert_[3*f[3*i + 2]]);
|
|
||||||
|
|
||||||
// compute and add volume
|
// compute and add volume
|
||||||
double vec[3] = {center[0] - facecen[0], center[1] - facecen[1], center[2] - facecen[2]};
|
double vec[3] = {center[0] - facecen[0], center[1] - facecen[1], center[2] - facecen[2]};
|
||||||
@@ -1239,14 +1223,13 @@ double mjCMesh::ComputeVolume(double CoM[3], const double facecen[3]) const {
|
|||||||
|
|
||||||
|
|
||||||
// compute the surface area and center-of-mass of the mesh given the face centroid
|
// compute the surface area and center-of-mass of the mesh given the face centroid
|
||||||
double mjCMesh::ComputeSurfaceArea(double CoM[3], const double facecen[3]) const {
|
double mjCMesh::ComputeSurfaceArea(double CoM[3], const double facecen[3], const double* dvert) const {
|
||||||
double surface = 0;
|
double surface = 0;
|
||||||
CoM[0] = CoM[1] = CoM[2] = 0;
|
CoM[0] = CoM[1] = CoM[2] = 0;
|
||||||
for (int i = 0; i < nface(); i++) {
|
for (int i = 0; i < nface(); i++) {
|
||||||
// get area and center
|
|
||||||
double area, center[3];
|
double area, center[3];
|
||||||
area = triangle(nullptr, center, &vert_[3*face_[3*i]],
|
area = triangle(nullptr, center, &dvert[3*face_[3*i]],
|
||||||
&vert_[3*face_[3*i + 1]], &vert_[3*face_[3*i + 2]]);
|
&dvert[3*face_[3*i + 1]], &dvert[3*face_[3*i + 2]]);
|
||||||
|
|
||||||
// add pyramid com
|
// add pyramid com
|
||||||
surface += area;
|
surface += area;
|
||||||
@@ -1267,28 +1250,24 @@ double mjCMesh::ComputeSurfaceArea(double CoM[3], const double facecen[3]) const
|
|||||||
|
|
||||||
|
|
||||||
// apply transformations
|
// apply transformations
|
||||||
void mjCMesh::ApplyTransformations() {
|
void mjCMesh::ApplyTransformations(double* dvert) {
|
||||||
// translate
|
|
||||||
if (refpos[0] != 0 || refpos[1] != 0 || refpos[2] != 0) {
|
if (refpos[0] != 0 || refpos[1] != 0 || refpos[2] != 0) {
|
||||||
int nv = nvert();
|
int nv = nvert();
|
||||||
for (int i = 0; i < nv; i++) {
|
for (int i = 0; i < nv; i++) {
|
||||||
vert_[3*i + 0] -= refpos[0];
|
dvert[3*i + 0] -= refpos[0];
|
||||||
vert_[3*i + 1] -= refpos[1];
|
dvert[3*i + 1] -= refpos[1];
|
||||||
vert_[3*i + 2] -= refpos[2];
|
dvert[3*i + 2] -= refpos[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate
|
|
||||||
if (refquat[0] != 1 || refquat[1] != 0 || refquat[2] != 0 || refquat[3] != 0) {
|
if (refquat[0] != 1 || refquat[1] != 0 || refquat[2] != 0 || refquat[3] != 0) {
|
||||||
// prepare rotation
|
|
||||||
double quat[4] = {refquat[0], refquat[1], refquat[2], refquat[3]};
|
double quat[4] = {refquat[0], refquat[1], refquat[2], refquat[3]};
|
||||||
double mat[9];
|
double mat[9];
|
||||||
mjuu_normvec(quat, 4);
|
mjuu_normvec(quat, 4);
|
||||||
mjuu_quat2mat(mat, quat);
|
mjuu_quat2mat(mat, quat);
|
||||||
|
|
||||||
// process vertices
|
|
||||||
for (int i = 0; i < nvert(); i++) {
|
for (int i = 0; i < nvert(); i++) {
|
||||||
mjuu_mulvecmatT(&vert_[3*i], &vert_[3*i], mat);
|
mjuu_mulvecmatT(&dvert[3*i], &dvert[3*i], mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process normals
|
// process normals
|
||||||
@@ -1304,9 +1283,9 @@ void mjCMesh::ApplyTransformations() {
|
|||||||
// scale
|
// scale
|
||||||
if (scale[0] != 1 || scale[1] != 1 || scale[2] != 1) {
|
if (scale[0] != 1 || scale[1] != 1 || scale[2] != 1) {
|
||||||
for (int i = 0; i < nvert(); i++) {
|
for (int i = 0; i < nvert(); i++) {
|
||||||
vert_[3*i + 0] *= scale[0];
|
dvert[3*i + 0] *= scale[0];
|
||||||
vert_[3*i + 1] *= scale[1];
|
dvert[3*i + 1] *= scale[1];
|
||||||
vert_[3*i + 2] *= scale[2];
|
dvert[3*i + 2] *= scale[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < nnormal(); i++) {
|
for (int i = 0; i < nnormal(); i++) {
|
||||||
@@ -1338,14 +1317,13 @@ void mjCMesh::ApplyTransformations() {
|
|||||||
|
|
||||||
|
|
||||||
// find centroid of faces, return total area
|
// find centroid of faces, return total area
|
||||||
double mjCMesh::ComputeFaceCentroid(double facecen[3]) const {
|
double mjCMesh::ComputeFaceCentroid(double facecen[3], const double* dvert) const {
|
||||||
double total_area = 0;
|
double total_area = 0;
|
||||||
|
|
||||||
for (int i = 0; i < nface(); i++) {
|
for (int i = 0; i < nface(); i++) {
|
||||||
// get area and center
|
|
||||||
double area, center[3];
|
double area, center[3];
|
||||||
area = triangle(nullptr, center, &vert_[3*face_[3*i]],
|
area = triangle(nullptr, center, &dvert[3*face_[3*i]],
|
||||||
&vert_[3*face_[3*i + 1]], &vert_[3*face_[3*i + 2]]);
|
&dvert[3*face_[3*i + 1]], &dvert[3*face_[3*i + 2]]);
|
||||||
|
|
||||||
// accumulate
|
// accumulate
|
||||||
facecen[0] += area * center[0];
|
facecen[0] += area * center[0];
|
||||||
@@ -1366,13 +1344,15 @@ double mjCMesh::ComputeFaceCentroid(double facecen[3]) const {
|
|||||||
|
|
||||||
|
|
||||||
void mjCMesh::Process() {
|
void mjCMesh::Process() {
|
||||||
|
std::vector<double> dvert(vert_.begin(), vert_.end());
|
||||||
|
|
||||||
// create half-edge structure (if mesh was in XML)
|
// create half-edge structure (if mesh was in XML)
|
||||||
if (halfedge_.empty()) {
|
if (halfedge_.empty()) {
|
||||||
for (int i = 0; i < nface(); i++) {
|
for (int i = 0; i < nface(); i++) {
|
||||||
int v0 = face_[3*i + 0];
|
int v0 = face_[3*i + 0];
|
||||||
int v1 = face_[3*i + 1];
|
int v1 = face_[3*i + 1];
|
||||||
int v2 = face_[3*i + 2];
|
int v2 = face_[3*i + 2];
|
||||||
if (triangle(nullptr, nullptr, &vert_[3*v0], &vert_[3*v1], &vert_[3*v2]) > sqrt(mjMINVAL)) {
|
if (triangle(nullptr, nullptr, &dvert[3*v0], &dvert[3*v1], &dvert[3*v2]) > sqrt(mjMINVAL)) {
|
||||||
halfedge_.push_back({v0, v1});
|
halfedge_.push_back({v0, v1});
|
||||||
halfedge_.push_back({v1, v2});
|
halfedge_.push_back({v1, v2});
|
||||||
halfedge_.push_back({v2, v0});
|
halfedge_.push_back({v2, v0});
|
||||||
@@ -1396,7 +1376,7 @@ void mjCMesh::Process() {
|
|||||||
|
|
||||||
// make graph describing convex hull
|
// make graph describing convex hull
|
||||||
if (needhull_ || face_.empty()) {
|
if (needhull_ || face_.empty()) {
|
||||||
MakeGraph();
|
MakeGraph(dvert.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// no faces: copy from convex hull
|
// no faces: copy from convex hull
|
||||||
@@ -1406,7 +1386,7 @@ void mjCMesh::Process() {
|
|||||||
|
|
||||||
// no normals: make
|
// no normals: make
|
||||||
if (normal_.empty()) {
|
if (normal_.empty()) {
|
||||||
MakeNormal();
|
MakeNormal(dvert.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check facenormal size
|
// check facenormal size
|
||||||
@@ -1429,17 +1409,31 @@ void mjCMesh::Process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (szgraph_) {
|
if (szgraph_) {
|
||||||
MakePolygons();
|
MakePolygons(dvert.data());
|
||||||
} else {
|
} else {
|
||||||
polygon_map_.resize(nvert());
|
polygon_map_.resize(nvert());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// correct winding order for left-handed coordinate systems
|
||||||
|
bool righthand = scale[0] * scale[1] * scale[2] > 0;
|
||||||
|
if (!righthand) {
|
||||||
|
for (size_t i = 0; i < face_.size(); i += 3) {
|
||||||
|
std::swap(face_[i + 1], face_[i + 2]);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < facenormal_.size(); i += 3) {
|
||||||
|
std::swap(facenormal_[i + 1], facenormal_[i + 2]);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < facetexcoord_.size(); i += 3) {
|
||||||
|
std::swap(facetexcoord_[i + 1], facetexcoord_[i + 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// user offset, rotation, scaling
|
// user offset, rotation, scaling
|
||||||
ApplyTransformations();
|
ApplyTransformations(dvert.data());
|
||||||
|
|
||||||
// find centroid of faces
|
// find centroid of faces
|
||||||
double facecen[3] = {0, 0, 0};
|
double facecen[3] = {0, 0, 0};
|
||||||
if (ComputeFaceCentroid(facecen) < mjMINVAL) {
|
if (ComputeFaceCentroid(facecen, dvert.data()) < mjMINVAL) {
|
||||||
throw mjCError(this, "mesh surface area is too small: %s", name.c_str());
|
throw mjCError(this, "mesh surface area is too small: %s", name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1450,12 +1444,12 @@ void mjCMesh::Process() {
|
|||||||
|
|
||||||
// compute CoM and volume/area
|
// compute CoM and volume/area
|
||||||
if (inertia == mjMESH_INERTIA_SHELL) {
|
if (inertia == mjMESH_INERTIA_SHELL) {
|
||||||
surface_ = ComputeSurfaceArea(CoM, facecen);
|
surface_ = ComputeSurfaceArea(CoM, facecen, dvert.data());
|
||||||
if (surface_ < mjMINVAL) {
|
if (surface_ < mjMINVAL) {
|
||||||
throw mjCError(this, "mesh surface area is too small: %s", name.c_str());
|
throw mjCError(this, "mesh surface area is too small: %s", name.c_str());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((volume_ = ComputeVolume(CoM, facecen)) < mjMINVAL) {
|
if ((volume_ = ComputeVolume(CoM, facecen, dvert.data())) < mjMINVAL) {
|
||||||
if (volume_ < 0) {
|
if (volume_ < 0) {
|
||||||
throw mjCError(this, "mesh volume is negative (misoriented triangles): %s", name.c_str());
|
throw mjCError(this, "mesh volume is negative (misoriented triangles): %s", name.c_str());
|
||||||
} else {
|
} else {
|
||||||
@@ -1466,7 +1460,7 @@ void mjCMesh::Process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compute inertia
|
// compute inertia
|
||||||
double total_volume = ComputeInertia(inert, CoM);
|
double total_volume = ComputeInertia(inert, CoM, dvert.data());
|
||||||
if (inertia == mjMESH_INERTIA_SHELL) {
|
if (inertia == mjMESH_INERTIA_SHELL) {
|
||||||
surface_ = total_volume;
|
surface_ = total_volume;
|
||||||
} else {
|
} else {
|
||||||
@@ -1510,11 +1504,11 @@ void mjCMesh::Process() {
|
|||||||
|
|
||||||
// transform CoM to origin
|
// transform CoM to origin
|
||||||
for (int i=0; i < nvert(); i++) {
|
for (int i=0; i < nvert(); i++) {
|
||||||
vert_[3*i + 0] -= CoM[0];
|
dvert[3*i + 0] -= CoM[0];
|
||||||
vert_[3*i + 1] -= CoM[1];
|
dvert[3*i + 1] -= CoM[1];
|
||||||
vert_[3*i + 2] -= CoM[2];
|
dvert[3*i + 2] -= CoM[2];
|
||||||
}
|
}
|
||||||
Rotate(quattmp);
|
Rotate(quattmp, dvert.data());
|
||||||
|
|
||||||
// save the pos and quat that was used to transform the mesh
|
// save the pos and quat that was used to transform the mesh
|
||||||
mjuu_copyvec(pos_, CoM, 3);
|
mjuu_copyvec(pos_, CoM, 3);
|
||||||
@@ -1524,11 +1518,11 @@ void mjCMesh::Process() {
|
|||||||
|
|
||||||
// no radii: make
|
// no radii: make
|
||||||
if (!center_) {
|
if (!center_) {
|
||||||
MakeCenter();
|
MakeCenter(dvert.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// recompute polygon normals
|
// recompute polygon normals
|
||||||
MakePolygonNormals();
|
MakePolygonNormals(dvert.data());
|
||||||
|
|
||||||
// make bounding volume hierarchy
|
// make bounding volume hierarchy
|
||||||
if (tree_.Bvh().empty()) {
|
if (tree_.Bvh().empty()) {
|
||||||
@@ -1536,16 +1530,31 @@ void mjCMesh::Process() {
|
|||||||
face_aabb_.reserve(3*face_.size());
|
face_aabb_.reserve(3*face_.size());
|
||||||
tree_.AllocateBoundingVolumes(nface());
|
tree_.AllocateBoundingVolumes(nface());
|
||||||
for (int i = 0; i < nface(); i++) {
|
for (int i = 0; i < nface(); i++) {
|
||||||
SetBoundingVolume(i);
|
SetBoundingVolume(i, dvert.data());
|
||||||
}
|
}
|
||||||
tree_.CreateBVH();
|
tree_.CreateBVH();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make octree
|
||||||
|
if (needsdf) {
|
||||||
|
octree_.SetFace(dvert, face_);
|
||||||
|
octree_.CreateOctree(aamm_);
|
||||||
|
|
||||||
|
if (!plugin.active) {
|
||||||
|
octree_.ComputeSdfCoeffs(dvert.data(), nvert(), face_.data(), nface(), tree_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// narrow back to float
|
||||||
|
for (int i = 0; i < (int)dvert.size(); i++) {
|
||||||
|
vert_[i] = (float)dvert[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// compute abstract (unitless) inertia, recompute area / volume
|
// compute abstract (unitless) inertia, recompute area / volume
|
||||||
double mjCMesh::ComputeInertia(double inert[6], const double CoM[3]) const {
|
double mjCMesh::ComputeInertia(double inert[6], const double CoM[3], const double* dvert) const {
|
||||||
double total_volume = 0;
|
double total_volume = 0;
|
||||||
|
|
||||||
// copy vertices to avoid modifying the original mesh
|
// copy vertices to avoid modifying the original mesh
|
||||||
@@ -1554,9 +1563,9 @@ double mjCMesh::ComputeInertia(double inert[6], const double CoM[3]) const {
|
|||||||
|
|
||||||
// translate vertices to origin in order to compute inertia
|
// translate vertices to origin in order to compute inertia
|
||||||
for (int i = 0; i < nvert(); i++) {
|
for (int i = 0; i < nvert(); i++) {
|
||||||
vert_centered.push_back(vert_[3*i + 0] - CoM[0]);
|
vert_centered.push_back(dvert[3*i + 0] - CoM[0]);
|
||||||
vert_centered.push_back(vert_[3*i + 1] - CoM[1]);
|
vert_centered.push_back(dvert[3*i + 1] - CoM[1]);
|
||||||
vert_centered.push_back(vert_[3*i + 2] - CoM[2]);
|
vert_centered.push_back(dvert[3*i + 2] - CoM[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// accumulate products of inertia, recompute volume
|
// accumulate products of inertia, recompute volume
|
||||||
@@ -1611,22 +1620,20 @@ double mjCMesh::ComputeInertia(double inert[6], const double CoM[3]) const {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void mjCMesh::Rotate(double quat[4]) {
|
void mjCMesh::Rotate(double quat[4], double* dvert) {
|
||||||
// rotate vertices and normals of mesh by quaternion
|
|
||||||
double neg[4] = {quat[0], -quat[1], -quat[2], -quat[3]};
|
double neg[4] = {quat[0], -quat[1], -quat[2], -quat[3]};
|
||||||
double mat[9];
|
double mat[9];
|
||||||
mjuu_quat2mat(mat, neg);
|
mjuu_quat2mat(mat, neg);
|
||||||
int nv = nvert();
|
int nv = nvert();
|
||||||
for (int i = 0; i < nv; i++) {
|
for (int i = 0; i < nv; i++) {
|
||||||
mjuu_mulvecmat(&vert_[3*i], &vert_[3*i], mat);
|
mjuu_mulvecmat(&dvert[3*i], &dvert[3*i], mat);
|
||||||
|
|
||||||
// axis-aligned bounding box
|
aamm_[0] = std::min(aamm_[0], dvert[3*i + 0]);
|
||||||
aamm_[0] = std::min(aamm_[0], vert_[3*i + 0]);
|
aamm_[3] = std::max(aamm_[3], dvert[3*i + 0]);
|
||||||
aamm_[3] = std::max(aamm_[3], vert_[3*i + 0]);
|
aamm_[1] = std::min(aamm_[1], dvert[3*i + 1]);
|
||||||
aamm_[1] = std::min(aamm_[1], vert_[3*i + 1]);
|
aamm_[4] = std::max(aamm_[4], dvert[3*i + 1]);
|
||||||
aamm_[4] = std::max(aamm_[4], vert_[3*i + 1]);
|
aamm_[2] = std::min(aamm_[2], dvert[3*i + 2]);
|
||||||
aamm_[2] = std::min(aamm_[2], vert_[3*i + 2]);
|
aamm_[5] = std::max(aamm_[5], dvert[3*i + 2]);
|
||||||
aamm_[5] = std::max(aamm_[5], vert_[3*i + 2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i < nnormal(); i++) {
|
for (int i=0; i < nnormal(); i++) {
|
||||||
@@ -1698,7 +1705,7 @@ double mjCMesh::GetVolumeRef() const {
|
|||||||
|
|
||||||
|
|
||||||
// make graph describing convex hull
|
// make graph describing convex hull
|
||||||
void mjCMesh::MakeGraph() {
|
void mjCMesh::MakeGraph(const double* dvert) {
|
||||||
int adr, ok, curlong, totlong, exitcode;
|
int adr, ok, curlong, totlong, exitcode;
|
||||||
facetT* facet, **facetp;
|
facetT* facet, **facetp;
|
||||||
vertexT* vertex, *vertex1, **vertex1p;
|
vertexT* vertex, *vertex1, **vertex1p;
|
||||||
@@ -1727,7 +1734,7 @@ void mjCMesh::MakeGraph() {
|
|||||||
if (!exitcode) {
|
if (!exitcode) {
|
||||||
// actual init
|
// actual init
|
||||||
qh_initflags(qh, const_cast<char*>(qhopt.c_str()));
|
qh_initflags(qh, const_cast<char*>(qhopt.c_str()));
|
||||||
qh_init_B(qh, vert_.data(), nvert(), 3, qh_False);
|
qh_init_B(qh, const_cast<double*>(dvert), nvert(), 3, qh_False);
|
||||||
|
|
||||||
// construct convex hull
|
// construct convex hull
|
||||||
qh_qhull(qh);
|
qh_qhull(qh);
|
||||||
@@ -2402,7 +2409,7 @@ void mjCMesh::MakeCone(int nedge, double radius) {
|
|||||||
|
|
||||||
|
|
||||||
// compute vertex normals
|
// compute vertex normals
|
||||||
void mjCMesh::MakeNormal() {
|
void mjCMesh::MakeNormal(const double* dvert) {
|
||||||
// only if normal data is missing
|
// only if normal data is missing
|
||||||
if (!normal_.empty()) {
|
if (!normal_.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -2426,8 +2433,8 @@ void mjCMesh::MakeNormal() {
|
|||||||
// get triangle edges
|
// get triangle edges
|
||||||
double vec01[3], vec02[3];
|
double vec01[3], vec02[3];
|
||||||
for (int j=0; j < 3; j++) {
|
for (int j=0; j < 3; j++) {
|
||||||
vec01[j] = vert_[3*vertid[1]+j] - vert_[3*vertid[0]+j];
|
vec01[j] = dvert[3*vertid[1]+j] - dvert[3*vertid[0]+j];
|
||||||
vec02[j] = vert_[3*vertid[2]+j] - vert_[3*vertid[0]+j];
|
vec02[j] = dvert[3*vertid[2]+j] - dvert[3*vertid[0]+j];
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute face normal
|
// compute face normal
|
||||||
@@ -2461,8 +2468,8 @@ void mjCMesh::MakeNormal() {
|
|||||||
// get triangle edges
|
// get triangle edges
|
||||||
double vec01[3], vec02[3];
|
double vec01[3], vec02[3];
|
||||||
for (int j=0; j < 3; j++) {
|
for (int j=0; j < 3; j++) {
|
||||||
vec01[j] = vert_[3*vertid[1]+j] - vert_[3*vertid[0]+j];
|
vec01[j] = dvert[3*vertid[1]+j] - dvert[3*vertid[0]+j];
|
||||||
vec02[j] = vert_[3*vertid[2]+j] - vert_[3*vertid[0]+j];
|
vec02[j] = dvert[3*vertid[2]+j] - dvert[3*vertid[0]+j];
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute face normal
|
// compute face normal
|
||||||
@@ -2514,7 +2521,7 @@ void mjCMesh::MakeNormal() {
|
|||||||
|
|
||||||
|
|
||||||
// compute face circumradii
|
// compute face circumradii
|
||||||
void mjCMesh::MakeCenter() {
|
void mjCMesh::MakeCenter(const double* dvert) {
|
||||||
if (center_) {
|
if (center_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2530,8 +2537,8 @@ void mjCMesh::MakeCenter() {
|
|||||||
// get triangle edges
|
// get triangle edges
|
||||||
double a[3], b[3];
|
double a[3], b[3];
|
||||||
for (int j=0; j < 3; j++) {
|
for (int j=0; j < 3; j++) {
|
||||||
a[j] = vert_[3*vertid[0]+j] - vert_[3*vertid[2]+j];
|
a[j] = dvert[3*vertid[0]+j] - dvert[3*vertid[2]+j];
|
||||||
b[j] = vert_[3*vertid[1]+j] - vert_[3*vertid[2]+j];
|
b[j] = dvert[3*vertid[1]+j] - dvert[3*vertid[2]+j];
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute face normal
|
// compute face normal
|
||||||
@@ -2550,20 +2557,20 @@ void mjCMesh::MakeCenter() {
|
|||||||
norm_a_2 * b[2] - norm_b_2 * a[2]
|
norm_a_2 * b[2] - norm_b_2 * a[2]
|
||||||
};
|
};
|
||||||
mjuu_crossvec(res, vec, nrm);
|
mjuu_crossvec(res, vec, nrm);
|
||||||
center_[3*i+0] = res[0]/(2*area*area) + vert_[3*vertid[2]+0];
|
center_[3*i+0] = res[0]/(2*area*area) + dvert[3*vertid[2]+0];
|
||||||
center_[3*i+1] = res[1]/(2*area*area) + vert_[3*vertid[2]+1];
|
center_[3*i+1] = res[1]/(2*area*area) + dvert[3*vertid[2]+1];
|
||||||
center_[3*i+2] = res[2]/(2*area*area) + vert_[3*vertid[2]+2];
|
center_[3*i+2] = res[2]/(2*area*area) + dvert[3*vertid[2]+2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// compute the normals of the polygons
|
// compute the normals of the polygons
|
||||||
void mjCMesh::MakePolygonNormals() {
|
void mjCMesh::MakePolygonNormals(const double* dvert) {
|
||||||
for (int i = 0; i < polygons_.size(); ++i) {
|
for (int i = 0; i < polygons_.size(); ++i) {
|
||||||
double n[3];
|
double n[3];
|
||||||
mjuu_makenormal(n, &vert_[3*polygons_[i][0]], &vert_[3*polygons_[i][1]],
|
mjuu_makenormal(n, &dvert[3*polygons_[i][0]], &dvert[3*polygons_[i][1]],
|
||||||
&vert_[3*polygons_[i][2]]);
|
&dvert[3*polygons_[i][2]]);
|
||||||
polygon_normals_[3*i + 0] = n[0];
|
polygon_normals_[3*i + 0] = n[0];
|
||||||
polygon_normals_[3*i + 1] = n[1];
|
polygon_normals_[3*i + 1] = n[1];
|
||||||
polygon_normals_[3*i + 2] = n[2];
|
polygon_normals_[3*i + 2] = n[2];
|
||||||
@@ -2802,7 +2809,7 @@ struct PairHash {
|
|||||||
|
|
||||||
|
|
||||||
// merge coplanar mesh triangular faces into polygonal sides to represent the geometry of the mesh
|
// merge coplanar mesh triangular faces into polygonal sides to represent the geometry of the mesh
|
||||||
void mjCMesh::MakePolygons() {
|
void mjCMesh::MakePolygons(const double* dvert) {
|
||||||
constexpr double kAngleTol = 0.01;
|
constexpr double kAngleTol = 0.01;
|
||||||
std::unordered_map<std::pair<double, double>, MeshPolygon, PairHash> mesh_polygons;
|
std::unordered_map<std::pair<double, double>, MeshPolygon, PairHash> mesh_polygons;
|
||||||
polygons_.clear();
|
polygons_.clear();
|
||||||
@@ -2819,9 +2826,9 @@ void mjCMesh::MakePolygons() {
|
|||||||
int vi1 = faces[3*i + 0];
|
int vi1 = faces[3*i + 0];
|
||||||
int vi2 = faces[3*i + 1];
|
int vi2 = faces[3*i + 1];
|
||||||
int vi3 = faces[3*i + 2];
|
int vi3 = faces[3*i + 2];
|
||||||
double* v1 = &vert_[3*vi1];
|
const double* v1 = &dvert[3*vi1];
|
||||||
double* v2 = &vert_[3*vi2];
|
const double* v2 = &dvert[3*vi2];
|
||||||
double* v3 = &vert_[3*vi3];
|
const double* v3 = &dvert[3*vi3];
|
||||||
|
|
||||||
std::pair<double, double> key;
|
std::pair<double, double> key;
|
||||||
if (!MeshPolygonKey(key, v1, v2, v3, kAngleTol)) {
|
if (!MeshPolygonKey(key, v1, v2, v3, kAngleTol)) {
|
||||||
|
|||||||
@@ -587,6 +587,7 @@ void mjCOctree::CopyCoeff(mjtNum* coeff) const {
|
|||||||
|
|
||||||
|
|
||||||
void mjCOctree::SetFace(const std::vector<double>& vert, const std::vector<int>& face) {
|
void mjCOctree::SetFace(const std::vector<double>& vert, const std::vector<int>& face) {
|
||||||
|
face_.reserve(face.size()/3);
|
||||||
for (int i = 0; i < face.size(); i += 3) {
|
for (int i = 0; i < face.size(); i += 3) {
|
||||||
std::array<double, 3> v0 = {vert[3*face[i+0]], vert[3*face[i+0]+1], vert[3*face[i+0]+2]};
|
std::array<double, 3> v0 = {vert[3*face[i+0]], vert[3*face[i+0]+1], vert[3*face[i+0]+2]};
|
||||||
std::array<double, 3> v1 = {vert[3*face[i+1]], vert[3*face[i+1]+1], vert[3*face[i+1]+2]};
|
std::array<double, 3> v1 = {vert[3*face[i+1]], vert[3*face[i+1]+1], vert[3*face[i+1]+2]};
|
||||||
@@ -613,6 +614,8 @@ void mjCOctree::Make(std::vector<Triangle>& elements) {
|
|||||||
|
|
||||||
|
|
||||||
void mjCOctree::CreateOctree(const double aamm[6]) {
|
void mjCOctree::CreateOctree(const double aamm[6]) {
|
||||||
|
Clear();
|
||||||
|
|
||||||
double aabb[6] = {(aamm[0] + aamm[3]) / 2, (aamm[1] + aamm[4]) / 2, (aamm[2] + aamm[5]) / 2,
|
double aabb[6] = {(aamm[0] + aamm[3]) / 2, (aamm[1] + aamm[4]) / 2, (aamm[2] + aamm[5]) / 2,
|
||||||
(aamm[3] - aamm[0]) / 2, (aamm[4] - aamm[1]) / 2, (aamm[5] - aamm[2]) / 2};
|
(aamm[3] - aamm[0]) / 2, (aamm[4] - aamm[1]) / 2, (aamm[5] - aamm[2]) / 2};
|
||||||
double box[6] = {aabb[0] - 1.1 * aabb[3], aabb[1] - 1.1 * aabb[4], aabb[2] - 1.1 * aabb[5],
|
double box[6] = {aabb[0] - 1.1 * aabb[3], aabb[1] - 1.1 * aabb[4], aabb[2] - 1.1 * aabb[5],
|
||||||
|
|||||||
+19
-17
@@ -299,8 +299,11 @@ class mjCOctree : public mjCOctree_ {
|
|||||||
sizeof(Point) * vert_.size();
|
sizeof(Point) * vert_.size();
|
||||||
}
|
}
|
||||||
void Clear() {
|
void Clear() {
|
||||||
|
nnode_ = 0;
|
||||||
|
nvert_ = 0;
|
||||||
node_.clear();
|
node_.clear();
|
||||||
face_.clear();
|
vert_.clear();
|
||||||
|
hang_.clear();
|
||||||
}
|
}
|
||||||
void AddCoeff(int n, int v, double coeff) { node_[n].coeff[v] = coeff; }
|
void AddCoeff(int n, int v, double coeff) { node_[n].coeff[v] = coeff; }
|
||||||
double Coeff(int n, int v) const { return node_[n].coeff[v]; }
|
double Coeff(int n, int v) const { return node_[n].coeff[v]; }
|
||||||
@@ -1071,7 +1074,7 @@ class mjCMesh_ : public mjCBase {
|
|||||||
std::string content_type_ = ""; // content type of file
|
std::string content_type_ = ""; // content type of file
|
||||||
std::string file_; // mesh file
|
std::string file_; // mesh file
|
||||||
mjResource* resource_ = nullptr; // resource for mesh file
|
mjResource* resource_ = nullptr; // resource for mesh file
|
||||||
std::vector<double> vert_; // vertex data
|
std::vector<float> vert_; // vertex data
|
||||||
std::vector<float> normal_; // normal data
|
std::vector<float> normal_; // normal data
|
||||||
std::vector<float> texcoord_; // texcoord data
|
std::vector<float> texcoord_; // texcoord data
|
||||||
std::vector<int> face_; // vertex indices
|
std::vector<int> face_; // vertex indices
|
||||||
@@ -1156,8 +1159,8 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
|||||||
const double* Refquat() const { return refquat; }
|
const double* Refquat() const { return refquat; }
|
||||||
const double* Scale() const { return scale; }
|
const double* Scale() const { return scale; }
|
||||||
bool SmoothNormal() const { return smoothnormal; }
|
bool SmoothNormal() const { return smoothnormal; }
|
||||||
const std::vector<double>& Vert() const { return vert_; }
|
const std::vector<float>& Vert() const { return vert_; }
|
||||||
double Vert(int i) const { return vert_[i]; }
|
float Vert(int i) const { return vert_[i]; }
|
||||||
const std::vector<float>& UserVert() const { return spec_vert_; }
|
const std::vector<float>& UserVert() const { return spec_vert_; }
|
||||||
const std::vector<float>& UserNormal() const { return spec_normal_; }
|
const std::vector<float>& UserNormal() const { return spec_normal_; }
|
||||||
const std::vector<float>& Texcoord() const { return texcoord_; }
|
const std::vector<float>& Texcoord() const { return texcoord_; }
|
||||||
@@ -1233,7 +1236,7 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
|||||||
void CopyPolygonNormals(mjtNum* arr);
|
void CopyPolygonNormals(mjtNum* arr);
|
||||||
|
|
||||||
// sets properties of a bounding volume given a face id
|
// sets properties of a bounding volume given a face id
|
||||||
void SetBoundingVolume(int faceid);
|
void SetBoundingVolume(int faceid, const double* dvert);
|
||||||
|
|
||||||
// load from OBJ, STL, or MSH file; throws mjCError on failure
|
// load from OBJ, STL, or MSH file; throws mjCError on failure
|
||||||
void LoadFromResource(mjResource* resource, bool remove_repeated = false);
|
void LoadFromResource(mjResource* resource, bool remove_repeated = false);
|
||||||
@@ -1264,22 +1267,22 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
|||||||
void LoadMSH(mjResource* resource, bool remove_repeated); // load mesh in MSH BIN format
|
void LoadMSH(mjResource* resource, bool remove_repeated); // load mesh in MSH BIN format
|
||||||
|
|
||||||
void LoadSDF(); // generate mesh using marching cubes
|
void LoadSDF(); // generate mesh using marching cubes
|
||||||
void MakeGraph(); // make graph of convex hull
|
void MakeGraph(const double* dvert); // make graph of convex hull
|
||||||
void CopyGraph(); // copy graph into face data
|
void CopyGraph(); // copy graph into face data
|
||||||
void MakeNormal(); // compute vertex normals
|
void MakeNormal(const double* dvert); // compute vertex normals
|
||||||
void MakeCenter(); // compute face circumcircle data
|
void MakeCenter(const double* dvert); // compute face circumcircle data
|
||||||
void Process(); // compute inertial properties
|
void Process(); // compute inertial properties
|
||||||
void ApplyTransformations(); // apply user transformations
|
void ApplyTransformations(double* dvert); // apply user transformations
|
||||||
double ComputeFaceCentroid(double[3]) const; // compute centroid of all faces
|
double ComputeFaceCentroid(double[3], const double* dvert) const;
|
||||||
void CheckInitialMesh() const; // check if initial mesh is valid
|
void CheckInitialMesh() const; // check if initial mesh is valid
|
||||||
void CopyPlugin();
|
void CopyPlugin();
|
||||||
void Rotate(double quat[4]); // rotate mesh by quaternion
|
void Rotate(double quat[4], double* dvert); // rotate mesh by quaternion
|
||||||
void Transform(double pos[3], double quat[4]); // transform mesh by position and quaternion
|
void Transform(double pos[3], double quat[4]); // transform mesh by position and quaternion
|
||||||
void MakePolygons(); // compute the polygon sides of the mesh
|
void MakePolygons(const double* dvert); // compute the polygon sides of the mesh
|
||||||
void MakePolygonNormals(); // compute the normals of the polygons
|
void MakePolygonNormals(const double* dvert); // compute the normals of the polygons
|
||||||
|
|
||||||
// computes the inertia matrix of the mesh given the type of inertia
|
// computes the inertia matrix of the mesh given the type of inertia
|
||||||
double ComputeInertia(double inert[6], const double CoM[3]) const;
|
double ComputeInertia(double inert[6], const double CoM[3], const double* dvert) const;
|
||||||
|
|
||||||
int* GraphFaces() const {
|
int* GraphFaces() const {
|
||||||
return graph_ + 2 + 3*(graph_[0] + graph_[1]);
|
return graph_ + 2 + 3*(graph_[0] + graph_[1]);
|
||||||
@@ -1295,9 +1298,8 @@ class mjCMesh: public mjCMesh_, private mjsMesh {
|
|||||||
std::vector<std::vector<int>> polygon_map_; // map from vertex to polygon
|
std::vector<std::vector<int>> polygon_map_; // map from vertex to polygon
|
||||||
|
|
||||||
// compute the volume and center-of-mass of the mesh given the face centroid
|
// compute the volume and center-of-mass of the mesh given the face centroid
|
||||||
double ComputeVolume(double CoM[3], const double facecen[3]) const;
|
double ComputeVolume(double CoM[3], const double facecen[3], const double* dvert) const;
|
||||||
// compute the surface area and center-of-mass of the mesh given the face centroid
|
double ComputeSurfaceArea(double CoM[3], const double facecen[3], const double* dvert) const;
|
||||||
double ComputeSurfaceArea(double CoM[3], const double facecen[3]) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ mjResource* mju_openResource(const char* dir, const char* name,
|
|||||||
if (non_const_vfs == nullptr) {
|
if (non_const_vfs == nullptr) {
|
||||||
mjVFS* local_vfs = (mjVFS*)mju_malloc(sizeof(mjVFS));
|
mjVFS* local_vfs = (mjVFS*)mju_malloc(sizeof(mjVFS));
|
||||||
mj_defaultVFS(local_vfs);
|
mj_defaultVFS(local_vfs);
|
||||||
mujoco::user::VFS::Upcast(local_vfs)->SetToSelfDestruct([](mjVFS* ptr) {
|
mujoco::user::VFS::Upcast(local_vfs)->SetToSelfDestruct([=]() {
|
||||||
mj_deleteVFS(ptr);
|
mj_deleteVFS(local_vfs);
|
||||||
mju_free(ptr);
|
mju_free(local_vfs);
|
||||||
});
|
});
|
||||||
|
|
||||||
non_const_vfs = local_vfs;
|
non_const_vfs = local_vfs;
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ std::string StripPathAndLower(std::string path) {
|
|||||||
|
|
||||||
namespace mujoco::user {
|
namespace mujoco::user {
|
||||||
|
|
||||||
VFS::VFS(mjVFS* vfs) : self_(vfs) {
|
VFS::VFS(mjVFS* vfs) {
|
||||||
|
wrapped_vfs_.impl_ = this;
|
||||||
mjp_defaultResourceProvider(&default_provider_);
|
mjp_defaultResourceProvider(&default_provider_);
|
||||||
default_provider_.open = [](mjResource* res) {
|
default_provider_.open = [](mjResource* res) {
|
||||||
return OpenFile(res->name, res);
|
return OpenFile(res->name, res);
|
||||||
@@ -121,7 +122,7 @@ VFS::VFS(mjVFS* vfs) : self_(vfs) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
default_provider_.prefix = nullptr;
|
default_provider_.prefix = nullptr;
|
||||||
default_mount_.vfs = self_;
|
default_mount_.vfs = &wrapped_vfs_;
|
||||||
default_mount_.provider = &default_provider_;
|
default_mount_.provider = &default_provider_;
|
||||||
default_mount_.data = nullptr;
|
default_mount_.data = nullptr;
|
||||||
default_mount_.name = nullptr;
|
default_mount_.name = nullptr;
|
||||||
@@ -245,7 +246,7 @@ int VFS::Read(mjResource* resource, const void** buffer) {
|
|||||||
VFS::ResourcePtr VFS::CreateResource(std::string_view name,
|
VFS::ResourcePtr VFS::CreateResource(std::string_view name,
|
||||||
const mjpResourceProvider* provider) {
|
const mjpResourceProvider* provider) {
|
||||||
mjResource* res = new mjResource();
|
mjResource* res = new mjResource();
|
||||||
res->vfs = self_;
|
res->vfs = &wrapped_vfs_;
|
||||||
res->provider = provider;
|
res->provider = provider;
|
||||||
res->data = nullptr;
|
res->data = nullptr;
|
||||||
res->name = new char[name.size() + 1];
|
res->name = new char[name.size() + 1];
|
||||||
@@ -312,11 +313,14 @@ mjResource* VFS::FindMount(const std::string& fullpath) {
|
|||||||
|
|
||||||
void VFS::MaybeSelfDestruct() {
|
void VFS::MaybeSelfDestruct() {
|
||||||
if (destructor_) {
|
if (destructor_) {
|
||||||
destructor_(self_);
|
// Copy the destructor to a local variable so that we can destroy `this`
|
||||||
|
// object within the destructor.
|
||||||
|
auto fn = std::move(destructor_);
|
||||||
|
fn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VFS::SetToSelfDestruct(std::function<void(mjVFS*)> destructor) {
|
void VFS::SetToSelfDestruct(std::function<void()> destructor) {
|
||||||
destructor_ = std::move(destructor);
|
destructor_ = std::move(destructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -96,7 +96,7 @@ class VFS {
|
|||||||
// This is useful for when you want to create a temporary VFS instance with
|
// This is useful for when you want to create a temporary VFS instance with
|
||||||
// a lifetime tied to a single mjResource to be opened. The `destructor`
|
// a lifetime tied to a single mjResource to be opened. The `destructor`
|
||||||
// should be set to `delete this` and any other cleanup that needs to happen.
|
// should be set to `delete this` and any other cleanup that needs to happen.
|
||||||
void SetToSelfDestruct(std::function<void(mjVFS*)> destructor);
|
void SetToSelfDestruct(std::function<void()> destructor);
|
||||||
|
|
||||||
// Converts the public C-API pointer to the internal C++ class.
|
// Converts the public C-API pointer to the internal C++ class.
|
||||||
static VFS* Upcast(mjVFS* vfs);
|
static VFS* Upcast(mjVFS* vfs);
|
||||||
@@ -117,13 +117,13 @@ class VFS {
|
|||||||
// that `this` will be invalidated after this call.
|
// that `this` will be invalidated after this call.
|
||||||
void MaybeSelfDestruct();
|
void MaybeSelfDestruct();
|
||||||
|
|
||||||
mjVFS* self_;
|
mjVFS wrapped_vfs_;
|
||||||
std::mutex mutex_; // Protects open_resources_ and mounts_.
|
std::mutex mutex_; // Protects open_resources_ and mounts_.
|
||||||
std::unordered_map<mjResource*, ResourcePtr> open_resources_;
|
std::unordered_map<mjResource*, ResourcePtr> open_resources_;
|
||||||
std::unordered_map<std::string, ResourcePtr> mounts_;
|
std::unordered_map<std::string, ResourcePtr> mounts_;
|
||||||
mjResource default_mount_;
|
mjResource default_mount_;
|
||||||
mjpResourceProvider default_provider_;
|
mjpResourceProvider default_provider_;
|
||||||
std::function<void(mjVFS*)> destructor_;
|
std::function<void()> destructor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mujoco::user
|
} // namespace mujoco::user
|
||||||
|
|||||||
@@ -1086,6 +1086,71 @@ TEST_F(MjCMeshTest, MeshScale) {
|
|||||||
mj_deleteModel(model);
|
mj_deleteModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MjCMeshTest, NegativeScaleUserMeshCompiles) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<mesh name="example_mesh" scale="-1 1 1" inertia="exact"
|
||||||
|
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
|
||||||
|
face="0 2 1 0 3 2 1 3 0 1 2 3" />
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<geom type="mesh" mesh="example_mesh"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
char error[1024];
|
||||||
|
mjModel* model = LoadModelFromString(xml, error, sizeof(error));
|
||||||
|
ASSERT_THAT(model, NotNull()) << error;
|
||||||
|
mj_deleteModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MjCMeshTest, NegativeScaleUserMeshMatchesPositiveScale) {
|
||||||
|
static constexpr char pos_xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<mesh name="pos_mesh"
|
||||||
|
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
|
||||||
|
face="0 2 1 0 3 2 1 3 0 1 2 3" />
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<geom type="mesh" mesh="pos_mesh"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
|
||||||
|
static constexpr char neg_xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<mesh name="neg_mesh" scale="-1 1 1"
|
||||||
|
vertex="0 0 0 1 0 0 0 1 0 0 0 1"
|
||||||
|
face="0 2 1 0 3 2 1 3 0 1 2 3" />
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<geom type="mesh" mesh="neg_mesh"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
|
||||||
|
char error[1024];
|
||||||
|
mjModel* pos_model = LoadModelFromString(pos_xml, error, sizeof(error));
|
||||||
|
ASSERT_THAT(pos_model, NotNull()) << error;
|
||||||
|
|
||||||
|
mjModel* neg_model = LoadModelFromString(neg_xml, error, sizeof(error));
|
||||||
|
ASSERT_THAT(neg_model, NotNull()) << error;
|
||||||
|
|
||||||
|
ASSERT_EQ(pos_model->nmeshface, neg_model->nmeshface);
|
||||||
|
|
||||||
|
for (int i = 0; i < pos_model->nmeshface; i++) {
|
||||||
|
EXPECT_EQ(pos_model->mesh_face[3*i + 0], neg_model->mesh_face[3*i + 0]);
|
||||||
|
EXPECT_EQ(pos_model->mesh_face[3*i + 1], neg_model->mesh_face[3*i + 2]);
|
||||||
|
EXPECT_EQ(pos_model->mesh_face[3*i + 2], neg_model->mesh_face[3*i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mj_deleteModel(pos_model);
|
||||||
|
mj_deleteModel(neg_model);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(MjCMeshTest, ShellInertiaTest) {
|
TEST_F(MjCMeshTest, ShellInertiaTest) {
|
||||||
static constexpr char xml[] = R"(
|
static constexpr char xml[] = R"(
|
||||||
<mujoco>
|
<mujoco>
|
||||||
|
|||||||
@@ -407,5 +407,29 @@ TEST_F(UserVfsTest, StackedMounts) {
|
|||||||
EXPECT_EQ(test2, expect2);
|
EXPECT_EQ(test2, expect2);
|
||||||
EXPECT_EQ(test3, expect3);
|
EXPECT_EQ(test3, expect3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(UserVfsTest, MoveVfs) {
|
||||||
|
// Create and move a VFS to another address.
|
||||||
|
mjVFS* original = new mjVFS();
|
||||||
|
mj_defaultVFS(original);
|
||||||
|
mjVFS vfs = *original;
|
||||||
|
delete original;
|
||||||
|
|
||||||
|
std::string buffer = "<mujoco/>";
|
||||||
|
mj_addBufferVFS(&vfs, "model", static_cast<const void*>(buffer.c_str()),
|
||||||
|
buffer.size());
|
||||||
|
|
||||||
|
mjResource* resource = mju_openResource("", "model", &vfs, nullptr, 0);
|
||||||
|
ASSERT_THAT(resource, NotNull());
|
||||||
|
|
||||||
|
const void* out = nullptr;
|
||||||
|
const int size = mju_readResource(resource, &out);
|
||||||
|
EXPECT_GT(size, 0);
|
||||||
|
EXPECT_THAT(out, NotNull());
|
||||||
|
|
||||||
|
mju_closeResource(resource);
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class MujocoBinaryRetriever {
|
|||||||
if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/mujoco.dylib") == null) {
|
if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/mujoco.dylib") == null) {
|
||||||
File.Copy(
|
File.Copy(
|
||||||
"/Applications/MuJoCo.app/Contents/Frameworks" +
|
"/Applications/MuJoCo.app/Contents/Frameworks" +
|
||||||
"/mujoco.framework/Versions/Current/libmujoco.3.6.0.dylib",
|
"/mujoco.framework/Versions/Current/libmujoco.3.7.0.dylib",
|
||||||
mujocoPath + "/mujoco.dylib");
|
mujocoPath + "/mujoco.dylib");
|
||||||
AssetDatabase.Refresh();
|
AssetDatabase.Refresh();
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ public class MujocoBinaryRetriever {
|
|||||||
if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/libmujoco.so") == null) {
|
if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/libmujoco.so") == null) {
|
||||||
File.Copy(
|
File.Copy(
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) +
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) +
|
||||||
"/.mujoco/mujoco-3.6.0/lib/libmujoco.so.3.6.0",
|
"/.mujoco/mujoco-3.7.0/lib/libmujoco.so.3.7.0",
|
||||||
mujocoPath + "/libmujoco.so");
|
mujocoPath + "/libmujoco.so");
|
||||||
AssetDatabase.Refresh();
|
AssetDatabase.Refresh();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public const int mjMAXLINEPNT = 1001;
|
|||||||
public const int mjMAXPLANEGRID = 200;
|
public const int mjMAXPLANEGRID = 200;
|
||||||
public const bool THIRD_PARTY_MUJOCO_MJXMACRO_H_ = true;
|
public const bool THIRD_PARTY_MUJOCO_MJXMACRO_H_ = true;
|
||||||
public const bool THIRD_PARTY_MUJOCO_MUJOCO_H_ = true;
|
public const bool THIRD_PARTY_MUJOCO_MUJOCO_H_ = true;
|
||||||
public const int mjVERSION_HEADER = 3006000;
|
public const int mjVERSION_HEADER = 3007000;
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------Enums------------------------------------
|
// ------------------------------------Enums------------------------------------
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "org.mujoco",
|
"name": "org.mujoco",
|
||||||
"displayName": "MuJoCo",
|
"displayName": "MuJoCo",
|
||||||
"version": "3.6.0",
|
"version": "3.7.0",
|
||||||
"description": "MuJoCo importer and runtime plug-in",
|
"description": "MuJoCo importer and runtime plug-in",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
+14
-6
@@ -307,7 +307,7 @@ The function `mjv_updateScene` populates an `mjvScene` object with information
|
|||||||
from `mjModel` and `mjData`.
|
from `mjModel` and `mjData`.
|
||||||
```typescript
|
```typescript
|
||||||
// Create instances of the necessary structs.
|
// Create instances of the necessary structs.
|
||||||
const model = mujoco.MjModel.loadFromXML(xmlContent);
|
const model = mujoco.MjModel.from_xml_string(xmlContent);
|
||||||
const data = new mujoco.MjData(model);
|
const data = new mujoco.MjData(model);
|
||||||
const scene = new mujoco.MjvScene(model, 1000);
|
const scene = new mujoco.MjvScene(model, 1000);
|
||||||
const option = new mujoco.MjvOption();
|
const option = new mujoco.MjvOption();
|
||||||
@@ -348,23 +348,31 @@ mujoco.mjtDisableBit.mjDSBL_CLAMPCTRL.value
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Constants
|
### Constants
|
||||||
Scalar constants will be accessed the same way they are on python, simply:
|
Scalar constants can be accessed as properties:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
mujoco.mjNEQDATA
|
mujoco.mjNEQDATA
|
||||||
```
|
```
|
||||||
|
|
||||||
Due to Embind limitations, more complex constants that are not scalar, but are
|
Non-scalar constants like `mjFRAMESTRING` are also accessed as properties, and
|
||||||
represented in more dimensions are exposed as functions. E.g. to use
|
return JavaScript arrays:
|
||||||
`mujoco.mjFRAMESTRING` you will need to call a function:
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
mujoco.get_mjFRAMESTRING()
|
mujoco.mjFRAMESTRING
|
||||||
```
|
```
|
||||||
|
|
||||||
This will return a javascript array representation of the values in MuJoCo
|
This will return a javascript array representation of the values in MuJoCo
|
||||||
`mjFRAMESTRING`.
|
`mjFRAMESTRING`.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> You will notice constants like `mjFRAMESTRING` are typed as `any`. This is
|
||||||
|
> because they are bound using `emscripten::val::array()` in C++, and Embind
|
||||||
|
> maps `emscripten::val` to `any` in TypeScript definition files. While
|
||||||
|
> `EMSCRIPTEN_DECLARE_VAL_TYPE(StringArray)` could be used to define
|
||||||
|
> `StringArray` as an alias for `emscripten::val` and hint to Embind how to
|
||||||
|
> handle conversions in function signatures or when using `.as()` it does not
|
||||||
|
> change how `emscripten::constant` infers types for properties.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
In order to change the bindings you will need to change the [`bindings.cc`](codegen/generated/bindings.cc)
|
In order to change the bindings you will need to change the [`bindings.cc`](codegen/generated/bindings.cc)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#include <emscripten/bind.h>
|
#include <emscripten/bind.h>
|
||||||
|
#include <emscripten/em_asm.h>
|
||||||
#include <emscripten/val.h>
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -8361,6 +8362,39 @@ std::unique_ptr<MjModel> mj_loadModel_wrapper(std::string filename, const MjVFS&
|
|||||||
return std::unique_ptr<MjModel>(new MjModel(model));
|
return std::unique_ptr<MjModel>(new MjModel(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MjModel> from_xml_string_wrapper_1(const std::string& xml) {
|
||||||
|
mjVFS vfs;
|
||||||
|
mj_defaultVFS(&vfs);
|
||||||
|
const char* filename = "model.xml";
|
||||||
|
int add_result = mj_addBufferVFS(&vfs, filename, xml.c_str(), xml.length());
|
||||||
|
if (add_result != 0) {
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
|
mju_error("Could not add XML string to VFS: %d", add_result);
|
||||||
|
}
|
||||||
|
char error[1000];
|
||||||
|
mjModel* model = mj_loadXML(filename, &vfs, error, sizeof(error));
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
|
if (!model) {
|
||||||
|
mju_error("Loading error: %s\n", error);
|
||||||
|
}
|
||||||
|
return std::unique_ptr<MjModel>(new MjModel(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MjModel> from_xml_string_wrapper_2(const std::string& xml, const MjVFS& vfs) {
|
||||||
|
std::string filename = "model.xml";
|
||||||
|
int add_result = mj_addBufferVFS(vfs.get(), filename.c_str(), xml.c_str(), xml.length());
|
||||||
|
if (add_result != 0) {
|
||||||
|
mju_error("Could not add XML string to VFS: %d", add_result);
|
||||||
|
}
|
||||||
|
char error[1000];
|
||||||
|
mjModel* model = mj_loadXML(filename.c_str(), vfs.get(), error, sizeof(error));
|
||||||
|
mj_deleteFileVFS(vfs.get(), filename.c_str());
|
||||||
|
if (!model) {
|
||||||
|
mju_error("Loading error: %s\n", error);
|
||||||
|
}
|
||||||
|
return std::unique_ptr<MjModel>(new MjModel(model));
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<MjSpec> parseXMLString_wrapper(const std::string &xml) {
|
std::unique_ptr<MjSpec> parseXMLString_wrapper(const std::string &xml) {
|
||||||
char error[1000];
|
char error[1000];
|
||||||
mjSpec *ptr = mj_parseXMLString(xml.c_str(), nullptr, error, sizeof(error));
|
mjSpec *ptr = mj_parseXMLString(xml.c_str(), nullptr, error, sizeof(error));
|
||||||
@@ -8599,7 +8633,7 @@ void mj_fwdVelocity_wrapper(const MjModel& m, MjData& d) {
|
|||||||
mj_fwdVelocity(m.get(), d.get());
|
mj_fwdVelocity(m.get(), d.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
mjtNum mj_geomDistance_wrapper(const MjModel& m, const MjData& d, int geom1, int geom2, mjtNum distmax, const val& fromto) {
|
mjtNum mj_geomDistance_wrapper(const MjModel& m, MjData& d, int geom1, int geom2, mjtNum distmax, const val& fromto) {
|
||||||
UNPACK_NULLABLE_VALUE(mjtNum, fromto);
|
UNPACK_NULLABLE_VALUE(mjtNum, fromto);
|
||||||
CHECK_SIZE(fromto, 6);
|
CHECK_SIZE(fromto, 6);
|
||||||
return mj_geomDistance(m.get(), d.get(), geom1, geom2, distmax, fromto_.data());
|
return mj_geomDistance(m.get(), d.get(), geom1, geom2, distmax, fromto_.data());
|
||||||
@@ -11596,9 +11630,16 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
.property("useexisting", &MjLROpt::useexisting, &MjLROpt::set_useexisting, reference())
|
.property("useexisting", &MjLROpt::useexisting, &MjLROpt::set_useexisting, reference())
|
||||||
.property("uselimit", &MjLROpt::uselimit, &MjLROpt::set_uselimit, reference());
|
.property("uselimit", &MjLROpt::uselimit, &MjLROpt::set_uselimit, reference());
|
||||||
emscripten::class_<MjModel>("MjModel")
|
emscripten::class_<MjModel>("MjModel")
|
||||||
|
// mj_loadXML is deprecated and will be removed in a future release
|
||||||
.class_function("mj_loadXML", emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))
|
.class_function("mj_loadXML", emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))
|
||||||
.class_function("mj_loadXML", emscripten::select_overload<std::unique_ptr<MjModel>(std::string, const MjVFS&)>(&mj_loadXML_wrapper_2))
|
.class_function("mj_loadXML", emscripten::select_overload<std::unique_ptr<MjModel>(std::string, const MjVFS&)>(&mj_loadXML_wrapper_2))
|
||||||
|
// mj_loadModel is deprecated and will be removed in a future release
|
||||||
.class_function("mj_loadModel", &mj_loadModel_wrapper)
|
.class_function("mj_loadModel", &mj_loadModel_wrapper)
|
||||||
|
.class_function("from_binary_path", &mj_loadModel_wrapper)
|
||||||
|
.class_function("from_xml_string", emscripten::select_overload<std::unique_ptr<MjModel>(const std::string&)>(&from_xml_string_wrapper_1))
|
||||||
|
.class_function("from_xml_string", emscripten::select_overload<std::unique_ptr<MjModel>(const std::string&, const MjVFS&)>(&from_xml_string_wrapper_2))
|
||||||
|
.class_function("from_xml_path", emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))
|
||||||
|
.class_function("from_xml_path", emscripten::select_overload<std::unique_ptr<MjModel>(std::string, const MjVFS&)>(&mj_loadXML_wrapper_2))
|
||||||
.constructor<const MjModel &>()
|
.constructor<const MjModel &>()
|
||||||
// Binds the functions on MjModel that return accessors.
|
// Binds the functions on MjModel that return accessors.
|
||||||
#define X_ACCESSOR(NAME, Name, OBJTYPE, field_name, nfield) \
|
#define X_ACCESSOR(NAME, Name, OBJTYPE, field_name, nfield) \
|
||||||
@@ -13379,6 +13420,8 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
// as using std::optional<MjVFS> caused memory errors due to missing copy/move constructors.
|
// as using std::optional<MjVFS> caused memory errors due to missing copy/move constructors.
|
||||||
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&)>(&mj_compile_wrapper_1));
|
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&)>(&mj_compile_wrapper_1));
|
||||||
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&, const MjVFS&)>(&mj_compile_wrapper_2));
|
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&, const MjVFS&)>(&mj_compile_wrapper_2));
|
||||||
|
function("from_xml_string", emscripten::select_overload<std::unique_ptr<MjModel>(const std::string&)>(&from_xml_string_wrapper_1));
|
||||||
|
function("from_xml_string", emscripten::select_overload<std::unique_ptr<MjModel>(const std::string&, const MjVFS&)>(&from_xml_string_wrapper_2));
|
||||||
|
|
||||||
emscripten::class_<WasmBuffer<float>>("FloatBuffer")
|
emscripten::class_<WasmBuffer<float>>("FloatBuffer")
|
||||||
.constructor<int>()
|
.constructor<int>()
|
||||||
@@ -13409,6 +13452,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
.function("GetView", &WasmBuffer<uint8_t>::GetView);
|
.function("GetView", &WasmBuffer<uint8_t>::GetView);
|
||||||
|
|
||||||
emscripten::register_vector<std::string>("mjStringVec");
|
emscripten::register_vector<std::string>("mjStringVec");
|
||||||
|
emscripten::register_vector<std::vector<std::string>>("mjStringVecVec");
|
||||||
emscripten::register_vector<int>("mjIntVec");
|
emscripten::register_vector<int>("mjIntVec");
|
||||||
emscripten::register_vector<mjIntVec>("mjIntVecVec");
|
emscripten::register_vector<mjIntVec>("mjIntVecVec");
|
||||||
emscripten::register_vector<float>("mjFloatVec");
|
emscripten::register_vector<float>("mjFloatVec");
|
||||||
@@ -13453,7 +13497,6 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
emscripten::constant("mjPI", mjPI);
|
emscripten::constant("mjPI", mjPI);
|
||||||
emscripten::constant("mjVERSION_HEADER", mjVERSION_HEADER);
|
emscripten::constant("mjVERSION_HEADER", mjVERSION_HEADER);
|
||||||
|
|
||||||
// These complex constants are bound using function() rather than constant()
|
|
||||||
emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING);
|
emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING);
|
||||||
emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING);
|
emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING);
|
||||||
emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING);
|
emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING);
|
||||||
@@ -13461,6 +13504,31 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING);
|
emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING);
|
||||||
emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING);
|
emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING);
|
||||||
emscripten::function("get_mjVISSTRING", &get_mjVISSTRING);
|
emscripten::function("get_mjVISSTRING", &get_mjVISSTRING);
|
||||||
|
// Bind these complex constants as properties on the module object.
|
||||||
|
// We use emscripten::constant with emscripten::val::array() to type them
|
||||||
|
// as `any` in TypeScript. At runtime, the EM_ASM block below overrides
|
||||||
|
// these properties with getters that return native JavaScript arrays
|
||||||
|
// (string[] or string[][]) via the get_ functions above, which is more
|
||||||
|
// performant and idiomatic than vector wrappers.
|
||||||
|
emscripten::constant("mjDISABLESTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjENABLESTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjFRAMESTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjLABELSTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjRNDSTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjTIMERSTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjVISSTRING", emscripten::val::array());
|
||||||
|
EM_ASM({
|
||||||
|
if (typeof Module !== "undefined") {
|
||||||
|
"mjDISABLESTRING mjENABLESTRING mjFRAMESTRING mjLABELSTRING mjRNDSTRING mjTIMERSTRING mjVISSTRING".split(" ").forEach(function(name) {
|
||||||
|
Object.defineProperty(Module, name, {
|
||||||
|
get: function() { return Module["get_" + name](); },
|
||||||
|
set: function(v) { },
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mujoco::wasm
|
} // namespace mujoco::wasm
|
||||||
|
|||||||
@@ -549,6 +549,9 @@ def _build_struct_bindings(
|
|||||||
MJDATA_ACCESSORS
|
MJDATA_ACCESSORS
|
||||||
#undef X_ACCESSOR""".lstrip())
|
#undef X_ACCESSOR""".lstrip())
|
||||||
elif w == "MjModel":
|
elif w == "MjModel":
|
||||||
|
builder.line(
|
||||||
|
"// mj_loadXML is deprecated and will be removed in a future release"
|
||||||
|
)
|
||||||
builder.line(
|
builder.line(
|
||||||
'.class_function("mj_loadXML",'
|
'.class_function("mj_loadXML",'
|
||||||
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))"
|
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))"
|
||||||
@@ -561,7 +564,31 @@ def _build_struct_bindings(
|
|||||||
f2 = common.wrapped_function_name(
|
f2 = common.wrapped_function_name(
|
||||||
introspect_functions.FUNCTIONS["mj_loadModel"]
|
introspect_functions.FUNCTIONS["mj_loadModel"]
|
||||||
)
|
)
|
||||||
|
builder.line(
|
||||||
|
"// mj_loadModel is deprecated and will be removed in a future"
|
||||||
|
" release"
|
||||||
|
)
|
||||||
builder.line(f'.class_function("mj_loadModel", &{f2})')
|
builder.line(f'.class_function("mj_loadModel", &{f2})')
|
||||||
|
builder.line(f'.class_function("from_binary_path", &{f2})')
|
||||||
|
builder.line(
|
||||||
|
'.class_function("from_xml_string",'
|
||||||
|
" emscripten::select_overload<std::unique_ptr<MjModel>(const"
|
||||||
|
" std::string&)>(&from_xml_string_wrapper_1))"
|
||||||
|
)
|
||||||
|
builder.line(
|
||||||
|
'.class_function("from_xml_string",'
|
||||||
|
" emscripten::select_overload<std::unique_ptr<MjModel>(const"
|
||||||
|
" std::string&, const MjVFS&)>(&from_xml_string_wrapper_2))"
|
||||||
|
)
|
||||||
|
builder.line(
|
||||||
|
'.class_function("from_xml_path",'
|
||||||
|
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string)>(&mj_loadXML_wrapper_1))"
|
||||||
|
)
|
||||||
|
builder.line(
|
||||||
|
'.class_function("from_xml_path",'
|
||||||
|
" emscripten::select_overload<std::unique_ptr<MjModel>(std::string,"
|
||||||
|
" const MjVFS&)>(&mj_loadXML_wrapper_2))"
|
||||||
|
)
|
||||||
builder.line(".constructor<const MjModel &>()")
|
builder.line(".constructor<const MjModel &>()")
|
||||||
builder.line("""
|
builder.line("""
|
||||||
// Binds the functions on MjModel that return accessors.
|
// Binds the functions on MjModel that return accessors.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#include <emscripten/bind.h>
|
#include <emscripten/bind.h>
|
||||||
|
#include <emscripten/em_asm.h>
|
||||||
#include <emscripten/val.h>
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -746,6 +747,39 @@ std::unique_ptr<MjModel> mj_loadModel_wrapper(std::string filename, const MjVFS&
|
|||||||
return std::unique_ptr<MjModel>(new MjModel(model));
|
return std::unique_ptr<MjModel>(new MjModel(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MjModel> from_xml_string_wrapper_1(const std::string& xml) {
|
||||||
|
mjVFS vfs;
|
||||||
|
mj_defaultVFS(&vfs);
|
||||||
|
const char* filename = "model.xml";
|
||||||
|
int add_result = mj_addBufferVFS(&vfs, filename, xml.c_str(), xml.length());
|
||||||
|
if (add_result != 0) {
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
|
mju_error("Could not add XML string to VFS: %d", add_result);
|
||||||
|
}
|
||||||
|
char error[1000];
|
||||||
|
mjModel* model = mj_loadXML(filename, &vfs, error, sizeof(error));
|
||||||
|
mj_deleteVFS(&vfs);
|
||||||
|
if (!model) {
|
||||||
|
mju_error("Loading error: %s\n", error);
|
||||||
|
}
|
||||||
|
return std::unique_ptr<MjModel>(new MjModel(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MjModel> from_xml_string_wrapper_2(const std::string& xml, const MjVFS& vfs) {
|
||||||
|
std::string filename = "model.xml";
|
||||||
|
int add_result = mj_addBufferVFS(vfs.get(), filename.c_str(), xml.c_str(), xml.length());
|
||||||
|
if (add_result != 0) {
|
||||||
|
mju_error("Could not add XML string to VFS: %d", add_result);
|
||||||
|
}
|
||||||
|
char error[1000];
|
||||||
|
mjModel* model = mj_loadXML(filename.c_str(), vfs.get(), error, sizeof(error));
|
||||||
|
mj_deleteFileVFS(vfs.get(), filename.c_str());
|
||||||
|
if (!model) {
|
||||||
|
mju_error("Loading error: %s\n", error);
|
||||||
|
}
|
||||||
|
return std::unique_ptr<MjModel>(new MjModel(model));
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<MjSpec> parseXMLString_wrapper(const std::string &xml) {
|
std::unique_ptr<MjSpec> parseXMLString_wrapper(const std::string &xml) {
|
||||||
char error[1000];
|
char error[1000];
|
||||||
mjSpec *ptr = mj_parseXMLString(xml.c_str(), nullptr, error, sizeof(error));
|
mjSpec *ptr = mj_parseXMLString(xml.c_str(), nullptr, error, sizeof(error));
|
||||||
@@ -845,6 +879,8 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
// as using std::optional<MjVFS> caused memory errors due to missing copy/move constructors.
|
// as using std::optional<MjVFS> caused memory errors due to missing copy/move constructors.
|
||||||
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&)>(&mj_compile_wrapper_1));
|
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&)>(&mj_compile_wrapper_1));
|
||||||
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&, const MjVFS&)>(&mj_compile_wrapper_2));
|
function("mj_compile", emscripten::select_overload<std::unique_ptr<MjModel>(const MjSpec&, const MjVFS&)>(&mj_compile_wrapper_2));
|
||||||
|
function("from_xml_string", emscripten::select_overload<std::unique_ptr<MjModel>(const std::string&)>(&from_xml_string_wrapper_1));
|
||||||
|
function("from_xml_string", emscripten::select_overload<std::unique_ptr<MjModel>(const std::string&, const MjVFS&)>(&from_xml_string_wrapper_2));
|
||||||
|
|
||||||
emscripten::class_<WasmBuffer<float>>("FloatBuffer")
|
emscripten::class_<WasmBuffer<float>>("FloatBuffer")
|
||||||
.constructor<int>()
|
.constructor<int>()
|
||||||
@@ -875,6 +911,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
.function("GetView", &WasmBuffer<uint8_t>::GetView);
|
.function("GetView", &WasmBuffer<uint8_t>::GetView);
|
||||||
|
|
||||||
emscripten::register_vector<std::string>("mjStringVec");
|
emscripten::register_vector<std::string>("mjStringVec");
|
||||||
|
emscripten::register_vector<std::vector<std::string>>("mjStringVecVec");
|
||||||
emscripten::register_vector<int>("mjIntVec");
|
emscripten::register_vector<int>("mjIntVec");
|
||||||
emscripten::register_vector<mjIntVec>("mjIntVecVec");
|
emscripten::register_vector<mjIntVec>("mjIntVecVec");
|
||||||
emscripten::register_vector<float>("mjFloatVec");
|
emscripten::register_vector<float>("mjFloatVec");
|
||||||
@@ -919,7 +956,6 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
emscripten::constant("mjPI", mjPI);
|
emscripten::constant("mjPI", mjPI);
|
||||||
emscripten::constant("mjVERSION_HEADER", mjVERSION_HEADER);
|
emscripten::constant("mjVERSION_HEADER", mjVERSION_HEADER);
|
||||||
|
|
||||||
// These complex constants are bound using function() rather than constant()
|
|
||||||
emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING);
|
emscripten::function("get_mjDISABLESTRING", &get_mjDISABLESTRING);
|
||||||
emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING);
|
emscripten::function("get_mjENABLESTRING", &get_mjENABLESTRING);
|
||||||
emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING);
|
emscripten::function("get_mjFRAMESTRING", &get_mjFRAMESTRING);
|
||||||
@@ -927,6 +963,31 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
|
|||||||
emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING);
|
emscripten::function("get_mjRNDSTRING", &get_mjRNDSTRING);
|
||||||
emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING);
|
emscripten::function("get_mjTIMERSTRING", &get_mjTIMERSTRING);
|
||||||
emscripten::function("get_mjVISSTRING", &get_mjVISSTRING);
|
emscripten::function("get_mjVISSTRING", &get_mjVISSTRING);
|
||||||
|
// Bind these complex constants as properties on the module object.
|
||||||
|
// We use emscripten::constant with emscripten::val::array() to type them
|
||||||
|
// as `any` in TypeScript. At runtime, the EM_ASM block below overrides
|
||||||
|
// these properties with getters that return native JavaScript arrays
|
||||||
|
// (string[] or string[][]) via the get_ functions above, which is more
|
||||||
|
// performant and idiomatic than vector wrappers.
|
||||||
|
emscripten::constant("mjDISABLESTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjENABLESTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjFRAMESTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjLABELSTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjRNDSTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjTIMERSTRING", emscripten::val::array());
|
||||||
|
emscripten::constant("mjVISSTRING", emscripten::val::array());
|
||||||
|
EM_ASM({
|
||||||
|
if (typeof Module !== "undefined") {
|
||||||
|
"mjDISABLESTRING mjENABLESTRING mjFRAMESTRING mjLABELSTRING mjRNDSTRING mjTIMERSTRING mjVISSTRING".split(" ").forEach(function(name) {
|
||||||
|
Object.defineProperty(Module, name, {
|
||||||
|
get: function() { return Module["get_" + name](); },
|
||||||
|
set: function(v) { },
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mujoco::wasm
|
} // namespace mujoco::wasm
|
||||||
|
|||||||
+1
-10
@@ -205,10 +205,7 @@ class App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadModel(xmlContent: string) {
|
loadModel(xmlContent: string) {
|
||||||
// Write xml as a file so that mujoco can find it
|
this.mjModel = mujoco.MjModel.from_xml_string(xmlContent);
|
||||||
(mujoco as any).FS.writeFile('/working/model.xml', xmlContent);
|
|
||||||
|
|
||||||
this.mjModel = mujoco.MjModel.mj_loadXML('/working/model.xml');
|
|
||||||
if (!app.mjModel) {
|
if (!app.mjModel) {
|
||||||
throw new Error('Failed to load model');
|
throw new Error('Failed to load model');
|
||||||
}
|
}
|
||||||
@@ -437,8 +434,6 @@ function setupWindowEvents() {
|
|||||||
// Tip: put "window.dispatchEvent(new Event('unload'))" in the console to test
|
// Tip: put "window.dispatchEvent(new Event('unload'))" in the console to test
|
||||||
window.addEventListener('unload', () => {
|
window.addEventListener('unload', () => {
|
||||||
app.dispose();
|
app.dispose();
|
||||||
|
|
||||||
(mujoco as any).FS.unmount('/working');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('keydown', (event) => {
|
window.addEventListener('keydown', (event) => {
|
||||||
@@ -464,10 +459,6 @@ async function main() {
|
|||||||
try {
|
try {
|
||||||
mujoco = await loadMujoco();
|
mujoco = await loadMujoco();
|
||||||
|
|
||||||
// Set up emscripten virtual file system
|
|
||||||
(mujoco as any).FS.mkdir('/working');
|
|
||||||
(mujoco as any).FS.mount((mujoco as any).MEMFS, {root: '.'}, '/working');
|
|
||||||
|
|
||||||
app = new App();
|
app = new App();
|
||||||
|
|
||||||
setupWindowEvents();
|
setupWindowEvents();
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "mujoco",
|
"name": "@mujoco/mujoco",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"description": "MuJoCo WASM bindings",
|
"description": "MuJoCo WASM bindings",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/google-deepmind/mujoco.git",
|
"url": "git+https://github.com/google-deepmind/mujoco.git",
|
||||||
"directory": "wasm"
|
"directory": "wasm"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/google-deepmind/mujoco/tree/main/wasm",
|
"homepage": "https://github.com/google-deepmind/mujoco/tree/main/wasm",
|
||||||
|
|||||||
@@ -683,24 +683,24 @@ describe('MuJoCo WASM Bindings', () => {
|
|||||||
|
|
||||||
it('should check constants values', () => {
|
it('should check constants values', () => {
|
||||||
expect(mujoco.mjNEQDATA).toBe(11);
|
expect(mujoco.mjNEQDATA).toBe(11);
|
||||||
expect(mujoco.get_mjDISABLESTRING()).toEqual([
|
expect(mujoco.mjDISABLESTRING).toEqual([
|
||||||
'Constraint', 'Equality', 'Frictionloss', 'Limit', 'Contact', 'Spring',
|
'Constraint', 'Equality', 'Frictionloss', 'Limit', 'Contact', 'Spring',
|
||||||
'Damper', 'Gravity', 'Clampctrl', 'Warmstart', 'Filterparent',
|
'Damper', 'Gravity', 'Clampctrl', 'Warmstart', 'Filterparent',
|
||||||
'Actuation', 'Refsafe', 'Sensor', 'Midphase', 'Eulerdamp', 'AutoReset',
|
'Actuation', 'Refsafe', 'Sensor', 'Midphase', 'Eulerdamp', 'AutoReset',
|
||||||
'NativeCCD', 'Island'
|
'NativeCCD', 'Island'
|
||||||
]);
|
]);
|
||||||
expect(mujoco.get_mjRNDSTRING()).toEqual([
|
expect(mujoco.mjRNDSTRING).toEqual([
|
||||||
['Shadow', '1', 'S'], ['Wireframe', '0', 'W'], ['Reflection', '1', 'R'],
|
['Shadow', '1', 'S'], ['Wireframe', '0', 'W'], ['Reflection', '1', 'R'],
|
||||||
['Additive', '0', 'L'], ['Skybox', '1', 'K'], ['Fog', '0', 'G'],
|
['Additive', '0', 'L'], ['Skybox', '1', 'K'], ['Fog', '0', 'G'],
|
||||||
['Haze', '1', '/'], ['Depth', '0', ''], ['Segment', '0', ','],
|
['Haze', '1', '/'], ['Depth', '0', ''], ['Segment', '0', ','],
|
||||||
['Id Color', '0', ''], ['Cull Face', '1', '']
|
['Id Color', '0', ''], ['Cull Face', '1', '']
|
||||||
]);
|
]);
|
||||||
expect(mujoco.get_mjFRAMESTRING().length)
|
expect(mujoco.mjFRAMESTRING.length).toEqual(mujoco.mjtFrame.mjNFRAME.value);
|
||||||
.toEqual(mujoco.mjtFrame.mjNFRAME.value);
|
expect(mujoco.mjVISSTRING.length)
|
||||||
expect(mujoco.get_mjVISSTRING().length)
|
|
||||||
.toEqual(mujoco.mjtVisFlag.mjNVISFLAG.value);
|
.toEqual(mujoco.mjtVisFlag.mjNVISFLAG.value);
|
||||||
expect(mujoco.get_mjVISSTRING()[mujoco.mjtVisFlag.mjVIS_INERTIA.value])
|
expect(mujoco.mjVISSTRING[mujoco.mjtVisFlag.mjVIS_INERTIA.value]).toEqual([
|
||||||
.toEqual(['Inertia', '0', 'I']);
|
'Inertia', '0', 'I'
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a spec from XML', () => {
|
it('should create a spec from XML', () => {
|
||||||
@@ -2603,4 +2603,56 @@ describe('MuJoCo WASM Bindings', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should load a model from an XML string', () => {
|
||||||
|
let model: MjModel|null = null;
|
||||||
|
try {
|
||||||
|
model = mujoco.from_xml_string(TEST_XML);
|
||||||
|
assertExists(model);
|
||||||
|
expect(model.nbody).toBe(5);
|
||||||
|
expect(model.ngeom).toBe(3);
|
||||||
|
} finally {
|
||||||
|
model?.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load a model from an XML string with VFS', () => {
|
||||||
|
const xml = `
|
||||||
|
<mujoco>
|
||||||
|
<asset>
|
||||||
|
<mesh file="cube.obj"/>
|
||||||
|
</asset>
|
||||||
|
<worldbody>
|
||||||
|
<geom type="mesh" mesh="cube"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>`;
|
||||||
|
|
||||||
|
const cube1 = `
|
||||||
|
v -1 -1 1
|
||||||
|
v 1 -1 1
|
||||||
|
v -1 1 1
|
||||||
|
v 1 1 1
|
||||||
|
v -1 1 -1
|
||||||
|
v 1 1 -1
|
||||||
|
v -1 -1 -1
|
||||||
|
v 1 -1 -1`;
|
||||||
|
|
||||||
|
let model: MjModel|null = null;
|
||||||
|
let vfs: MjVFS|null = null;
|
||||||
|
try {
|
||||||
|
vfs = new mujoco.MjVFS();
|
||||||
|
vfs.addBuffer('cube.obj', new TextEncoder().encode(cube1));
|
||||||
|
assertExists(vfs);
|
||||||
|
|
||||||
|
model = mujoco.from_xml_string(xml, vfs);
|
||||||
|
assertExists(model);
|
||||||
|
expect(model.nmesh).toBe(1);
|
||||||
|
|
||||||
|
const meshId =
|
||||||
|
mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_MESH.value, 'cube');
|
||||||
|
expect(meshId).toBeGreaterThanOrEqual(0);
|
||||||
|
} finally {
|
||||||
|
model?.delete();
|
||||||
|
vfs?.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ declare function loadMujoco(): Promise<MainModule>;
|
|||||||
async function main() {
|
async function main() {
|
||||||
const mujoco: MainModule = await loadMujoco();
|
const mujoco: MainModule = await loadMujoco();
|
||||||
|
|
||||||
(mujoco as any).FS.mkdir('/working');
|
|
||||||
(mujoco as any).FS.mount((mujoco as any).MEMFS, {root: '.'}, '/working');
|
|
||||||
|
|
||||||
const xmlContent = `
|
const xmlContent = `
|
||||||
<mujoco model="Box falling">
|
<mujoco model="Box falling">
|
||||||
<option viscosity="1"/>
|
<option viscosity="1"/>
|
||||||
@@ -37,13 +34,12 @@ async function main() {
|
|||||||
</worldbody>
|
</worldbody>
|
||||||
</mujoco>`;
|
</mujoco>`;
|
||||||
|
|
||||||
(mujoco as any).FS.writeFile('/working/hello.xml', xmlContent);
|
|
||||||
let model: MjModel|undefined;
|
let model: MjModel|undefined;
|
||||||
let data: MjData|undefined;
|
let data: MjData|undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Hello world!: Loading model');
|
console.log('Hello world!: Loading model');
|
||||||
model = mujoco.MjModel.mj_loadXML('/working/hello.xml');
|
model = mujoco.MjModel.from_xml_string(xmlContent);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
throw new Error('Failed to load model');
|
throw new Error('Failed to load model');
|
||||||
}
|
}
|
||||||
@@ -57,7 +53,6 @@ async function main() {
|
|||||||
} finally {
|
} finally {
|
||||||
model?.delete();
|
model?.delete();
|
||||||
data?.delete();
|
data?.delete();
|
||||||
(mujoco as any).FS.unmount('/working');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user