Initial release of the Unity plugin.
PiperOrigin-RevId: 425947505
This commit is contained in:
committed by
Saran Tunyasuvunakool
parent
8ec3dd97a4
commit
6de9eafa13
@@ -13,4 +13,5 @@
|
||||
XMLreference
|
||||
programming
|
||||
APIreference
|
||||
unity
|
||||
changelog
|
||||
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
=============
|
||||
Unity Plug-in
|
||||
=============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The MuJoCo `Unity plug-in <https://github.com/deepmind/mujoco/tree/main/unity>`_ allows the Unity Editor and runtime to use the MuJoCo physics engine. Users can import MJCF
|
||||
files and edit the models in the Editor. The plug-in relies on Unity for most aspects -- assets, game logic, simulation
|
||||
time -- but uses MuJoCo to determine how objects move, giving the designer access to MuJoCo's full API.
|
||||
|
||||
.. _UInstallation:
|
||||
|
||||
Installation instructions
|
||||
-------------------------
|
||||
|
||||
The plug-in directory (available at https://github.com/deepmind/mujoco/tree/main/unity) includes a ``package.json``
|
||||
file. Unity's package manager recognizes this file and will import the plug-in's C# codebase to your project. In
|
||||
addition, Unity also needs the native MuJoCo library, which can be found in the specific platfomr archive at
|
||||
https://github.com/deepmind/mujoco/release.
|
||||
|
||||
On Unity version 2020.2 and later, the Package Manager will look for the native library file and copy it to the package
|
||||
directory when the package is imported. Alternatively, you can manually copy the native library to the package directory
|
||||
and rename it, see platform-specific instructions below. The library can also be copied into any location under your
|
||||
project's Assets directory.
|
||||
|
||||
MacOS
|
||||
_____
|
||||
|
||||
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
|
||||
``/Applications/MuJoCo.app/Contents/Frameworks/MuJoCo.framework/Versions/Current/libmujoco.2.1.1.dylib`` (it can be
|
||||
found by browsing the contents of ``MuJoCo.app``) and rename it as ``mujoco.dylib``.
|
||||
|
||||
Linux
|
||||
_____
|
||||
|
||||
Expand the ``tar.gz`` archive to ``~/.mujoco``. Then copy the dynamic library from
|
||||
``~/.mujoco/mujoco-2.1.1/lib/libmujoco_nogl.so.2.1.1`` (note the ``_nogl`` suffix) and rename it as ``libmujoco.so``.
|
||||
|
||||
Windows
|
||||
_______
|
||||
|
||||
Expand the ``zip`` archive to a directory called ``MuJoCo`` in your user directory, and copy the file
|
||||
``MuJoCo\bin\mujoco.dll``.
|
||||
|
||||
.. _UUsing:
|
||||
|
||||
Using the plug-in
|
||||
-----------------
|
||||
|
||||
.. _UImporter:
|
||||
|
||||
Importer
|
||||
________
|
||||
|
||||
The importer is invoked from the Editor's *Asset* menu: click on "Import MuJoCo Scene" and select the XML file with your
|
||||
model's MJCF specification.
|
||||
|
||||
Context menus
|
||||
_____________
|
||||
|
||||
- Right-clicking a geom component offers two options:
|
||||
|
||||
- "Add mesh renderer" adds components to the same game object that render the geom: a standard ``MeshRenderer`` and a
|
||||
``MjMeshFilter`` that creates a procedural mesh that is recreated when the geom shape properties change.
|
||||
- "Convert to free object" adds two new game objects: a parent with an ``MjBody`` component and a sibling with an
|
||||
``MjFreeJoint`` component. This allows the previously static geom to move about freely in the scene. This action
|
||||
only applies to "world" geoms -- those that do not currently have an ``MjBody`` parent.
|
||||
|
||||
- Right-clicking a Unity Collider offers the option to "Add a matching MuJoCo geom" to the same game object. Note that
|
||||
this does not comprise a complete conversion of the physics -- Rigidbody, ArticulationBody and Joint configurations
|
||||
still need to be recreated manually.
|
||||
|
||||
Mouse spring
|
||||
____________
|
||||
|
||||
When the selected game object has an ``MjBody`` component, spring forces can be applied to this body towards the mouse
|
||||
cursor through a control-left-drag action in the Scene view. The 3D position of the spring force origin is found by
|
||||
projecting the mouse position on a plane defined by the camera X direction and the world Y direction. Adding the shift
|
||||
key changes the projection plane to be parallel to the world's X and Z axes.
|
||||
|
||||
.. _UTips:
|
||||
|
||||
Tips to Unity users
|
||||
___________________
|
||||
|
||||
- If any compilation or runtime errors are encountered, the state of the system is undefined. Therefore, we recommend
|
||||
turning on “Error Pause” in the console window.
|
||||
- In PhysX, every `Rigidbody` is a “free body”. In contrast, MuJoCo requires explicit specification of joints for
|
||||
mobility. For convenience, we provide a context menu for “freeing” a world geom (i.e., an ``MjGeom`` component without
|
||||
any ``MjBody`` ancestor) by adding a parent ``MjBody`` and a sibling ``MjFreeJoint``.
|
||||
- The plug-in doesn’t support collision detection without physical presence, so there is no built-in notion of trigger
|
||||
colliders. The presence or absence of a contact force can be read by adding a touch sensor and reading its
|
||||
``SensorReading`` value (which will correspond to the normal force, see `touch sensor documentation <sensor-touch>`).
|
||||
|
||||
.. _UDesign:
|
||||
|
||||
Design principles
|
||||
-----------------
|
||||
|
||||
The plug-in design provides a one-to-one mapping between MJCF elements and Unity components. In order to simulate a
|
||||
Unity scene (e.g., when the user hits the “play” button in the Editor) using MuJoCo, the plug-in:
|
||||
|
||||
1. Scans the GameObject hierarchy in the scene for MuJoCo components.
|
||||
2. Creates an MJCF description and passes it to MuJoCo’s compiler.
|
||||
3. Binds every component to the MuJoCo runtime via the corresponding index in MuJoCo’s data structures. This index is
|
||||
used for updating Unity’s transforms during simulation.
|
||||
|
||||
This design principle has several implications:
|
||||
|
||||
- Most fields of the Unity components correspond directly to MJCF attributes. Therefore, the user can refer to the
|
||||
MuJoCo documentation for details on the semantics of different values.
|
||||
- The layout of MuJoCo components in the GameObject hierarchy determines the layout of the resulting MuJoCo model.
|
||||
Therefore, we adopt a design rule that **every game object must have at most one MuJoCo component**.
|
||||
- We rely on Unity for spatial configuration, which requires vector components to be `swizzled
|
||||
<https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)>`_ since Unity uses left-handed frames with Y as the
|
||||
vertical axis, while MuJoCo uses right-handed frames with Z as the vertical axis.
|
||||
- Unity transform scaling affects positions, orientations, and scale of the entire game object subtree. However, MuJoCo
|
||||
doesn’t support collision of skewed cylinders and capsules (skewed spheres are supported via the ellipsoid primitive).
|
||||
The gizmo for geoms and sites ignores this skew (similarly to PhysX colliders), and will always show the primitive
|
||||
shape as it will appear to the physics.
|
||||
- During runtime, changing values of component fields will not trigger scene recreation, so it will have no immediate
|
||||
effect on the physics. However, the new values will be loaded upon the next scene recreation.
|
||||
|
||||
Wherever possible, we do things the Unity Way: gravity is read from Unity’s physics settings, and the simulation step is
|
||||
read from Unity’s Time Manager’s `Fixed Timestep`. All aspects of appearance (e.g., meshes, materials, and textures)
|
||||
are handled by Unity’s Asset Manager, and RGBA specifications are done using material assets.
|
||||
|
||||
.. _UNotes:
|
||||
|
||||
Implementation notes
|
||||
--------------------
|
||||
|
||||
Importer workflow
|
||||
_________________
|
||||
|
||||
When the user selects an MJCF file, the importer first loads
|
||||
the file in MuJoCo, saves it to a temporary location, and then processes the generated saved file. This has several
|
||||
effects:
|
||||
|
||||
- It validates the MJCF - we are guaranteed that the saved MJCF matches the `schema <CSchema>`_.
|
||||
- It validates the assets (materials, meshes, textures) and imports these assets into Unity, as well as creating new
|
||||
material assets for geom RGBA specification.
|
||||
- It allows the importer to handle :ref:`\<include\> <include>` elements without replicating MuJoCo’s file-system
|
||||
workflow.
|
||||
- The current version of MuJoCo generates MJCF files with explicit :ref:`\<inertial\> <inertial>` elements, even when
|
||||
the original model uses geoms for implicit definition of the body inertia. If you plan to change geom properties of
|
||||
an imported model, remove these auto-generated ``MjInertial`` components manually. We plan to address this in a
|
||||
future release of MuJoCo.
|
||||
|
||||
In Unity, there is no equivalent to MJCF’s “cascading” :ref:`\<default\> <default>` clauses. Therefore, components in
|
||||
Unity reflect the corresponding elements’ state after applying all the relevant default classes, and the class structure
|
||||
in the original MJCF is discarded.
|
||||
|
||||
The MuJoCo Scene
|
||||
________________
|
||||
|
||||
When a MuJoCo scene is created, the ``MjScene`` component first scans the scene for all instances of ``MjComponent``.
|
||||
Each component creates its own MJCF element using Unity scene’s spatial structure to describe the model’s initial
|
||||
reference pose (called ``qpos0`` in MuJoCo). ``MjScene`` combines these XML elements according to the hierarchy of the
|
||||
respective game objects and creates a single MJCF description of the physics model. It then creates the runtime structs
|
||||
``mjModel`` and ``mjData``, and binds each component to the runtime by identifying its unique index.
|
||||
|
||||
During runtime, ``MjScene.FixedUpdate()`` calls :ref:`mj_step`, and then synchronizes the state of each game object
|
||||
according to the index ``MjComponent.MujocoId`` identified at binding time. An ``MjScene`` component is added
|
||||
automatically when the application starts (e.g., when the user hits “play”) if and only if the scene includes any MuJoCo
|
||||
components. If your application’s initialization phase involves ticking the physics while adding game objects and
|
||||
components, you can call ``MjScene.CreateScene()`` when the initialization phase is over.
|
||||
|
||||
Scene recreation maintains continuity of physics and state in the following way:
|
||||
|
||||
1. The position and velocity of joints is cached.
|
||||
2. MuJoCo’s state is reset (to ``qpos0``) and Unity transforms are synchronized.
|
||||
3. A new XML is generated, creating a model that has the same ``qpos0`` as the previous one for the joints that
|
||||
persisted.
|
||||
4. The MuJoCo state (for the joints that persisted) is set from the cache, and Unity transforms are synchronized.
|
||||
|
||||
Because the MuJoCo library doesn’t (yet) expose an API for scene editing, adding and removing MuJoCo components causes
|
||||
complete scene recreation. This can be expensive for large models or if it happens frequently. We expect this
|
||||
performance limitation to be lifted in future versions of MuJoCo.
|
||||
|
||||
Global Settings
|
||||
_______________
|
||||
|
||||
An exception to the one-element-per-one-component is the Global Settings component. This component is responsible for
|
||||
all the configuration options that are included in the fixed-size, singleton, global elements of MJCF. Currently it
|
||||
holds information that corresponds to the :ref:`\<option\> <option>` and :ref:`\<size\> <size>` elements, and in the
|
||||
future it will also be used for the :ref:`\<compiler\> <compiler>` element, if/when fields there will be relevant to the
|
||||
Unity plug-in.
|
||||
|
||||
Invoking the importer at application runtime
|
||||
____________________________________________
|
||||
|
||||
The importer is implemented by the class ``MjImporterWithAssets``, which is a subclass of ``MjcfImporter``. This parent
|
||||
class takes an MJCF string and generates the hierarchy of components. It can be invoked at play-time (it doesn’t
|
||||
involve Editor functionality), and it doesn’t invoke any functions of the MuJoCo library. This is useful when MuJoCo
|
||||
models are generated procedurally (e.g., by some evolutionary process) and/or when an MJCF is imported only to be
|
||||
converted (e.g., to PhysX, or URDF). Since it cannot interact with Unity’s ``AssetManager`` (which is a feature of the
|
||||
Editor), this class’s functionality is restricted. Specifically:
|
||||
|
||||
- It ignores all assets (including collision meshes).
|
||||
- It ignores visuals (including RGBA specifications).
|
||||
|
||||
MuJoCo sensor components
|
||||
________________________
|
||||
|
||||
MuJoCo defines many sensors, and we were concerned that creating a separate ``MjComponent`` class for each would lead to
|
||||
a lot of code duplication. Therefore, we created classes according to the type of object (actuator / body / geom /
|
||||
joint / site) whose properties are measured, and the type (scalar / vector / quaternion) of the measured data.
|
||||
|
||||
Here’s a table that maps types to sensors:
|
||||
|
||||
+------------------------+---------------+---------------------+
|
||||
| **Mujoco Object Type** | **Data Type** | **Sensor Name** |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Actuator | Scalar | - ``actuatorpos`` |
|
||||
| | | - ``actuatorvel`` |
|
||||
| | | - ``actuatorfrc`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Body | Vector | - ``subtreecom`` |
|
||||
| | | - ``subtreelinvel`` |
|
||||
| | | - ``subtreeangmom`` |
|
||||
| | | - ``framepos`` |
|
||||
| | | - ``framexaxis`` |
|
||||
| | | - ``frameyaxis`` |
|
||||
| | | - ``framezaxis`` |
|
||||
| | | - ``framelinvel`` |
|
||||
| | | - ``frameangvel`` |
|
||||
| | | - ``framelinacc`` |
|
||||
| | | - ``frameangacc`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Body | Quaternion | - ``framequat`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Geom | Vector | - ``framepos`` |
|
||||
| | | - ``framexaxis`` |
|
||||
| | | - ``frameyaxis`` |
|
||||
| | | - ``framezaxis`` |
|
||||
| | | - ``framelinvel`` |
|
||||
| | | - ``frameangvel`` |
|
||||
| | | - ``framelinacc`` |
|
||||
| | | - ``frameangacc`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Geom | Quaternion | - ``framequat`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Joint | Scalar | - ``jointpos`` |
|
||||
| | | - ``jointvel`` |
|
||||
| | | - ``jointlimitpos`` |
|
||||
| | | - ``jointlimitvel`` |
|
||||
| | | - ``jointlimitfrc`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Site | Scalar | - ``touch`` |
|
||||
| | | - ``rangefinder`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Site | Vector | - ``accelerometer`` |
|
||||
| | | - ``velocimeter`` |
|
||||
| | | - ``force`` |
|
||||
| | | - ``torque`` |
|
||||
| | | - ``gyro`` |
|
||||
| | | - ``magnetometer`` |
|
||||
| | | - ``framepos`` |
|
||||
| | | - ``framexaxis`` |
|
||||
| | | - ``frameyaxis`` |
|
||||
| | | - ``framezaxis`` |
|
||||
| | | - ``framelinvel`` |
|
||||
| | | - ``frameangvel`` |
|
||||
| | | - ``framelinacc`` |
|
||||
| | | - ``frameangacc`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
| Site | Quaternion | - ``framequat`` |
|
||||
+------------------------+---------------+---------------------+
|
||||
|
||||
Here’s the same table in reverse, mapping sensors to classes:
|
||||
|
||||
================= ===================================
|
||||
Sensor Name Plugin Class
|
||||
================= ===================================
|
||||
``accelerometer`` SiteVector
|
||||
``actuatorfrc`` ActuatorScalar
|
||||
``actuatorpos`` ActuatorScalar
|
||||
``actuatorvel`` ActuatorScalar
|
||||
``force`` SiteVector
|
||||
``frameangacc`` \*Vector (depends on frame type)
|
||||
``frameangvel`` \*Vector (depends on frame type)
|
||||
``framelinacc`` \*Vector (depends on frame type)
|
||||
``framelinvel`` \*Vector (depends on frame type)
|
||||
``framepos`` \*Vector (depends on frame type)
|
||||
``framequat`` \*Quaternion (depends on frame type)
|
||||
``framexaxis`` \*Vector (depends on frame type)
|
||||
``frameyaxis`` \*Vector (depends on frame type)
|
||||
``framezaxis`` \*Vector (depends on frame type)
|
||||
``gyro`` SiteVector
|
||||
``jointlimitfrc`` JointScalar
|
||||
``jointlimitpos`` JointScalar
|
||||
``jointlimitvel`` JointScalar
|
||||
``jointpos`` JointScalar
|
||||
``jointvel`` JointScalar
|
||||
``magnetometer`` SiteVector
|
||||
``subtreeangmom`` BodyVector
|
||||
``subtreecom`` BodyVector
|
||||
``subtreelinvel`` BodyVector
|
||||
``torque`` SiteVector
|
||||
``touch`` SiteScalar
|
||||
``velocimeter`` SiteVector
|
||||
================= ===================================
|
||||
|
||||
The following sensors are not yet implemented:
|
||||
|
||||
| ``tendonpos``
|
||||
| ``tendonvel``
|
||||
| ``ballquat``
|
||||
| ``ballangvel``
|
||||
| ``tendonlimitpos``
|
||||
| ``tendonlimitvel``
|
||||
| ``tendonlimitfrc``
|
||||
| ``user``
|
||||
|
||||
Mesh Shapes
|
||||
___________
|
||||
|
||||
The plug-in allows using arbitrary Unity meshes for MuJoCo collision. At model compilation, MuJoCo calls `qhull
|
||||
<http://www.qhull.org/>`__ to create a convex hull of the mesh, and uses that for collisions. Currently the computed
|
||||
convex hull is not visible in Unity, but we intend to expose it in future versions.
|
||||
|
||||
Interaction with External Processes
|
||||
___________________________________
|
||||
|
||||
Roboti’s `MuJoCo plug-in for Unity <https://roboti.us/download.html>`_ steps the simulation in an external Python
|
||||
process, and uses Unity only for rendering. In contrast, our plug-in relies on Unity to step the simulation. It should
|
||||
be possible to use our plug-in while an external process "drives" the simulation, for example by seting ``qpos``,
|
||||
calling ``mj_kinematics``, synchronizing the transforms, and then using Unity to render or compute game logic. In order
|
||||
to establish communication with an external process, you can use Unity's `ML-Agents
|
||||
<https://github.com/Unity-Technologies/ml-agents>`_ package.
|
||||
@@ -56,7 +56,8 @@
|
||||
rgb1="0.6 0.6 0.6" rgb2="0.6 0.6 0.6" markrgb="1 1 1"/>
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1=".4 .4 .4" rgb2=".6 .6 .6"
|
||||
width="512" height="512"/>
|
||||
<material name='MatPlane' reflectance='0.3' texture="texplane" texrepeat="1 1" texuniform="true"/>
|
||||
<material name='MatPlane' reflectance='0.3' texture="texplane" texrepeat="1 1" texuniform="true"
|
||||
rgba=".7 .7 .7 1"/>
|
||||
<material name='object1' texture="texgeom" texuniform="true" rgba=".4 .9 .6 1" />
|
||||
<material name='object2' texture="texgeom" texuniform="true" rgba=".4 .6 .9 1" />
|
||||
<material name='object3' texture="texgeom" texuniform="true" rgba=".4 .9 .9 1" />
|
||||
@@ -73,7 +74,7 @@
|
||||
|
||||
<worldbody>
|
||||
<light directional="true" diffuse=".8 .8 .8" pos="0 0 10" dir="0 0 -10"/>
|
||||
<geom pos="0 0 0" type="plane" size="3 3 .5" rgba=".7 .7 .7 1" material="MatPlane"/>
|
||||
<geom pos="0 0 0" type="plane" size="3 3 .5" material="MatPlane"/>
|
||||
<geom class="border" fromto="-3 3 0 3 3 0" />
|
||||
<geom class="border" fromto="-3 -3 0 3 -3 0" />
|
||||
<geom class="border" fromto="3 3 0 3 -3 0" />
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [2.1.1] - 2022-02-02
|
||||
|
||||
### Initial release
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0f2a83d7d240967bb5945d478c8b7f5
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19efbd5e1b0bbc43b9f6a3defa20bb80
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5a5ea2338a298b5e94d5f5653c0b85b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[CustomPropertyDrawer(typeof(AbsoluteValueAttribute))]
|
||||
public class AbsoluteValuePropertyDrawer : PropertyDrawer {
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
property.floatValue = Mathf.Abs(EditorGUI.FloatField(position, label, property.floatValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac61b66fbbd264beebb1df6cd423d122
|
||||
timeCreated: 1538126279
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[CustomEditor(typeof(MjActuator))]
|
||||
[CanEditMultipleObjects]
|
||||
public class MjActuatorEditor : Editor {
|
||||
private bool _showCustomParams = false;
|
||||
private bool _showDebugValues = false;
|
||||
private bool _showDynPrm = false;
|
||||
private bool _showGainPrm = false;
|
||||
private bool _showBiasValues = false;
|
||||
|
||||
// General actuator properties.
|
||||
private SerializedProperty _dynType;
|
||||
private SerializedProperty _gainType;
|
||||
private SerializedProperty _biasType;
|
||||
private SerializedProperty _dynPrm;
|
||||
private SerializedProperty _gainPrm;
|
||||
private SerializedProperty _biasPrm;
|
||||
// Position actuator properties.
|
||||
private SerializedProperty _kp;
|
||||
// Velocity actuator properties.
|
||||
private SerializedProperty _kv;
|
||||
// Cylinder actuator properties.
|
||||
private SerializedProperty _cylinderTimeConst;
|
||||
private SerializedProperty _area;
|
||||
private SerializedProperty _diameter;
|
||||
private SerializedProperty _bias;
|
||||
// Cylinder actuator properties.
|
||||
private SerializedProperty _muscleTimeConst;
|
||||
private SerializedProperty _range;
|
||||
private SerializedProperty _force;
|
||||
private SerializedProperty _scale;
|
||||
private SerializedProperty _lmin;
|
||||
private SerializedProperty _lmax;
|
||||
private SerializedProperty _vmax;
|
||||
private SerializedProperty _fpmax;
|
||||
private SerializedProperty _fvmax;
|
||||
|
||||
public void OnEnable() {
|
||||
_showCustomParams = false;
|
||||
_showDebugValues = false;
|
||||
_showDynPrm = false;
|
||||
_showGainPrm = false;
|
||||
_showBiasValues = false;
|
||||
|
||||
var customParams = serializedObject.FindProperty("CustomParams");
|
||||
// General actuator properties.
|
||||
_dynType = customParams.FindPropertyRelative("DynType");
|
||||
_gainType = customParams.FindPropertyRelative("GainType");
|
||||
_biasType = customParams.FindPropertyRelative("BiasType");
|
||||
_dynPrm = customParams.FindPropertyRelative("DynPrm");
|
||||
_gainPrm = customParams.FindPropertyRelative("GainPrm");
|
||||
_biasPrm = customParams.FindPropertyRelative("BiasPrm");
|
||||
// Position actuator properties.
|
||||
_kp = customParams.FindPropertyRelative("Kp");
|
||||
// Velocity actuator properties.
|
||||
_kv = customParams.FindPropertyRelative("Kv");
|
||||
// Cylinder actuator properties.
|
||||
_cylinderTimeConst = customParams.FindPropertyRelative("CylinderTimeConst");
|
||||
_area = customParams.FindPropertyRelative("Area");
|
||||
_diameter = customParams.FindPropertyRelative("Diameter");
|
||||
_bias = customParams.FindPropertyRelative("Bias");
|
||||
// Cylinder actuator properties.
|
||||
_muscleTimeConst = customParams.FindPropertyRelative("MuscleTimeConst");
|
||||
_range = customParams.FindPropertyRelative("Range");
|
||||
_force = customParams.FindPropertyRelative("Force");
|
||||
_scale = customParams.FindPropertyRelative("Scale");
|
||||
_lmin = customParams.FindPropertyRelative("LMin");
|
||||
_lmax = customParams.FindPropertyRelative("LMax");
|
||||
_vmax = customParams.FindPropertyRelative("VMax");
|
||||
_fpmax = customParams.FindPropertyRelative("FpMax");
|
||||
_fvmax = customParams.FindPropertyRelative("FvMax");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
serializedObject.Update();
|
||||
foreach (var target in serializedObject.targetObjects) {
|
||||
EditActuator(target as MjActuator);
|
||||
}
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void EditActuator(MjActuator actuator) {
|
||||
DrawDefaultInspector();
|
||||
_showCustomParams = EditorGUILayout.Foldout(_showCustomParams, "Custom Params");
|
||||
if (_showCustomParams) {
|
||||
EditCustomParams(actuator.Type, actuator.CustomParams);
|
||||
}
|
||||
_showDebugValues = EditorGUILayout.Foldout(_showDebugValues, "Debug");
|
||||
if (_showDebugValues) {
|
||||
using (new EditorGUI.DisabledScope(true)) {
|
||||
EditorGUILayout.FloatField("Length", actuator.Length);
|
||||
EditorGUILayout.FloatField("Velocity", actuator.Velocity);
|
||||
EditorGUILayout.FloatField("Force", actuator.Force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EditCustomParams(MjActuator.ActuatorType type,
|
||||
MjActuator.CustomParameters parameters) {
|
||||
switch (type) {
|
||||
case MjActuator.ActuatorType.General: {
|
||||
EditGeneralParams(parameters);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Position: {
|
||||
EditPositionParams(parameters);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Velocity: {
|
||||
EditVelocityParams(parameters);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Cylinder: {
|
||||
EditCylinderParams(parameters);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Muscle: {
|
||||
EditMuscleParams(parameters);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EditGeneralParams(MjActuator.CustomParameters parameters) {
|
||||
EditorGUILayout.PropertyField(_dynType);
|
||||
EditorGUILayout.PropertyField(_gainType);
|
||||
EditorGUILayout.PropertyField(_biasType);
|
||||
|
||||
_showDynPrm = EditorGUILayout.Foldout(_showDynPrm, "DynPrm");
|
||||
if (_showDynPrm) {
|
||||
for (var i = 0; i < parameters.DynPrm.Count; ++i) {
|
||||
EditorGUILayout.PropertyField(_dynPrm.GetArrayElementAtIndex(i));
|
||||
}
|
||||
}
|
||||
_showGainPrm = EditorGUILayout.Foldout(_showGainPrm, "GainPrm");
|
||||
if (_showGainPrm) {
|
||||
for (var i = 0; i < parameters.GainPrm.Count; ++i) {
|
||||
EditorGUILayout.PropertyField(_gainPrm.GetArrayElementAtIndex(i));
|
||||
}
|
||||
}
|
||||
_showBiasValues = EditorGUILayout.Foldout(_showBiasValues, "BiasPrm");
|
||||
if (_showBiasValues) {
|
||||
for (var i = 0; i < parameters.BiasPrm.Count; ++i) {
|
||||
EditorGUILayout.PropertyField(_biasPrm.GetArrayElementAtIndex(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EditPositionParams(MjActuator.CustomParameters parameters) {
|
||||
EditorGUILayout.PropertyField(_kp);
|
||||
}
|
||||
|
||||
private void EditVelocityParams(MjActuator.CustomParameters parameters) {
|
||||
EditorGUILayout.PropertyField(_kv);
|
||||
}
|
||||
|
||||
private void EditCylinderParams(MjActuator.CustomParameters parameters) {
|
||||
EditorGUILayout.PropertyField(_cylinderTimeConst);
|
||||
EditorGUILayout.PropertyField(_area);
|
||||
EditorGUILayout.PropertyField(_diameter);
|
||||
_showBiasValues = EditorGUILayout.Foldout(_showBiasValues, "Bias");
|
||||
if (_showBiasValues) {
|
||||
for (var i = 0; i < parameters.Bias.Length; ++i) {
|
||||
EditorGUILayout.PropertyField(_bias.GetArrayElementAtIndex(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EditMuscleParams(MjActuator.CustomParameters parameters) {
|
||||
EditorGUILayout.PropertyField(_muscleTimeConst, new GUIContent("TimeConst"));
|
||||
EditorGUILayout.PropertyField(_range);
|
||||
EditorGUILayout.PropertyField(_force);
|
||||
EditorGUILayout.PropertyField(_scale);
|
||||
EditorGUILayout.PropertyField(_lmin);
|
||||
EditorGUILayout.PropertyField(_lmax);
|
||||
EditorGUILayout.PropertyField(_vmax);
|
||||
EditorGUILayout.PropertyField(_fpmax);
|
||||
EditorGUILayout.PropertyField(_fvmax);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76f90e10df489475490b387a53dd88da
|
||||
timeCreated: 1547050687
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[CustomEditor(typeof(MjGeom), true)]
|
||||
[CanEditMultipleObjects]
|
||||
public class MjGeomEditor : MjShapeComponentEditor {
|
||||
public override void OnInspectorGUI() {
|
||||
serializedObject.Update();
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("Mass"));
|
||||
EditorGUILayout.PropertyField(serializedObject.FindProperty("Density"));
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
base.OnInspectorGUI();
|
||||
}
|
||||
|
||||
[MenuItem("CONTEXT/MjGeom/Add mesh renderer")]
|
||||
private static void AddMeshComponents(MenuCommand menuCommand) {
|
||||
var geom = menuCommand.context as MjGeom;
|
||||
geom.gameObject.AddComponent<MjMeshFilter>();
|
||||
geom.gameObject.AddComponent<MeshRenderer>().material = new Material(Shader.Find("Diffuse"));
|
||||
}
|
||||
|
||||
[MenuItem("CONTEXT/MjGeom/Convert to free object")]
|
||||
private static void ConvertToFreeObject(MenuCommand menuCommand) {
|
||||
var geom = menuCommand.context as MjGeom;
|
||||
if (geom.GetComponentInParent<MjBody>()) {
|
||||
Debug.LogError("This geom already has a Body parent.", geom.GetComponentInParent<MjBody>());
|
||||
return;
|
||||
}
|
||||
var parent = new GameObject(geom.gameObject.name + " Body").transform;
|
||||
var root = geom.transform.root;
|
||||
if (root != geom.transform) {
|
||||
parent.parent = geom.transform.parent;
|
||||
}
|
||||
parent.gameObject.AddComponent<MjBody>();
|
||||
var joint = new GameObject("Free Joint").AddComponent<MjFreeJoint>();
|
||||
joint.transform.parent = parent;
|
||||
geom.transform.parent = parent;
|
||||
}
|
||||
|
||||
[MenuItem("CONTEXT/Collider/Add a matching MuJoCo geom")]
|
||||
private static void AddMatchingGeom(MenuCommand menuCommand) {
|
||||
var collider = menuCommand.context as Collider;
|
||||
collider.enabled = false;
|
||||
var geom = collider.gameObject.AddComponent<MjGeom>();
|
||||
if (collider as BoxCollider) {
|
||||
geom.ShapeType = MjShapeComponent.ShapeTypes.Box;
|
||||
} else if (collider as SphereCollider) {
|
||||
geom.ShapeType = MjShapeComponent.ShapeTypes.Sphere;
|
||||
} else if (collider as CapsuleCollider) {
|
||||
geom.ShapeType = MjShapeComponent.ShapeTypes.Capsule;
|
||||
} else if (collider as MeshCollider) {
|
||||
geom.ShapeType = MjShapeComponent.ShapeTypes.Mesh;
|
||||
((MjMeshShape)geom.Shape).Mesh = ((MeshCollider)collider).sharedMesh;
|
||||
} else {
|
||||
Debug.LogError("Collider type not supported yet.", collider);
|
||||
collider.enabled = true;
|
||||
GameObject.DestroyImmediate(geom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5409b149ad694715a57f4dc8bb7a857
|
||||
timeCreated: 1552041114
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public static class MjHandles {
|
||||
|
||||
// Renders a visualization of an axis, anchored at the specified world space origin.
|
||||
//
|
||||
// Args:
|
||||
// origin: World space coordinates of the anchoring point for the visualization.
|
||||
// direction: Axis direction.
|
||||
public static void Axis(Vector3 origin, Vector3 direction) {
|
||||
var size = HandleUtility.GetHandleSize(origin) * 0.3f;
|
||||
var axisEnd = origin + direction * size;
|
||||
var capRotation = Quaternion.LookRotation(direction);
|
||||
Handles.DrawLine(origin, axisEnd);
|
||||
Handles.ConeHandleCap(0, axisEnd, capRotation, size, EventType.Repaint);
|
||||
}
|
||||
|
||||
// Renders a visualization of linear (translational) limits, anchored at the specified world
|
||||
// space origin.
|
||||
//
|
||||
// Args:
|
||||
// origin: World space coordinates of the anchoring point for the visualization.
|
||||
// lower: Distance from the origin below, where the limit expires.
|
||||
// upper: Distance from the origin above, where the limit expires.
|
||||
// axis: Axis along which the limit works.
|
||||
public static void LinearLimits(Vector3 origin, float lower, float upper, Vector3 axis) {
|
||||
if (upper > lower) {
|
||||
var discRadius = HandleUtility.GetHandleSize(origin) * 0.7f;
|
||||
var startPosition = origin + axis * lower;
|
||||
var endPosition = origin + axis * upper;
|
||||
Handles.DrawSolidDisc(startPosition, axis, discRadius);
|
||||
Handles.DrawSolidDisc(endPosition, axis, discRadius);
|
||||
Handles.DrawLine(origin, endPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a810c29762134ebb9bbee3903172764
|
||||
timeCreated: 1537794456
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[CustomEditor(typeof(MjHingeJoint))]
|
||||
public class MjHingeJointEditor : Editor {
|
||||
private MjHingeJoint _joint;
|
||||
|
||||
protected virtual void OnEnable() {
|
||||
_joint = (MjHingeJoint)target;
|
||||
}
|
||||
|
||||
protected virtual void OnSceneGUI() {
|
||||
DrawHandles(_joint);
|
||||
}
|
||||
|
||||
public static void DrawHandles(MjHingeJoint joint) {
|
||||
Handles.color = Color.yellow;
|
||||
MjHandles.Axis(joint.transform.position, joint.RotationAxis);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdaa159fcf7064108aed2229bc39ac0d
|
||||
timeCreated: 1537794793
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,176 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// During play mode, in the scene view, with a MjBody component selected, holding down control
|
||||
// and left mouse drag will apply a force on the body. Holding shift down will change the applied
|
||||
// force direction between World XZ plane and Y[camera-up].
|
||||
|
||||
[CustomEditor(typeof(MjBody))]
|
||||
public class MjMouseSpring : Editor {
|
||||
private bool _lastShiftKeyState = false;
|
||||
|
||||
private Plane _mouseDragPlane;
|
||||
private Vector3 _mouseDragCurrentPoint = Vector3.negativeInfinity;
|
||||
|
||||
private Color _translucentRed = new Color(1, 0, 0, 0.1f);
|
||||
|
||||
public void OnDisable() {
|
||||
// If we're still the hot control at this stage, we need to release.
|
||||
int uniqueID = GUIUtility.GetControlID(FocusType.Passive);
|
||||
if (GUIUtility.hotControl == uniqueID) {
|
||||
GUIUtility.hotControl = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDragOriginAndDragPlane(Vector3 planeOrigin, Vector3 normal) {
|
||||
normal[1] = 0;
|
||||
normal = _lastShiftKeyState ? Vector3.up : normal;
|
||||
_mouseDragPlane.SetNormalAndPosition(normal, planeOrigin);
|
||||
}
|
||||
|
||||
private void UpdatePositionOnDragPlane(Vector3 mousePosition) {
|
||||
float rayDist;
|
||||
Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);
|
||||
|
||||
if (_mouseDragPlane.Raycast(ray, out rayDist)) {
|
||||
_mouseDragCurrentPoint = ray.GetPoint(rayDist);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMouseSpringGui(Vector3 bodyPosition, Vector3 direction, Camera renderCamera) {
|
||||
// Update and draw the drag indicator.
|
||||
var rot = Quaternion.FromToRotation(Vector3.up, direction);
|
||||
|
||||
// Backup defaults.
|
||||
var backupColour = Handles.color;
|
||||
|
||||
// Update the drag plane indicator disc and outline...
|
||||
var discRadius = (_mouseDragCurrentPoint - bodyPosition).magnitude;
|
||||
|
||||
Handles.color = Color.red;
|
||||
Handles.DrawWireDisc(bodyPosition, _mouseDragPlane.normal, discRadius);
|
||||
Handles.color = _translucentRed;
|
||||
Handles.DrawSolidDisc(bodyPosition, _mouseDragPlane.normal, discRadius);
|
||||
|
||||
// ...and give it a phat poly line to show the drag anchor origin.
|
||||
Handles.color = Color.white;
|
||||
Handles.DrawAAPolyLine(6, new Vector3[] { bodyPosition, _mouseDragCurrentPoint });
|
||||
|
||||
// Restore defaults.
|
||||
Handles.color = backupColour;
|
||||
}
|
||||
|
||||
public unsafe void OnSceneGUI() {
|
||||
if (!Application.isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentEvent = UnityEngine.Event.current;
|
||||
|
||||
// Cache the hot control to determine whether we're currently capturing mouse input.
|
||||
int uniqueID = GUIUtility.GetControlID(FocusType.Passive);
|
||||
|
||||
// Mouse spring is active if the control key is held down and the user is dragging the
|
||||
// left mouse button, or if we're already in the process of capturing mouse input.
|
||||
bool mouseSpringActive = GUIUtility.hotControl == uniqueID;
|
||||
bool mouseSpringStarting = currentEvent.control && currentEvent.button == 0;
|
||||
if (!mouseSpringStarting && !mouseSpringActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sceneCamera = SceneView.currentDrawingSceneView?.camera;
|
||||
if (sceneCamera == null) {
|
||||
Debug.LogError("SceneView.currentDrawingSceneView is null");
|
||||
return;
|
||||
}
|
||||
|
||||
MjBody body = target as MjBody;
|
||||
Vector3 bodyPosition =
|
||||
body != null ? body.transform.position : Vector3.zero;
|
||||
var scene = MjScene.Instance;
|
||||
|
||||
switch (currentEvent.type) {
|
||||
case EventType.MouseDown:
|
||||
if (EditorWindow.mouseOverWindow == UnityEditor.EditorWindow.GetWindow<SceneView>()) {
|
||||
// The mouse was pressed in the scene view, so start capturing all mouse input until
|
||||
// the mouse button's released.
|
||||
GUIUtility.hotControl = uniqueID;
|
||||
|
||||
_lastShiftKeyState = false;
|
||||
SetDragOriginAndDragPlane(bodyPosition, -sceneCamera.transform.forward);
|
||||
|
||||
currentEvent.Use();
|
||||
}
|
||||
return;
|
||||
|
||||
case EventType.MouseDrag:
|
||||
if (mouseSpringActive) {
|
||||
// We're currently capturing all mouse input, so consume the event.
|
||||
GUIUtility.hotControl = uniqueID;
|
||||
currentEvent.Use();
|
||||
}
|
||||
return;
|
||||
|
||||
case EventType.MouseUp:
|
||||
if (mouseSpringActive) {
|
||||
// We're still capturing all mouse input so consume the event, but we can release our
|
||||
// control over capturing input now as the mouse button's been released.
|
||||
GUIUtility.hotControl = 0;
|
||||
currentEvent.Use();
|
||||
// as opposed to unity's addforce, xfrc_applied is persistent
|
||||
scene.Data->xfrc_applied[6*body.MujocoId + 0] = 0;
|
||||
scene.Data->xfrc_applied[6*body.MujocoId + 1] = 0;
|
||||
scene.Data->xfrc_applied[6*body.MujocoId + 2] = 0;
|
||||
}
|
||||
return;
|
||||
|
||||
case EventType.Repaint: {
|
||||
if (mouseSpringActive) {
|
||||
|
||||
if (currentEvent.shift != _lastShiftKeyState) {
|
||||
_lastShiftKeyState = currentEvent.shift;
|
||||
SetDragOriginAndDragPlane(bodyPosition, -sceneCamera.transform.forward);
|
||||
}
|
||||
|
||||
// Raycast towards the drag plane to update _mouseDragCurrentPoint.
|
||||
UpdatePositionOnDragPlane(currentEvent.mousePosition);
|
||||
|
||||
Vector3 bodyVel = Vector3.one;
|
||||
double[] mjBodyVel = new double[6];
|
||||
fixed (double* res = mjBodyVel) {
|
||||
MujocoLib.mj_objectVelocity(
|
||||
scene.Model, scene.Data, (int)MujocoLib.mjtObj.mjOBJ_BODY, body.MujocoId, res, 0);
|
||||
// linear velocity is in the last 3 entries
|
||||
bodyVel = MjEngineTool.UnityVector3(res, 1);
|
||||
}
|
||||
|
||||
float springStiffness = 100;
|
||||
var settings = MjGlobalSettings.Instance;
|
||||
if (settings) {
|
||||
springStiffness = settings.MouseSpringStiffness;
|
||||
}
|
||||
|
||||
Vector3 delta = _mouseDragCurrentPoint - bodyPosition;
|
||||
float mass = 1.0f / (float)scene.Model->body_invweight0[2*body.MujocoId];
|
||||
Vector3 unityForce = delta * springStiffness * mass;
|
||||
unityForce -= bodyVel * Mathf.Sqrt(springStiffness) * mass;
|
||||
Vector3 mjForce = MjEngineTool.MjVector3(unityForce);
|
||||
scene.Data->xfrc_applied[6*body.MujocoId + 0] = mjForce.x;
|
||||
scene.Data->xfrc_applied[6*body.MujocoId + 1] = mjForce.y;
|
||||
scene.Data->xfrc_applied[6*body.MujocoId + 2] = mjForce.z;
|
||||
|
||||
// Draw and immediately force a repaint.
|
||||
DrawMouseSpringGui(body.transform.position, delta, sceneCamera);
|
||||
SceneView.RepaintAll();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c4ae8cbdd0cac0a387364d88408d932
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[CustomEditor(typeof(MjShapeComponent), true)]
|
||||
[CanEditMultipleObjects]
|
||||
public class MjShapeComponentEditor : Editor {
|
||||
|
||||
public override void OnInspectorGUI() {
|
||||
|
||||
serializedObject.Update();
|
||||
var shapeType = serializedObject.FindProperty("ShapeType");
|
||||
EditorGUILayout.PropertyField(shapeType);
|
||||
EditorGUILayout.PropertyField(
|
||||
serializedObject.FindProperty($"{shapeType.enumDisplayNames[shapeType.enumValueIndex]}"),
|
||||
includeChildren: true);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21af9fef28ac0899e982e791916b5fc5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[CustomEditor(typeof(MjSlideJoint))]
|
||||
public class MjSlideJointEditor : Editor {
|
||||
private MjSlideJoint _joint;
|
||||
|
||||
protected virtual void OnEnable() {
|
||||
_joint = (MjSlideJoint)target;
|
||||
}
|
||||
|
||||
protected virtual void OnSceneGUI() {
|
||||
DrawHandles(_joint);
|
||||
}
|
||||
|
||||
// Draw the handles for the slide joint.
|
||||
public static void DrawHandles(MjSlideJoint joint) {
|
||||
// Draw the handles.
|
||||
if (joint.Settings.Solver.Limited) {
|
||||
Handles.color = Color.blue;
|
||||
MjHandles.LinearLimits(joint.transform.position, joint.RangeLower, joint.RangeUpper,
|
||||
joint.SlideAxis);
|
||||
}
|
||||
Handles.color = Color.yellow;
|
||||
MjHandles.Axis(joint.transform.position, joint.SlideAxis);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf7c14a263938456e9393a8d0fce7b9f
|
||||
timeCreated: 1538066332
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 974c7b5fe753877ebaa54ec26e113e1c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// Mujoco scenes importer window.
|
||||
public class MjImporterEditorWindow : EditorWindow {
|
||||
[MenuItem("Assets/Import MuJoCo Scene")]
|
||||
public static void Apply() {
|
||||
string path = EditorUtility.OpenFilePanel("Select an MJCF model", "", "xml");
|
||||
if (!string.IsNullOrEmpty(path)) {
|
||||
var importer = new MjImporterWithAssets();
|
||||
importer.ImportFile(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85ddf6116132e4c128ed82ac6034ce4a
|
||||
timeCreated: 1538581084
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// API for importing Mujoco XML files into Unity scenes.
|
||||
public class MjImporterWithAssets : MjcfImporter {
|
||||
|
||||
private const string _semiTransparentMaterialName = "mujoco_semitransparent_template";
|
||||
|
||||
private string _sourceMeshesDir;
|
||||
private string _targetMeshesDir;
|
||||
private string _targetAssetDir;
|
||||
private unsafe MujocoLib.mjModel_* _mjModel = null;
|
||||
|
||||
// Imports the scene from the specified file, which should be a well-formed MJCF document.
|
||||
// The imported scene will be placed under a single node with the requested name assigned.
|
||||
//
|
||||
// Args:
|
||||
// filePath: Path to XML file.
|
||||
//
|
||||
// Throws:
|
||||
// Exception if the parsed XML is malformed or contains rough errors. If an exception is thrown,
|
||||
// the entire imported scene will be automatically deleted.
|
||||
// TODO(etom) - reconsider unencouraged pattern of validation through exception side-effects.
|
||||
public unsafe GameObject ImportFile(string filePath) {
|
||||
// If MuJoCo can't parse the mjcfString, we abort the entire process.
|
||||
// MjEngineTool.LoadModelFromString throws an exception when MuJoCo fails to parse the provided
|
||||
// mjcfString.
|
||||
var name = Path.GetFileNameWithoutExtension(filePath) + $"{UnityEngine.Random.Range(0,999)}";
|
||||
string newPath = Path.Combine(Application.temporaryCachePath, $"{name}.xml");
|
||||
_mjModel = MjEngineTool.LoadModelFromFile(filePath);
|
||||
MjEngineTool.SaveModelToFile(newPath, _mjModel);
|
||||
Debug.Log($"Imported MJCF loaded, saved to {newPath}");
|
||||
string mjcfString = File.ReadAllText(newPath);
|
||||
GameObject root = null;
|
||||
try {
|
||||
root = ImportString(mjcfString, name, filePath);
|
||||
} finally {
|
||||
MujocoLib.mj_deleteModel(_mjModel);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
public GameObject ImportString(
|
||||
string mjcfString, string name = null, string filePath = null) {
|
||||
var mjcfXml = new XmlDocument();
|
||||
mjcfXml.LoadXml(mjcfString);
|
||||
// Determine the folder where the meshes are stored.
|
||||
ConfigureMeshPath(filePath, name, mjcfXml);
|
||||
GameObject sceneRoot = null;
|
||||
try {
|
||||
sceneRoot = ImportXml(mjcfXml, name);
|
||||
} catch (Exception) {
|
||||
// We consider any error as critical, and end the import process immediately, cleaning up
|
||||
// any assets created.
|
||||
AssetDatabase.DeleteAsset(_targetAssetDir);
|
||||
}
|
||||
return sceneRoot;
|
||||
}
|
||||
|
||||
protected override void ParseRoot(GameObject parentObject, XmlElement parentNode) {
|
||||
|
||||
// This makes no references, and should be parsed first.
|
||||
var assetNode = parentNode.SelectSingleNode("asset") as XmlElement;
|
||||
if (assetNode != null) {
|
||||
ParseAssets(assetNode);
|
||||
}
|
||||
base.ParseRoot(parentObject, parentNode);
|
||||
}
|
||||
|
||||
protected override void ParseGeom(GameObject parentObject, XmlElement child) {
|
||||
var gameObject = CreateGameObjectWithUniqueName<MjGeom>(parentObject, child);
|
||||
gameObject.AddComponent<MjMeshFilter>();
|
||||
var renderer = gameObject.AddComponent<MeshRenderer>();
|
||||
ResolveOrCreateMaterial(renderer, child);
|
||||
}
|
||||
|
||||
private void ConfigureMeshPath(string path, string projectName, XmlDocument mjcf) {
|
||||
// Crate the mesh target directory.
|
||||
_targetMeshesDir = Path.Combine(
|
||||
Application.dataPath, "Local", "MjImports", projectName, "Resources");
|
||||
_targetAssetDir = Path.Combine("Assets", "Local", "MjImports", projectName, "Resources");
|
||||
if (!Directory.Exists(_targetMeshesDir)) {
|
||||
Directory.CreateDirectory(_targetMeshesDir);
|
||||
}
|
||||
if (string.IsNullOrEmpty(path)) { // we're loading a string
|
||||
return;
|
||||
}
|
||||
_sourceMeshesDir = Path.GetDirectoryName(path);
|
||||
var compilerNode = mjcf.SelectSingleNode("/mujoco/compiler") as XmlElement;
|
||||
if (compilerNode != null) {
|
||||
// Parse the location of meshes
|
||||
var relativeMeshDir = compilerNode.GetStringAttribute("meshdir", defaultValue: string.Empty);
|
||||
_sourceMeshesDir = Path.Combine(_sourceMeshesDir, relativeMeshDir);
|
||||
}
|
||||
Debug.Log(
|
||||
$"Meshes locations: source = {_sourceMeshesDir}, target = {_targetMeshesDir}");
|
||||
}
|
||||
|
||||
private void ParseAssets(XmlElement parentNode) {
|
||||
foreach (var child in parentNode.SelectNodes("descendant::mesh").OfType<XmlElement>()) {
|
||||
_modifiers.ApplyModifiersToElement(child);
|
||||
ParseMesh(child);
|
||||
}
|
||||
foreach (var child in parentNode.SelectNodes("descendant::material").OfType<XmlElement>()) {
|
||||
_modifiers.ApplyModifiersToElement(child);
|
||||
ParseMaterial(child);
|
||||
}
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
private void ImportMeshFromModel(int meshIndex) {
|
||||
}
|
||||
|
||||
// Using mesh assets involves:
|
||||
// (1) Copying the asset to a Resources folder, and rescaling it during that operation.
|
||||
// (2) Allowing Unity to parse it using a registered asset importer (STLMeshImporter).
|
||||
// (3) Loading that asset as a Mesh resource when the referring geom is parsed.
|
||||
//
|
||||
// We're also using a dedicated folder to deploy the meshes that are being imported, so that the
|
||||
// user can find all meshes loaded by importing a specific model.
|
||||
private void ParseMesh(XmlElement parentNode) {
|
||||
if (parentNode.HasAttribute("vertex")) {
|
||||
throw new NotImplementedException("XML with explicit mesh info not supported yet.");
|
||||
}
|
||||
var fileName = parentNode.GetStringAttribute("file");
|
||||
// If we want to use the element name as the asset name, we must sanitize it:
|
||||
var unsanitizedAssetReferenceName =
|
||||
parentNode.GetStringAttribute("name", defaultValue: string.Empty);
|
||||
var assetReferenceName = MjEngineTool.Sanitize(unsanitizedAssetReferenceName);
|
||||
var sourceFilePath = Path.Combine(_sourceMeshesDir, fileName);
|
||||
var targetFilePath = Path.Combine(_targetMeshesDir, assetReferenceName + ".stl");
|
||||
if (File.Exists(targetFilePath)) {
|
||||
File.Delete(targetFilePath);
|
||||
}
|
||||
var scale = MjEngineTool.UnityVector3(
|
||||
parentNode.GetVector3Attribute("scale", defaultValue: Vector3.one));
|
||||
CopyMeshAndRescale(sourceFilePath, targetFilePath, scale);
|
||||
var assetPath = Path.Combine(_targetAssetDir, assetReferenceName + ".stl");
|
||||
// This asset path should be available because the MuJoCo compiler guarantees element names
|
||||
// are unique, but check for completeness (and in case sanitizing the name broke uniqueness):
|
||||
if (AssetDatabase.LoadMainAssetAtPath(assetPath) != null) {
|
||||
throw new Exception(
|
||||
$"Trying to import mesh {unsanitizedAssetReferenceName} but {assetPath} already exists.");
|
||||
}
|
||||
AssetDatabase.ImportAsset(assetPath);
|
||||
var copiedMesh = AssetDatabase.LoadMainAssetAtPath(assetPath) as Mesh;
|
||||
if (copiedMesh == null) {
|
||||
throw new Exception($"Mesh {assetPath} was not imported.");
|
||||
}
|
||||
copiedMesh.RecalculateNormals();
|
||||
copiedMesh.RecalculateTangents();
|
||||
copiedMesh.RecalculateBounds();
|
||||
}
|
||||
|
||||
private void CopyMeshAndRescale(
|
||||
string sourceFilePath, string targetFilePath, Vector3 scale) {
|
||||
var originalMeshBytes = File.ReadAllBytes(sourceFilePath);
|
||||
var mesh = StlMeshParser.ParseBinary(originalMeshBytes, scale);
|
||||
var rescaledMeshBytes = StlMeshParser.SerializeBinary(mesh);
|
||||
File.WriteAllBytes(targetFilePath, rescaledMeshBytes);
|
||||
}
|
||||
|
||||
private void ParseMaterial(XmlElement parentNode) {
|
||||
var rgba = parentNode.GetFloatArrayAttribute(
|
||||
"rgba", defaultValue: new float[] {1.0f, 1.0f, 1.0f, 1.0f});
|
||||
var emission = parentNode.GetFloatAttribute("emission", defaultValue: 0.0f);
|
||||
var reflectance = parentNode.GetFloatAttribute("reflectance", defaultValue: 0.0f);
|
||||
var specular = parentNode.GetFloatAttribute("specular", defaultValue: 0.5f);
|
||||
var shininess = parentNode.GetFloatAttribute("shininess", defaultValue: 0.5f);
|
||||
var unsanitizedName = parentNode.GetStringAttribute("name", defaultValue: string.Empty);
|
||||
var name = MjEngineTool.Sanitize(unsanitizedName);
|
||||
var albedo = new Color(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
|
||||
// Mujoco uses a Blinn/Phong shading model with the addition of reflectance. Unfortunately, at
|
||||
// the moment of writing this comment, Unity does not come with a compatible shader. The closest
|
||||
// results can be achieved using the Standard shader that implements the Cook-Torrence shading
|
||||
// model.
|
||||
Material material;
|
||||
if (rgba[3] < 1f) {
|
||||
material = new Material(AssetDatabase.LoadMainAssetAtPath(
|
||||
AssetDatabase.GUIDToAssetPath(
|
||||
AssetDatabase.FindAssets(_semiTransparentMaterialName)[0])) as Material);
|
||||
} else {
|
||||
material = new Material(Shader.Find("Standard"));
|
||||
}
|
||||
material.SetColor("_Color", albedo);
|
||||
material.SetFloat("_Metallic", reflectance);
|
||||
|
||||
// In order to convert the specular/shininess parameters into glossiness/roughness,
|
||||
// we perform a coarse approximation.
|
||||
// In Blinn/Phong model, Shininess corresponds to the width of the specular spot, while
|
||||
// the Specularity corresponds to its strength (how visible it is). In order to approximate it
|
||||
// with a single parameter Glossiness, we will assume that the largest value wins. The model
|
||||
// will break at the extremes (Spec~=1 & Shin~=0, Spec~=0 & Shin~=1), however it should be
|
||||
// representative for the intermediate values.
|
||||
float glossiness = Math.Max(specular, shininess);
|
||||
// We observe that any reflective material is automatically glossy, by the property of not
|
||||
// having rough surface that would scatter the incoming light.
|
||||
// Instead of modifying the Shininess parameter however, we're dirrectly modifying
|
||||
// the glossiness by bringing the value closer to the upper boundary.
|
||||
glossiness = (1.0f - reflectance) * glossiness + reflectance;
|
||||
material.SetFloat("_Glossiness", glossiness);
|
||||
|
||||
// We choose to define a simple emission model that only emits light, without scaling
|
||||
// the brightness of the defined color. If the user requires, they should tweak the material
|
||||
// settings manually.
|
||||
if (emission > 0.5f) {
|
||||
material.EnableKeyword("_EMISSION");
|
||||
material.SetColor("_EmissionColor", albedo);
|
||||
}
|
||||
var assetPath = Path.Combine(_targetAssetDir, name + ".mat");
|
||||
if (AssetDatabase.LoadMainAssetAtPath(assetPath) != null) {
|
||||
throw new Exception(
|
||||
$"Trying to create material {unsanitizedName} but {assetPath} already exists.");
|
||||
}
|
||||
AssetDatabase.CreateAsset(material, assetPath);
|
||||
}
|
||||
|
||||
// Loads a named material asset, or creates an ad-hoc material asset for the specific node.
|
||||
private void ResolveOrCreateMaterial(MeshRenderer renderer, XmlElement parentNode) {
|
||||
// When the asset was parsed and stored its name was sanitized, so we should load it using a
|
||||
// sanitized name:
|
||||
var materialName =
|
||||
MjEngineTool.Sanitize(parentNode.GetStringAttribute("material", defaultValue: string.Empty));
|
||||
Material material = null;
|
||||
if (!string.IsNullOrEmpty(materialName)) {
|
||||
var assetPath = Path.Combine(_targetAssetDir, materialName + ".mat");
|
||||
material = (Material)AssetDatabase.LoadAssetAtPath(assetPath, typeof(Material));
|
||||
} else {
|
||||
// Nodes may contain inlined color definitions, which override the assigned material colors.
|
||||
if (parentNode.HasAttribute("rgba")) {
|
||||
// We need a bespoke copy of the material from the database for this particular node.
|
||||
var rgba = parentNode.GetFloatArrayAttribute(
|
||||
"rgba", defaultValue: new float[] {1.0f, 1.0f, 1.0f, 1.0f});
|
||||
if (rgba[3] < 1f) {
|
||||
material = new Material(
|
||||
AssetDatabase.LoadMainAssetAtPath(
|
||||
AssetDatabase.GUIDToAssetPath(
|
||||
AssetDatabase.FindAssets(_semiTransparentMaterialName)[0])) as Material);
|
||||
} else {
|
||||
material = new Material(Shader.Find("Standard"));
|
||||
}
|
||||
material.color = new Color(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
// We use the geom's name, guaranteed to be unique, as the asset name.
|
||||
// If geom is nameless, use a random number.
|
||||
var name =
|
||||
MjEngineTool.Sanitize(parentNode.GetStringAttribute(
|
||||
"name", defaultValue: $"{UnityEngine.Random.Range(0, 1000000)}"));
|
||||
var assetPath = Path.Combine(_targetAssetDir, name + ".mat");
|
||||
if (AssetDatabase.LoadMainAssetAtPath(assetPath) != null) {
|
||||
throw new Exception(
|
||||
$"Creating a material asset for the geom {name}, but {assetPath} already exists.");
|
||||
}
|
||||
AssetDatabase.CreateAsset(material, assetPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
material = AssetDatabase.LoadMainAssetAtPath(assetPath) as Material;
|
||||
} else {
|
||||
material = DefaultMujocoMaterial;
|
||||
}
|
||||
}
|
||||
renderer.sharedMaterial = material;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0115d33f9ea9249e78644b1fa5cd7765
|
||||
timeCreated: 1538581083
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor.AssetImporters;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
// Importer for STL mesh files.
|
||||
[ScriptedImporter(version: 1, ext: "stl")]
|
||||
public class StlMeshImporter : ScriptedImporter {
|
||||
|
||||
public override void OnImportAsset(AssetImportContext ctx) {
|
||||
var assetName = Path.GetFileNameWithoutExtension(ctx.assetPath);
|
||||
var modelContents = File.ReadAllBytes(ctx.assetPath);
|
||||
|
||||
var mesh = StlMeshParser.ParseBinary(modelContents, Vector3.one);
|
||||
|
||||
ctx.AddObjectToAsset(assetName, mesh);
|
||||
ctx.SetMainObject(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9060c43f3694e454c873753ea0cc2f49
|
||||
timeCreated: 1550070047
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public static class BinaryReaderExtensions {
|
||||
public static Vector3 ReadVector3(this BinaryReader reader) {
|
||||
var x = reader.ReadSingle();
|
||||
var y = reader.ReadSingle();
|
||||
var z = reader.ReadSingle();
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
public static class BinaryWriterExtensions {
|
||||
public static void Write(this BinaryWriter writer, Vector3 val) {
|
||||
writer.Write(val.x);
|
||||
writer.Write(val.y);
|
||||
writer.Write(val.z);
|
||||
}
|
||||
}
|
||||
|
||||
public class StlMeshParser {
|
||||
|
||||
private const int _headerLength = 80;
|
||||
private const int _attributesSizeLength = 2;
|
||||
private const int _verticesPerTriangle = 3;
|
||||
private const int _unityLimitNumVerticesPerMesh = 65535;
|
||||
private const string _asciiFileTypeId = "solid";
|
||||
|
||||
private static Vector3 ToXZY(Vector3 v) => new Vector3(v.x, v.z, v.y);
|
||||
|
||||
// The binary STL format is described here: https://en.wikipedia.org/wiki/STL_(file_format)
|
||||
public static Mesh ParseBinary(byte[] stlFileContents, Vector3 scale) {
|
||||
var fileTypeId = System.Text.Encoding.UTF8.GetString(
|
||||
stlFileContents.Take(_asciiFileTypeId.Length).ToArray());
|
||||
if (fileTypeId == _asciiFileTypeId) {
|
||||
throw new IOException("Ascii STL file format is not supported.");
|
||||
}
|
||||
|
||||
using (var stream = new MemoryStream(stlFileContents)) {
|
||||
using (var reader = new BinaryReader(stream)) {
|
||||
reader.ReadBytes(_headerLength);
|
||||
var numTriangles = reader.ReadUInt32();
|
||||
var numVertices = numTriangles * _verticesPerTriangle;
|
||||
if (numVertices > _unityLimitNumVerticesPerMesh) {
|
||||
throw new IndexOutOfRangeException(
|
||||
"The mesh exceeds the number of vertices per mesh allowed by Unity. " +
|
||||
$"({numVertices} > {_unityLimitNumVerticesPerMesh})");
|
||||
}
|
||||
var triangleIndices = new List<int>(capacity: (int)numVertices);
|
||||
var vertices = new List<Vector3>(capacity: (int)numVertices);
|
||||
var normals = new List<Vector3>(capacity: (int)numVertices);
|
||||
for (var i = 0; i < numVertices; i += _verticesPerTriangle) {
|
||||
var triangleNormal = ToXZY(reader.ReadVector3());
|
||||
normals.AddRange(new[] { triangleNormal, triangleNormal, triangleNormal });
|
||||
vertices.AddRange(new[] {
|
||||
ToXZY(reader.ReadVector3()),
|
||||
ToXZY(reader.ReadVector3()),
|
||||
ToXZY(reader.ReadVector3()) });
|
||||
triangleIndices.AddRange(new[] {i, i + 2, i + 1});
|
||||
reader.ReadInt16(); // Read the unused attribute indices field.
|
||||
}
|
||||
|
||||
var mesh = new Mesh();
|
||||
mesh.vertices = vertices.ToArray();
|
||||
mesh.normals = normals.ToArray();
|
||||
mesh.triangles = triangleIndices.ToArray();
|
||||
mesh.vertices = mesh.vertices.Select(
|
||||
vertexPosition => Vector3.Scale(vertexPosition, scale)).ToArray();
|
||||
mesh.RecalculateNormals();
|
||||
mesh.RecalculateTangents();
|
||||
mesh.RecalculateBounds();
|
||||
return mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] SerializeBinary(Mesh mesh) {
|
||||
using (var stream = new MemoryStream()) {
|
||||
using (var writer = new BinaryWriter(stream)) {
|
||||
// Write the header. We only want to write the id and then pad the rest with zeros, up to 80
|
||||
// bytes.
|
||||
writer.Write(_asciiFileTypeId);
|
||||
writer.Write(new byte[_headerLength - _asciiFileTypeId.Length - 1]);
|
||||
|
||||
var numTriangles = mesh.triangles.Length / 3;
|
||||
writer.Write((int)numTriangles);
|
||||
|
||||
for (var i = 0; i < mesh.triangles.Length; i += _verticesPerTriangle) {
|
||||
// STL format uses face normals, while Unity Meshes use vertex normals. We need to convert
|
||||
// one into another by calculating a mean of vertex normals.
|
||||
var i1 = mesh.triangles[i];
|
||||
var i2 = mesh.triangles[i + 1];
|
||||
var i3 = mesh.triangles[i + 2];
|
||||
var faceNormal = (mesh.normals[i1] + mesh.normals[i2] + mesh.normals[i3]).normalized;
|
||||
writer.Write(ToXZY(faceNormal));
|
||||
|
||||
writer.Write(ToXZY(mesh.vertices[i1]));
|
||||
writer.Write(ToXZY(mesh.vertices[i3]));
|
||||
writer.Write(ToXZY(mesh.vertices[i2]));
|
||||
|
||||
writer.Write((short)0);
|
||||
}
|
||||
return stream.GetBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d443565163aa4ddea5d910f3517ecfb
|
||||
timeCreated: 1550063597
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Mujoco.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Mujoco"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7bdf927c2041ed42ba41bdbbf914db24
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
# Unity Plug-in
|
||||
|
||||
The MuJoCo Unity plug-in allows the Unity Editor and runtime to use the MuJoCo
|
||||
physics engine. Users can import MJCF files and edit the models in the Editor.
|
||||
The plug-in relies on Unity for most aspects -- assets, game logic, simulation
|
||||
time -- but uses MuJoCo to determine how objects move, giving the designer
|
||||
access to MuJoCo's full API. See documentation and installation instructions
|
||||
[here](https://mujoco.readthedocs.io/en/latest/unity.html).
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aefae1ccbed1b0597a0ac21f37888aa3
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa19c3a835699cca3a2b4f2aabbe14b7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21aeeb2df019248c78e85014384bd21f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mujoco {
|
||||
// An implementation of IList<T>, only missing the indexer and the count.
|
||||
public abstract class FixedSizeIListHelper<T> : IList<T> {
|
||||
public IEnumerator<T> GetEnumerator() {
|
||||
for (int i = 0; i < this.Count; ++i) {
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(T item) {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool Contains(T item) {
|
||||
for (int i = 0; i < Count; ++i) {
|
||||
if (this[i].Equals(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) {
|
||||
for (int i = 0; i < Count; ++i) {
|
||||
array[arrayIndex + i] = this[i];
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(T item) {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public int IndexOf(T item) {
|
||||
for (int i = 0; i < Count; ++i) {
|
||||
if (this[i].Equals(item)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Insert(int index, T item) {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index) {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public abstract int Count { get; }
|
||||
public abstract T this[int index] {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9015a95216e0843069042f712722cca6
|
||||
timeCreated: 1538057012
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEditor;
|
||||
using UnityEditor.PackageManager;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
public class MujocoBinaryRetriever {
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
static void SubscribeToEvent() {
|
||||
// This causes the method to be invoked after the Editor registers the new list of packages.
|
||||
Events.registeredPackages += RegisteredPackagesEventHandler;
|
||||
}
|
||||
|
||||
static void RegisteredPackagesEventHandler(
|
||||
PackageRegistrationEventArgs packageRegistrationEventArgs) {
|
||||
var mujocoPath = packageRegistrationEventArgs.added[0].assetPath;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
|
||||
if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/mujoco.dylib") == null) {
|
||||
File.Copy(
|
||||
"/Applications/MuJoCo.app/Contents/Frameworks" +
|
||||
"/MuJoCo.framework/Versions/Current/libmujoco.2.1.1.dylib",
|
||||
mujocoPath + "/mujoco.dylib");
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
|
||||
if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/libmujoco.so") == null) {
|
||||
File.Copy(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) +
|
||||
"/.mujoco/mujoco-2.1.1/lib/libmujoco_nogl.so.2.1.1",
|
||||
mujocoPath + "/libmujoco.so");
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
} else {
|
||||
if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/mujoco.dll") == null) {
|
||||
File.Copy(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) +
|
||||
"\\MuJoCo\\bin\\mujoco.dll",
|
||||
mujocoPath + "\\mujoco.dll");
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3328869c60ca872129794f55c2d5bd05
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bffff449e27e88308992f7b9ce821e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49cbe951a7bc505f39a1762fe932f068
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// Functionality that needs to be implemented for each shape type.
|
||||
public interface IMjShape {
|
||||
|
||||
// Generate the Mjcf representation of the shape. Infer the scale of the shape from the transform.
|
||||
void ToMjcf(XmlElement mjcf, Transform transform);
|
||||
|
||||
// Parse the shape settings from the specified Mjcf node.
|
||||
void FromMjcf(XmlElement mjcf);
|
||||
|
||||
// Build a parametric mesh (vertices, triangles) of this shape.
|
||||
Tuple<Vector3[], int[]> BuildMesh();
|
||||
|
||||
// Generate a timestamp that can be used to quickly compare if the settings of a shape
|
||||
// have changed.
|
||||
// The timestamp should also allow to distinguish between different types of shapes.
|
||||
//
|
||||
// Because most of shapes use at most 3 floating point values as parameters, we will
|
||||
// use the first 3 components of the returned Vector4 to store those values. We will store
|
||||
// the id of the shape in the 4th component. That id will be derrived from MjShapeComponent.ShapeTypes
|
||||
// enum.
|
||||
Vector4 GetChangeStamp();
|
||||
|
||||
// Draw a debug gizmo for the shape.
|
||||
void DebugDraw(Transform transform);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4b869d65e8404f3aa92042bbd7b9c43
|
||||
timeCreated: 1547647082
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,360 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public static class MeshGenerators {
|
||||
|
||||
// Creates a sphere mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// scale: Non-uniform sphere scale. Allows to create ellipsoid shapes.
|
||||
// numVerticalSlices: How many vertices should there be in a single horizontal slice.
|
||||
// numHorizontalSlices: How many horizontal slices should the sphere consist of.
|
||||
public static Tuple<Vector3[], int[]> BuildSphere(
|
||||
Vector3 scale, int numVerticalSlices = 16, int numHorizontalSlices = 16) {
|
||||
// Generate the vertices.
|
||||
Vector3[] vertices;
|
||||
int[] triangles;
|
||||
GenerateSphereMeshSlice(
|
||||
scale: scale,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
numHorizontalSlices: numHorizontalSlices,
|
||||
firstSliceY: -1.0f,
|
||||
lastSliceY: 1.0f,
|
||||
vertices: out vertices,
|
||||
triangles: out triangles);
|
||||
return Tuple.Create(vertices, triangles);
|
||||
}
|
||||
|
||||
// Creates a cylinder mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// radius: Radius of the cylider's body.
|
||||
// height: Height of the cylinder, from its bottom to its top base.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
public static Tuple<Vector3[], int[]> BuildCylinder(
|
||||
float radius, float height, int numVerticalSlices = 16) {
|
||||
Vector3[] bodyVertices;
|
||||
int[] bodyTriangles;
|
||||
GenerateCylinderBody(radius, height, numVerticalSlices, out bodyVertices, out bodyTriangles);
|
||||
|
||||
Vector3[] baseCapVertices;
|
||||
int[] baseCapTriangles;
|
||||
GenerateCylinderBaseCaps(
|
||||
radius, height, numVerticalSlices, out baseCapVertices, out baseCapTriangles);
|
||||
|
||||
var merger = new MeshMerger();
|
||||
merger.Add(bodyVertices, bodyTriangles);
|
||||
merger.Add(baseCapVertices, baseCapTriangles);
|
||||
return Tuple.Create(merger.Vertices, merger.Triangles);
|
||||
}
|
||||
|
||||
// Creates a capsule mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// radius: Radius of the capsule's body.
|
||||
// height: Height of the capsule, from its bottom to its top base.
|
||||
// numVerticalSlices: How many vertices should there be in a single horizontal slice.
|
||||
// numHorizontalSlices: How many vertical slices should the sphere consist of.
|
||||
public static Tuple<Vector3[], int[]> BuildCapsule(
|
||||
float radius, float height, int numVerticalSlices = 16, int numHorizontalSlices = 16) {
|
||||
var baseHalfHeight = Math.Max(0.0f, height * 0.5f - radius);
|
||||
Vector3[] topCapVertices;
|
||||
int[] topCapTriangles;
|
||||
GenerateSphereMeshSlice(
|
||||
scale: Vector3.one * radius,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
numHorizontalSlices: numHorizontalSlices / 2,
|
||||
firstSliceY: -1.0f,
|
||||
lastSliceY: 0.0f,
|
||||
vertices: out topCapVertices,
|
||||
triangles: out topCapTriangles);
|
||||
|
||||
Vector3[] bottomCapVertices;
|
||||
int[] bottomCapTriangles;
|
||||
GenerateSphereMeshSlice(
|
||||
scale: Vector3.one * radius,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
numHorizontalSlices: numHorizontalSlices / 2,
|
||||
firstSliceY: 0.0f,
|
||||
lastSliceY: 1.0f,
|
||||
vertices: out bottomCapVertices,
|
||||
triangles: out bottomCapTriangles);
|
||||
|
||||
Vector3[] bodyVertices;
|
||||
int[] bodyTriangles;
|
||||
GenerateCylinderBody(
|
||||
radius: radius,
|
||||
height: baseHalfHeight * 2.0f,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
vertices: out bodyVertices,
|
||||
triangles: out bodyTriangles);
|
||||
|
||||
var merger = new MeshMerger();
|
||||
merger.AddAndTranslate(topCapVertices, topCapTriangles, Vector3.up * baseHalfHeight * -1.0f);
|
||||
merger.AddAndTranslate(
|
||||
bottomCapVertices, bottomCapTriangles, Vector3.up * baseHalfHeight * 1.0f);
|
||||
merger.Add(bodyVertices, bodyTriangles);
|
||||
return Tuple.Create(merger.Vertices, merger.Triangles);
|
||||
}
|
||||
|
||||
// Creates a box mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// extents: Extents of the box, along each major axis.
|
||||
public static Tuple<Vector3[], int[]> BuildBox(Vector3 extents) {
|
||||
// In order to ensure the box renders with flat faces, we need to make sure the triangles
|
||||
// do not share vertices. We'll accomplish that by assigning a unique vertex to every triangle
|
||||
// apex.
|
||||
// We define a set of 8 vertex positions that form the box, and then sample from that set using
|
||||
// triangle indices.
|
||||
var vertexPositions = new Vector3[] {
|
||||
Vector3.Scale(new Vector3(-1, -1, -1), extents),
|
||||
Vector3.Scale(new Vector3(1, -1, -1), extents),
|
||||
Vector3.Scale(new Vector3(-1, -1, 1), extents),
|
||||
Vector3.Scale(new Vector3(1, -1, 1), extents),
|
||||
Vector3.Scale(new Vector3(-1, 1, -1), extents),
|
||||
Vector3.Scale(new Vector3(1, 1, -1), extents),
|
||||
Vector3.Scale(new Vector3(-1, 1, 1), extents),
|
||||
Vector3.Scale(new Vector3(1, 1, 1), extents),
|
||||
};
|
||||
var vertexSamplingPattern = new int[] {
|
||||
0, 1, 3, 0, 3, 2,
|
||||
4, 7, 5, 4, 6, 7,
|
||||
0, 5, 1, 0, 4, 5,
|
||||
1, 7, 3, 1, 5, 7,
|
||||
3, 6, 2, 3, 7, 6,
|
||||
2, 4, 0, 2, 6, 4,
|
||||
};
|
||||
|
||||
var vertices = vertexSamplingPattern.Select(index => vertexPositions[index]).ToArray();
|
||||
var triangles = Enumerable.Range(0, vertexSamplingPattern.Length).ToArray();
|
||||
return Tuple.Create(vertices, triangles);
|
||||
}
|
||||
|
||||
// Creates a plane mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// width: Width of the plane, along the OX axis.
|
||||
// height: Height of the plane, along the OZ axis.
|
||||
public static Tuple<Vector3[], int[]> BuildPlane(float width, float height) {
|
||||
var vertices = new Vector3[] {
|
||||
new Vector3(-0.5f * width, 0, -0.5f * height),
|
||||
new Vector3(0.5f * width, 0, -0.5f * height),
|
||||
new Vector3(-0.5f * width, 0, 0.5f * height),
|
||||
new Vector3(0.5f * width, 0, 0.5f * height),
|
||||
};
|
||||
var triangles = new int[] {
|
||||
0, 3, 1,
|
||||
0, 2, 3,
|
||||
};
|
||||
return Tuple.Create(vertices, triangles);
|
||||
}
|
||||
|
||||
// Generates a slice of a sphere mesh.
|
||||
// Conceptually, the algorithm generates a sphere with a unit radius (spanning from -1 to 1 along
|
||||
// each of the major axes). Then, it cuts it using 2 planes parallel to the XZ plane, located at
|
||||
// distances defined by 'firstSliceY' and 'lastSliceY' parameters respectively. It then returns
|
||||
// the section of the mesh contained between those planes.
|
||||
//
|
||||
// This allows to use the method to generate variants of the sphere - full sphere, hemispheres.
|
||||
//
|
||||
// Args:
|
||||
// scale: Non-uniform sphere scale. Allows to create ellipsoid shapes.
|
||||
// numVerticalSlices: How many vertices should there be in a single horizontal slice.
|
||||
// numHorizontalSlices: How many horizontal slices should the sphere consist of.
|
||||
// firstSliceY: Vertical position of the first slice. Must be a value in range <-1, 1>.
|
||||
// lastSliceY: Vertical position of the last slice. Must a value in range <-1, 1>.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
// triangles: (Out) Array with the sphere triangle connectivity.
|
||||
private static void GenerateSphereMeshSlice(
|
||||
Vector3 scale, int numVerticalSlices, int numHorizontalSlices, float firstSliceY,
|
||||
float lastSliceY, out Vector3[] vertices, out int[] triangles) {
|
||||
numVerticalSlices = Math.Max(3, numVerticalSlices);
|
||||
numHorizontalSlices = Math.Max(3, numHorizontalSlices);
|
||||
triangles = new int[(numHorizontalSlices - 1) * numVerticalSlices * 6];
|
||||
vertices = new Vector3[numVerticalSlices * numHorizontalSlices];
|
||||
if (firstSliceY < -1.0f || firstSliceY > 1.0f) {
|
||||
throw new IOException("firstSliceY should be a value in range <-1, 1>");
|
||||
}
|
||||
if (lastSliceY < -1.0f || lastSliceY > 1.0f) {
|
||||
throw new IOException("lastSliceY should be a value in range <-1, 1>");
|
||||
}
|
||||
if (firstSliceY > lastSliceY) {
|
||||
throw new IOException("Value of firstSliceY should be lower than the value of lastSliceY.");
|
||||
}
|
||||
// Generate the vertices.
|
||||
var deltaY = (lastSliceY - firstSliceY) / (numHorizontalSlices - 1);
|
||||
var deltaYaw = 360.0f / numVerticalSlices;
|
||||
for (var slice = 0; slice < numHorizontalSlices; ++slice) {
|
||||
var y = firstSliceY + deltaY * slice;
|
||||
var radius = (float)Math.Sqrt(1 - Math.Min(1.0f, y * y));
|
||||
for (var vertex = 0; vertex < numVerticalSlices; ++vertex) {
|
||||
var position = Quaternion.AngleAxis(deltaYaw * vertex, Vector3.up) * Vector3.right * radius;
|
||||
position.y = y;
|
||||
vertices[slice * numVerticalSlices + vertex] = Vector3.Scale(position, scale);
|
||||
}
|
||||
}
|
||||
// Build the triangles.
|
||||
for (var slice = 0; slice < (numHorizontalSlices - 1); ++slice) {
|
||||
var firstVertexInSlice = slice * numVerticalSlices;
|
||||
for (var vertex = 0; vertex < numVerticalSlices; ++vertex) {
|
||||
var index1 = firstVertexInSlice + vertex;
|
||||
var index2 = (vertex + 1 == numVerticalSlices) ? firstVertexInSlice : index1 + 1;
|
||||
var index3 = index1 + numVerticalSlices;
|
||||
var index4 = index2 + numVerticalSlices;
|
||||
var quadBaseAddress = (slice * numVerticalSlices + vertex) * 6;
|
||||
triangles[quadBaseAddress] = index1;
|
||||
triangles[quadBaseAddress + 1] = index2;
|
||||
triangles[quadBaseAddress + 2] = index4;
|
||||
triangles[quadBaseAddress + 3] = index1;
|
||||
triangles[quadBaseAddress + 4] = index4;
|
||||
triangles[quadBaseAddress + 5] = index3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the cylinder mesh vertices.
|
||||
// This functionality will be shared between the cylinder body and cylinder base caps generators.
|
||||
//
|
||||
// Args:
|
||||
// radius: Cylinder radius.
|
||||
// height: Cylinder height.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
private static void GenerateCylinderVertices(
|
||||
float radius, float height, int numVerticalSlices, out Vector3[] vertices) {
|
||||
vertices = new Vector3[numVerticalSlices * 2];
|
||||
var dYaw = 360.0f / numVerticalSlices;
|
||||
var yaw = 0.0f;
|
||||
var vertexIndex = 0;
|
||||
for (var y = 0; y <= 1; ++y) {
|
||||
var yPos = (y - 0.5f) * height;
|
||||
for (var i = 0; i < numVerticalSlices; ++i, yaw += dYaw) {
|
||||
var vertexPosition = Quaternion.AngleAxis(yaw, Vector3.up) * Vector3.right * radius;
|
||||
vertexPosition.y = yPos;
|
||||
vertices[vertexIndex++] = vertexPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the meshes for the cylinder body, excluding its bases.
|
||||
//
|
||||
// Args:
|
||||
// radius: Cylinder radius.
|
||||
// height: Cylinder height.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
// triangles: (Out) Array with the sphere triangle connectivity.
|
||||
private static void GenerateCylinderBody(
|
||||
float radius, float height, int numVerticalSlices, out Vector3[] vertices,
|
||||
out int[] triangles) {
|
||||
GenerateCylinderVertices(radius, height, numVerticalSlices, out vertices);
|
||||
triangles = new int[numVerticalSlices * 6];
|
||||
var apexIndex = 0;
|
||||
for (var i = 0; i < numVerticalSlices; ++i) {
|
||||
var v1 = i;
|
||||
var v2 = (i + 1 == numVerticalSlices) ? 0 : v1 + 1;
|
||||
var v3 = v1 + numVerticalSlices;
|
||||
var v4 = v2 + numVerticalSlices;
|
||||
triangles[apexIndex++] = v1;
|
||||
triangles[apexIndex++] = v2;
|
||||
triangles[apexIndex++] = v4;
|
||||
triangles[apexIndex++] = v1;
|
||||
triangles[apexIndex++] = v4;
|
||||
triangles[apexIndex++] = v3;
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the meshes for the cylinder base circles.
|
||||
//
|
||||
// Args:
|
||||
// radius: Cylinder radius.
|
||||
// height: Cylinder height.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
// triangles: (Out) Array with the sphere triangle connectivity.
|
||||
private static void GenerateCylinderBaseCaps(
|
||||
float radius, float height, int numVerticalSlices, out Vector3[] vertices,
|
||||
out int[] triangles) {
|
||||
GenerateCylinderVertices(radius, height, numVerticalSlices, out vertices);
|
||||
triangles = new int[(numVerticalSlices - 1) * 6];
|
||||
var apexIndex = 0;
|
||||
for (var baseIdx = 0; baseIdx <= 1; ++baseIdx) {
|
||||
var v1 = baseIdx * numVerticalSlices;
|
||||
for (var i = 1; i < numVerticalSlices; ++i) {
|
||||
var v2 = v1 + (i % numVerticalSlices);
|
||||
var v3 = v1 + ((i + 1) % numVerticalSlices);
|
||||
triangles[apexIndex++] = v1;
|
||||
triangles[apexIndex++] = baseIdx == 0 ? v3 : v2;
|
||||
triangles[apexIndex++] = baseIdx == 0 ? v2 : v3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A tool used to merge the geometry of separate meshes.
|
||||
public class MeshMerger {
|
||||
private List<Vector3> _vertices = new List<Vector3>();
|
||||
private List<int> _triangles = new List<int>();
|
||||
|
||||
// The vertices of the merged mesh.
|
||||
public Vector3[] Vertices => _vertices.ToArray();
|
||||
|
||||
// The connectivity array of the merged mesh.
|
||||
public int[] Triangles => _triangles.ToArray();
|
||||
|
||||
// Adds a new submesh.
|
||||
//
|
||||
// Args:
|
||||
// vertices: Array of submesh vertices.
|
||||
// vertices: Triangles connectivity array of the submesh.
|
||||
public void Add(Vector3[] vertices, int[] triangles) {
|
||||
var triangleOffset = _triangles.Count;
|
||||
var vertexOffset = _vertices.Count;
|
||||
_vertices.AddRange(vertices);
|
||||
_triangles.AddRange(triangles);
|
||||
for (var i = triangleOffset; i < _triangles.Count; ++i) {
|
||||
_triangles[i] += vertexOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a new submesh, translating its vertices by a specified amount.
|
||||
//
|
||||
// Args:
|
||||
// vertices: Array of submesh vertices.
|
||||
// vertices: Triangles connectivity array of the submesh.
|
||||
// translation: Additional translation to be applied to the submesh.
|
||||
public void AddAndTranslate(Vector3[] vertices, int[] triangles, Vector3 translation) {
|
||||
var triangleOffset = _triangles.Count;
|
||||
var vertexOffset = _vertices.Count;
|
||||
_vertices.AddRange(vertices);
|
||||
for (var i = vertexOffset; i < _vertices.Count; ++i) {
|
||||
_vertices[i] += translation;
|
||||
}
|
||||
_triangles.AddRange(triangles);
|
||||
for (var i = triangleOffset; i < _triangles.Count; ++i) {
|
||||
_triangles[i] += vertexOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: deb4d8c01485d4e44bc815b133e707c0
|
||||
timeCreated: 1539115944
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,394 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
// Actuators provide means to set joints in motion.
|
||||
public class MjActuator : MjComponent {
|
||||
|
||||
public enum ActuatorType {
|
||||
General,
|
||||
Motor,
|
||||
Position,
|
||||
Velocity,
|
||||
Cylinder,
|
||||
Muscle
|
||||
}
|
||||
|
||||
// This structure holds the parameters shared by all types of actuators.
|
||||
//
|
||||
// All constants found in this class are copied from the official documentation, and can be found
|
||||
// here: http://mujoco.org/book/XMLreference.html#actuator
|
||||
[Serializable]
|
||||
public class CommonParameters {
|
||||
|
||||
// If true, the control input to this actuator is automatically clamped to ctrlrange at runtime.
|
||||
// If false, control input clamping is disabled.
|
||||
public bool CtrlLimited;
|
||||
|
||||
// If true, the force output of this actuator is automatically clamped to forcerange at runtime.
|
||||
// If false, force output clamping is disabled.
|
||||
public bool ForceLimited;
|
||||
|
||||
// Range for clamping the control input.
|
||||
public Vector2 CtrlRange;
|
||||
|
||||
// Range for clamping the force output.
|
||||
public Vector2 ForceRange;
|
||||
|
||||
// Range of feasible lengths of the actuator's transmission.
|
||||
public Vector2 LengthRange;
|
||||
|
||||
// This attribute scales the length (and consequently moment arms, velocity and force) of the
|
||||
// actuator, for all transmission types. It is different from the gain in the force generation
|
||||
// mechanism, because the gain only scales the force output and does not affect the length,
|
||||
// moment arms and velocity.
|
||||
public List<float> Gear = new List<float>() { 1.0f };
|
||||
|
||||
public void ToMjcf(XmlElement mjcf) {
|
||||
mjcf.SetAttribute("ctrllimited", $"{CtrlLimited}".ToLowerInvariant());
|
||||
mjcf.SetAttribute("forcelimited", $"{ForceLimited}".ToLowerInvariant());
|
||||
mjcf.SetAttribute(
|
||||
"ctrlrange",
|
||||
$"{MjEngineTool.GetSorted(CtrlRange).x} {MjEngineTool.GetSorted(CtrlRange).y}");
|
||||
mjcf.SetAttribute(
|
||||
"forcerange",
|
||||
$"{MjEngineTool.GetSorted(ForceRange).x} {MjEngineTool.GetSorted(ForceRange).y}");
|
||||
mjcf.SetAttribute(
|
||||
"lengthrange",
|
||||
$"{MjEngineTool.GetSorted(LengthRange).x} {MjEngineTool.GetSorted(LengthRange).y}");
|
||||
mjcf.SetAttribute("gear", MjEngineTool.ListToMjcf(Gear));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
CtrlLimited = mjcf.GetBoolAttribute("ctrllimited", defaultValue: false);
|
||||
ForceLimited = mjcf.GetBoolAttribute("forcelimited", defaultValue: false);
|
||||
CtrlRange = mjcf.GetVector2Attribute("ctrlrange", defaultValue: Vector2.zero);
|
||||
ForceRange = mjcf.GetVector2Attribute("forcerange", defaultValue: Vector2.zero);
|
||||
LengthRange = mjcf.GetVector2Attribute("lengthrange", defaultValue: Vector2.zero);
|
||||
Gear = mjcf.GetFloatArrayAttribute("gear", defaultValue: new float[] { 1.0f }).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
// This structure holds all parameters unique to each type of the actuator.
|
||||
//
|
||||
// Because UnityEditor doesn't handle polymorphism well, I decided to put all parameters here
|
||||
// and then create a custom editor (MjActuatorEditor), that will display only the values
|
||||
// relevant to the selected articulation type. The choice will be made based on the value of
|
||||
// the 'Type' field.
|
||||
//
|
||||
// All constants found in this class are copied from the official documentation, and can be found
|
||||
// here: http://mujoco.org/book/XMLreference.html#actuator
|
||||
[Serializable]
|
||||
public class CustomParameters {
|
||||
|
||||
//// General actuator parameters.
|
||||
|
||||
// Activation dynamics type for the actuator.
|
||||
public MujocoLib.mjtDyn DynType;
|
||||
|
||||
// The gain and bias together determine the output of the force generation mechanism, which is
|
||||
// currently assumed to be affine. As already explained in Actuation model, the general formula
|
||||
// is:
|
||||
// scalar_force = gain_term * (act or ctrl) + bias_term.
|
||||
// The formula uses the activation state when present, and the control otherwise.
|
||||
public MujocoLib.mjtGain GainType;
|
||||
|
||||
// Bias type.
|
||||
public MujocoLib.mjtBias BiasType;
|
||||
|
||||
// Activation dynamics parameters. The built-in activation types (except for muscle) use only
|
||||
// the first parameter, but we provide additional parameters in case user callbacks implement a
|
||||
// more elaborate model. The length of this array is not enforced by the parser, so the user can
|
||||
// enter as many parameters as needed.
|
||||
public List<float> DynPrm = new List<float>() { 1.0f, 0.0f, 0.0f };
|
||||
|
||||
// Gain parameters. The built-in gain types (except for muscle) use only the first parameter,
|
||||
// but we provide additional parameters in case user callbacks implement a more elaborate model.
|
||||
// The length of this array is not enforced by the parser, so the user can enter as many
|
||||
// parameters as needed.
|
||||
public List<float> GainPrm = new List<float>() { 1.0f, 0.0f, 0.0f };
|
||||
|
||||
// Bias parameters. The affine bias type uses three parameters. The length of this array is not
|
||||
// enforced by the parser, so the user can enter as many parameters as needed.
|
||||
public List<float> BiasPrm = new List<float>() { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
public void GeneralToMjcf(XmlElement mjcf) {
|
||||
mjcf.SetAttribute("dyntype", $"{DynType}".Substring(6).ToLowerInvariant());
|
||||
mjcf.SetAttribute("gaintype", $"{GainType}".Substring(7).ToLowerInvariant());
|
||||
mjcf.SetAttribute("biastype", $"{BiasType}".Substring(7).ToLowerInvariant());
|
||||
mjcf.SetAttribute("dynprm", MjEngineTool.ListToMjcf(DynPrm));
|
||||
mjcf.SetAttribute("gainprm", MjEngineTool.ListToMjcf(GainPrm));
|
||||
mjcf.SetAttribute("biasprm", MjEngineTool.ListToMjcf(BiasPrm));
|
||||
}
|
||||
|
||||
public void GeneralFromMjcf(XmlElement mjcf) {
|
||||
var dynTypeStr = mjcf.GetStringAttribute("dyntype", defaultValue: "none");
|
||||
var gainTypeStr = mjcf.GetStringAttribute("gaintype", defaultValue: "fixed");
|
||||
var biasTypeStr = mjcf.GetStringAttribute("biastype", defaultValue: "none");
|
||||
var ignoreCase = true;
|
||||
Enum.TryParse<MujocoLib.mjtDyn>($"mjdyn_{dynTypeStr}", ignoreCase, out DynType);
|
||||
Enum.TryParse<MujocoLib.mjtGain>($"mjgain_{gainTypeStr}", ignoreCase, out GainType);
|
||||
Enum.TryParse<MujocoLib.mjtBias>($"mjbias_{biasTypeStr}", ignoreCase, out BiasType);
|
||||
|
||||
DynPrm = mjcf.GetFloatArrayAttribute(
|
||||
"dynprm", defaultValue: new float[] { 1.0f, 0.0f, 0.0f }).ToList();
|
||||
GainPrm = mjcf.GetFloatArrayAttribute(
|
||||
"gainprm", defaultValue: new float[] { 1.0f, 0.0f, 0.0f }).ToList();
|
||||
BiasPrm = mjcf.GetFloatArrayAttribute(
|
||||
"biasprm", defaultValue: new float[] { 0.0f, 0.0f, 0.0f }).ToList();
|
||||
}
|
||||
|
||||
//// Position actuator parameters.
|
||||
|
||||
// Position feedback gain.
|
||||
[AbsoluteValue]
|
||||
public float Kp = 1.0f;
|
||||
|
||||
public void PositionToMjcf(XmlElement mjcf) {
|
||||
mjcf.SetAttribute("kp", $"{Math.Abs(Kp)}");
|
||||
}
|
||||
public void PositionFromMjcf(XmlElement mjcf) {
|
||||
Kp = mjcf.GetFloatAttribute("kp", defaultValue: 1.0f);
|
||||
}
|
||||
|
||||
//// Velocity actuator parameters.
|
||||
|
||||
// Velocity feedback gain.
|
||||
[AbsoluteValue]
|
||||
public float Kv = 1.0f;
|
||||
|
||||
public void VelocityToMjcf(XmlElement mjcf) {
|
||||
mjcf.SetAttribute("kv", $"{Math.Abs(Kv)}");
|
||||
}
|
||||
public void VelocityFromMjcf(XmlElement mjcf) {
|
||||
Kv = mjcf.GetFloatAttribute("kv", defaultValue: 1.0f);
|
||||
}
|
||||
|
||||
//// Cylinder actuator parameters.
|
||||
|
||||
// Time constant of the activation dynamics.
|
||||
public float CylinderTimeConst = 1.0f;
|
||||
|
||||
// Area of the cylinder. This is used internally as actuator gain.
|
||||
[AbsoluteValue]
|
||||
public float Area = 1.0f;
|
||||
|
||||
// Instead of area the user can specify diameter. If both are specified, diameter has
|
||||
// precedence.
|
||||
[AbsoluteValue]
|
||||
public float Diameter = 0.0f;
|
||||
|
||||
// Bias parameters, copied internally into biasprm.
|
||||
public float[] Bias = new float[] { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
public void CylinderToMjcf(XmlElement mjcf) {
|
||||
mjcf.SetAttribute("timeconst", $"{CylinderTimeConst}");
|
||||
mjcf.SetAttribute("area", $"{Math.Abs(Area)}");
|
||||
mjcf.SetAttribute("diameter", $"{Math.Abs(Diameter)}");
|
||||
mjcf.SetAttribute("bias", MjEngineTool.ArrayToMjcf(Bias));
|
||||
}
|
||||
|
||||
public void CylinderFromMjcf(XmlElement mjcf) {
|
||||
CylinderTimeConst = mjcf.GetFloatAttribute("timeconst", defaultValue: 1.0f);
|
||||
Area = mjcf.GetFloatAttribute("area", defaultValue: 1.0f);
|
||||
Diameter = mjcf.GetFloatAttribute("diameter", defaultValue: 0.0f);
|
||||
Bias = mjcf.GetFloatArrayAttribute("bias", defaultValue: new float[] { 0.0f, 0.0f, 0.0f });
|
||||
}
|
||||
|
||||
//// Muscle actuator parameters.
|
||||
|
||||
// Time constants for activation and de-activation dynamics.
|
||||
public Vector2 MuscleTimeConst = new Vector2(0.01f, 0.04f);
|
||||
|
||||
// Operating length range of the muscle, in units of L0.
|
||||
public Vector2 Range = new Vector2(0.75f, 1.05f);
|
||||
|
||||
// Peak active force at rest. If this value is negative, the peak force is determined
|
||||
// automatically using the scale attribute below.
|
||||
public float Force = -1.0f;
|
||||
|
||||
// If the force attribute is negative, the peak active force for the muscle is set to this value
|
||||
// divided by mjModel.actuator_acc0. The latter is the norm of the joint-space acceleration
|
||||
// vector caused by unit force on the actuator's transmission in qpos0. In other words, scaling
|
||||
// produces higher peak forces for muscles that pull more weight.
|
||||
public float Scale = 200.0f;
|
||||
|
||||
// Lower position range of the normalized FLV curve, in units of L0.
|
||||
public float LMin = 0.5f;
|
||||
|
||||
// Upper position range of the normalized FLV curve, in units of L0.
|
||||
public float LMax = 1.6f;
|
||||
|
||||
// Shortening velocity at which muscle force drops to zero, in units of L0 per second.
|
||||
public float VMax = 1.5f;
|
||||
|
||||
// Passive force generated at lmax, relative to the peak rest force.
|
||||
public float FpMax = 1.3f;
|
||||
|
||||
// Active force generated at saturating lengthening velocity, relative to the peak rest force.
|
||||
public float FvMax = 1.2f;
|
||||
|
||||
public void MuscleToMjcf(XmlElement mjcf) {
|
||||
mjcf.SetAttribute("timeconst", $"{MuscleTimeConst[0]} {MuscleTimeConst[1]}");
|
||||
mjcf.SetAttribute(
|
||||
"range", $"{MjEngineTool.GetSorted(Range).x} {MjEngineTool.GetSorted(Range).y}");
|
||||
mjcf.SetAttribute("force", $"{Force}");
|
||||
mjcf.SetAttribute("scale", $"{Scale}");
|
||||
mjcf.SetAttribute("lmin", $"{LMin}");
|
||||
mjcf.SetAttribute("lmax", $"{LMax}");
|
||||
mjcf.SetAttribute("vmax", $"{VMax}");
|
||||
mjcf.SetAttribute("fpmax", $"{FpMax}");
|
||||
mjcf.SetAttribute("fvmax", $"{FvMax}");
|
||||
}
|
||||
|
||||
public void MuscleFromMjcf(XmlElement mjcf) {
|
||||
MuscleTimeConst = mjcf.GetVector2Attribute(
|
||||
"timeconst", defaultValue: new Vector2(0.01f, 0.04f));
|
||||
Range = mjcf.GetVector2Attribute("range", defaultValue: new Vector2(0.75f, 1.05f));
|
||||
Force = mjcf.GetFloatAttribute("force", defaultValue: -1.0f);
|
||||
Scale = mjcf.GetFloatAttribute("scale", defaultValue: 200.0f);
|
||||
LMin = mjcf.GetFloatAttribute("lmin", defaultValue: 0.5f);
|
||||
LMax = mjcf.GetFloatAttribute("lmax", defaultValue: 1.6f);
|
||||
VMax = mjcf.GetFloatAttribute("vmax", defaultValue: 1.5f);
|
||||
FpMax = mjcf.GetFloatAttribute("fpmax", defaultValue: 1.3f);
|
||||
FvMax = mjcf.GetFloatAttribute("fvmax", defaultValue: 1.2f);
|
||||
}
|
||||
}
|
||||
|
||||
public ActuatorType Type;
|
||||
|
||||
[Tooltip("Joint actuation target. Mutually exclusive with tendon target.")]
|
||||
public MjBaseJoint Joint;
|
||||
|
||||
[Tooltip("Tendon actuation target. Mutually exclusive with joint target.")]
|
||||
public MjBaseTendon Tendon;
|
||||
|
||||
[Tooltip("Parameters specific to each actuator type.")]
|
||||
[HideInInspector]
|
||||
public CustomParameters CustomParams = new CustomParameters();
|
||||
|
||||
[Tooltip("Parameters shared by all types of actuators.")]
|
||||
public CommonParameters CommonParams = new CommonParameters();
|
||||
|
||||
[Tooltip("Actuator control.")]
|
||||
public float Control;
|
||||
|
||||
// Actuator length.
|
||||
public float Length { get; private set; }
|
||||
|
||||
// Actuator velocity.
|
||||
public float Velocity { get; private set; }
|
||||
|
||||
// Actuator force.
|
||||
public float Force { get; private set; }
|
||||
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_ACTUATOR;
|
||||
|
||||
// Parse the component settings from an external Mjcf.
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
if (!Enum.TryParse(mjcf.Name, ignoreCase: true, result: out Type)) {
|
||||
throw new ArgumentException($"Unknown actuator type {mjcf.Name}.");
|
||||
}
|
||||
CommonParams.FromMjcf(mjcf);
|
||||
switch (Type) {
|
||||
case MjActuator.ActuatorType.General: {
|
||||
CustomParams.GeneralFromMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Position: {
|
||||
CustomParams.PositionFromMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Velocity: {
|
||||
CustomParams.VelocityFromMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Cylinder: {
|
||||
CustomParams.CylinderFromMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Muscle: {
|
||||
CustomParams.MuscleFromMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Joint = mjcf.GetObjectReferenceAttribute<MjBaseJoint>("joint");
|
||||
Tendon = mjcf.GetObjectReferenceAttribute<MjBaseTendon>("tendon");
|
||||
}
|
||||
|
||||
// Generate implementation specific XML element.
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
if (Joint == null && Tendon == null) {
|
||||
throw new InvalidOperationException($"Actuator {name} is not assigned a joint nor tendon.");
|
||||
}
|
||||
if (Joint != null && Tendon != null) {
|
||||
throw new InvalidOperationException(
|
||||
$"Actuator {name} can't have both a tendon and a joint target.");
|
||||
}
|
||||
|
||||
var mjcf = doc.CreateElement(Type.ToString().ToLowerInvariant());
|
||||
if (Joint != null) {
|
||||
mjcf.SetAttribute("joint", Joint.MujocoName);
|
||||
} else {
|
||||
mjcf.SetAttribute("tendon", Tendon.MujocoName);
|
||||
}
|
||||
CommonParams.ToMjcf(mjcf);
|
||||
|
||||
switch (Type) {
|
||||
case MjActuator.ActuatorType.General: {
|
||||
CustomParams.GeneralToMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Position: {
|
||||
CustomParams.PositionToMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Velocity: {
|
||||
CustomParams.VelocityToMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Cylinder: {
|
||||
CustomParams.CylinderToMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
case MjActuator.ActuatorType.Muscle: {
|
||||
CustomParams.MuscleToMjcf(mjcf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
// Synchronize the state of the component.
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
data->ctrl[MujocoId] = Control;
|
||||
Length = (float)data->actuator_length[MujocoId];
|
||||
Velocity = (float)data->actuator_velocity[MujocoId];
|
||||
Force = (float)data->actuator_force[MujocoId];
|
||||
}
|
||||
|
||||
public void OnValidate() {
|
||||
if (Joint != null && Tendon != null) {
|
||||
Debug.LogError(
|
||||
$"Actuator {name} can't have both a tendon and a joint target.", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3f3a0cb6a6d64327b27c7d5bdd8d430
|
||||
timeCreated: 1546881319
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
public class MjActuatorScalarSensor : MjBaseSensor {
|
||||
// NOTE: These names must match sensor names listed on mujoco.org.
|
||||
// Changing them is justified only when Mujoco is upgraded to a new version.
|
||||
public enum AvailableSensors {
|
||||
ActuatorPos,
|
||||
ActuatorVel,
|
||||
ActuatorFrc,
|
||||
}
|
||||
public AvailableSensors SensorType;
|
||||
public MjActuator Actuator;
|
||||
|
||||
public double SensorReading { get; private set; }
|
||||
|
||||
protected override XmlElement ToMjcf(XmlDocument doc) {
|
||||
if (Actuator == null) {
|
||||
throw new NullReferenceException("Missing a reference to a MjActuator.");
|
||||
}
|
||||
var mjcf = doc.CreateElement(SensorType.ToString().ToLower());
|
||||
mjcf.SetAttribute("actuator", Actuator.MujocoName);
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
if (!Enum.TryParse(mjcf.Name, ignoreCase: true, result: out SensorType)) {
|
||||
throw new ArgumentException($"Unknown sensor type {mjcf.Name}.");
|
||||
}
|
||||
Actuator = mjcf.GetObjectReferenceAttribute<MjActuator>("actuator");
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
SensorReading = data->sensordata[_sensorAddress];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d6f957ac545d4e8ea581fcc861a0498
|
||||
timeCreated: 1548092931
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// The component represents a joint with 1 degree of translational freedom.
|
||||
public class MjBallJoint : MjBaseJoint {
|
||||
[Tooltip("In radians.")]
|
||||
public float RangeUpper;
|
||||
|
||||
[Tooltip("Joint settings.")]
|
||||
public MjJointSettings Settings = MjJointSettings.Default;
|
||||
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
// Transform.
|
||||
transform.localPosition =
|
||||
MjEngineTool.UnityVector3(mjcf.GetVector3Attribute("pos", defaultValue: Vector3.zero));
|
||||
|
||||
Settings.FromMjcf(mjcf);
|
||||
var rangeValues = mjcf.GetFloatArrayAttribute("range", defaultValue: new float[] { 0, 0 });
|
||||
// rangeValues[0] is always 0 for ball joints.
|
||||
RangeUpper = rangeValues[1];
|
||||
}
|
||||
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = (XmlElement)doc.CreateElement("joint");
|
||||
mjcf.SetAttribute("type", "ball");
|
||||
|
||||
MjEngineTool.PositionToMjcf(mjcf, this);
|
||||
|
||||
Settings.ToMjcf(mjcf);
|
||||
mjcf.SetAttribute("range", $"0 {RangeUpper}");
|
||||
|
||||
return mjcf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc71be844486c4586ba729c1ab4d914a
|
||||
timeCreated: 1552056297
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public abstract class MjBaseBody : MjComponent {
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_BODY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 088eb1d485bd54fcaa1061c79b289dde
|
||||
timeCreated: 1551357866
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public abstract class MjBaseConstraint : MjComponent {
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_EQUALITY;
|
||||
|
||||
protected abstract string _constraintName { get; }
|
||||
|
||||
public SolverImpedance SolverImpedanceSettings = SolverImpedance.Default;
|
||||
public SolverReference SolverReferenceSettings = SolverReference.Default;
|
||||
|
||||
protected abstract void ToMjcf(XmlElement mjcf);
|
||||
protected abstract void FromMjcf(XmlElement mjcf);
|
||||
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
FromMjcf(mjcf);
|
||||
SolverReferenceSettings.FromMjcf(mjcf, "solref");
|
||||
SolverImpedanceSettings.FromMjcf(mjcf, "solimp");
|
||||
}
|
||||
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = (XmlElement)doc.CreateElement(_constraintName);
|
||||
ToMjcf(mjcf);
|
||||
SolverReferenceSettings.ToMjcf(mjcf, "solref");
|
||||
SolverImpedanceSettings.ToMjcf(mjcf, "solimp");
|
||||
return mjcf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a34f7c43f90c84e64a45942e605c0a2b
|
||||
timeCreated: 1552309644
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public abstract class MjBaseJoint : MjComponent {
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_JOINT;
|
||||
|
||||
public int QposAddress { get; private set; } = -1;
|
||||
public int DofAddress { get; private set; } = -1;
|
||||
// Joint will add Degrees of Freedom to this MjBaseBody.
|
||||
// It's the MjBaseBody located immediately above this joint in the scene tree.
|
||||
// NOTE: This property won't be initialized until runtime.
|
||||
public MjBaseBody ParentBody { get; private set; }
|
||||
|
||||
// Joint will constrain the ParentBody to this MjBaseBody.
|
||||
// It's the MjBaseBody located two levels above this joint in the scene tree.
|
||||
// For some joints, such as MjFreeJoint, this can be null.
|
||||
// NOTE: This property won't be initialized until runtime.
|
||||
public MjBaseBody GrandParentBody { get; private set; }
|
||||
|
||||
// Return the bodies connected by this joint.
|
||||
public void GetConnectedBodies(out MjBaseBody grandParent, out MjBaseBody parent) {
|
||||
parent = MjHierarchyTool.FindParentComponent<MjBaseBody>(this);
|
||||
if (parent != null) {
|
||||
grandParent = MjHierarchyTool.FindParentComponent<MjBaseBody>(parent);
|
||||
} else {
|
||||
grandParent = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override unsafe void OnBindToRuntime(MujocoLib.mjModel_* model, MujocoLib.mjData_* data) {
|
||||
QposAddress = model->jnt_qposadr[MujocoId];
|
||||
DofAddress = model->jnt_dofadr[MujocoId];
|
||||
}
|
||||
|
||||
protected override void Start() {
|
||||
base.Start();
|
||||
MjBaseBody grandparentBody, parentBody;
|
||||
GetConnectedBodies(out grandparentBody, out parentBody);
|
||||
GrandParentBody = grandparentBody;
|
||||
ParentBody = parentBody;
|
||||
if (ParentBody == null) {
|
||||
throw new Exception("The joint doesn't have a ParentBody.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07bd997db28964e5daf6af0ff47e157f
|
||||
timeCreated: 1538399740
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// The base class for Mujoco sensors.
|
||||
//
|
||||
// Mujoco offers a very wide range of sensors, each contingent on the following three aspects:
|
||||
// 1. the Mujoco Component it observes: Body, Joint, etc.
|
||||
// 2. the type of data it returns: scalar, Vector3, quaternion, etc.
|
||||
// 3. the field it reads to retreive the data: xpos, xquat, cfrc, etc.
|
||||
//
|
||||
// In Mujoco proper, names of some of the sensors repeat for many data types. In order to get rid
|
||||
// of this ambiguity, we chose to split the sensors into groups.
|
||||
// Each sensor class represents a group of aspects (1) and (2), which is reflected in its name.
|
||||
// For example, the class of sensors that listen to Bodies and produce Vector3 observations is
|
||||
// called MjBodyVectorSensor.
|
||||
//
|
||||
// That very class contains a wide range of sensing capabilities - as in fact most of the classes
|
||||
// do. What each sensor can sense can be controlled by the sensor's Type field. Using the
|
||||
// MjBodyVectorSensor as an example, one can set it to SubtreeCom, FramePos or any of the other
|
||||
// supported observation types.
|
||||
public abstract class MjBaseSensor : MjComponent {
|
||||
|
||||
[Tooltip("The standard deviation of zero-mean Gaussian noise added to the sensor output.")]
|
||||
[AbsoluteValue]
|
||||
public float Noise = 0.0f;
|
||||
|
||||
[Tooltip("When this value is positive, it limits the absolute value of the sensor output.")]
|
||||
[AbsoluteValue]
|
||||
public float Cutoff = 0.0f;
|
||||
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_SENSOR;
|
||||
|
||||
// Address of the sensor, that can be used to index into MujocoLib.mjData_.sensordata.
|
||||
protected int _sensorAddress;
|
||||
|
||||
// Parse the component settings from an external Mjcf.
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
Noise = mjcf.GetFloatAttribute("noise", defaultValue: 0.0f);
|
||||
Cutoff = mjcf.GetFloatAttribute("cutoff", defaultValue: 0.0f);
|
||||
FromMjcf(mjcf);
|
||||
}
|
||||
|
||||
// Generate implementation specific XML element.
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = ToMjcf(doc);
|
||||
mjcf.SetAttribute("noise", Noise.ToString());
|
||||
mjcf.SetAttribute("cutoff", Cutoff.ToString());
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
// Perform bind time initialization of the component.
|
||||
protected override unsafe void OnBindToRuntime(MujocoLib.mjModel_* model, MujocoLib.mjData_* data) {
|
||||
_sensorAddress = model->sensor_adr[MujocoId];
|
||||
}
|
||||
|
||||
// Create the implementation dependent Mjcf node.
|
||||
protected abstract XmlElement ToMjcf(XmlDocument doc);
|
||||
|
||||
// Parse the implementation dependent details from the provided Mjcf node.
|
||||
protected abstract void FromMjcf(XmlElement mjcf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9535e8d4bdb484c4dbd07806fcae7568
|
||||
timeCreated: 1548084206
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public abstract class MjBaseTendon : MjComponent {
|
||||
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_TENDON;
|
||||
|
||||
public SolverSettings Solver = SolverSettings.Default;
|
||||
|
||||
[Tooltip("Length at zero spring force. If negative, this resting length is computed at qpos0.")]
|
||||
public float SpringLength = -1.0f;
|
||||
public float Stiffness = 0.0f;
|
||||
public float Damping = 0.0f;
|
||||
|
||||
// Tendon length.
|
||||
public float Length { get; private set; }
|
||||
|
||||
// Create the implementation dependent Mjcf node.
|
||||
protected abstract XmlElement ToMjcf(XmlDocument doc);
|
||||
|
||||
// Parse the implementation dependent details from the provided Mjcf node.
|
||||
protected abstract void FromMjcf(XmlElement mjcf);
|
||||
|
||||
// Parse the component settings from an external Mjcf.
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
Solver.FromMjcf(mjcf);
|
||||
SpringLength = mjcf.GetFloatAttribute("springlength", defaultValue: -1.0f);
|
||||
Stiffness = mjcf.GetFloatAttribute("damping");
|
||||
Damping = mjcf.GetFloatAttribute("stiffness");
|
||||
FromMjcf(mjcf);
|
||||
}
|
||||
|
||||
// Generate implementation specific XML element.
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = ToMjcf(doc);
|
||||
Solver.ToMjcf(mjcf);
|
||||
mjcf.SetAttribute("springlength", $"{SpringLength}");
|
||||
mjcf.SetAttribute("damping", $"{Damping}");
|
||||
mjcf.SetAttribute("stiffness", $"{Stiffness}");
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
// Synchronize the state of the component.
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
Length = (float)data->ten_length[MujocoId];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d585fcecff4e4b13b8647ad4d18c5b5
|
||||
timeCreated: 1551348729
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// The component represents the apex of hierarchy that defines a single rigid body.
|
||||
public class MjBody : MjBaseBody {
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
// Transform
|
||||
transform.localPosition =
|
||||
MjEngineTool.UnityVector3(mjcf.GetVector3Attribute("pos", defaultValue: Vector3.zero));
|
||||
transform.localRotation = MjEngineTool.UnityQuaternion(
|
||||
mjcf.GetQuaternionAttribute("quat", defaultValue: MjEngineTool.MjQuaternionIdentity));
|
||||
}
|
||||
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = (XmlElement)doc.CreateElement("body");
|
||||
MjEngineTool.PositionRotationToMjcf(mjcf, this);
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
transform.position = MjEngineTool.UnityVector3(data->xpos, MujocoId);
|
||||
transform.rotation = MjEngineTool.UnityQuaternion(data->xquat, MujocoId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ef51a360e7c24695800e2d06f70db49
|
||||
timeCreated: 1537462857
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public class MjBodyQuaternionSensor : MjBaseSensor {
|
||||
public MjBody Body;
|
||||
|
||||
[Tooltip("Should the Frame sensors use the inertial or the regular frame of reference.")]
|
||||
public bool UseInertialFrame;
|
||||
|
||||
public Quaternion SensorReading { get; private set; }
|
||||
|
||||
protected override XmlElement ToMjcf(XmlDocument doc) {
|
||||
if (Body == null) {
|
||||
throw new NullReferenceException("Missing a reference to a MjBody.");
|
||||
}
|
||||
var mjcf = doc.CreateElement("framequat");
|
||||
mjcf.SetAttribute("objtype", UseInertialFrame ? "body" : "xbody");
|
||||
mjcf.SetAttribute("objname", Body.MujocoName);
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
UseInertialFrame = mjcf.HasAttribute("body");
|
||||
Body = mjcf.GetObjectReferenceAttribute<MjBody>("objname");
|
||||
if (Body == null) {
|
||||
throw new NullReferenceException("Missing a reference to a MjBody.");
|
||||
}
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
SensorReading = MjEngineTool.UnityQuaternion(data->sensordata, _sensorAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af03f553fc93c44ec8d1cc3fbbcce947
|
||||
timeCreated: 1548170856
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public class MjBodyVectorSensor : MjBaseSensor {
|
||||
// NOTE: These names must match sensor names listed on mujoco.org.
|
||||
// Changing them is justified only when Mujoco is upgraded to a new version.
|
||||
public enum AvailableSensors {
|
||||
SubtreeCom,
|
||||
SubtreeLinVel,
|
||||
SubtreeAngMom,
|
||||
FramePos,
|
||||
FrameXAxis,
|
||||
FrameYAxis,
|
||||
FrameZAxis,
|
||||
FrameLinVel,
|
||||
FrameAngVel,
|
||||
FrameLinAcc,
|
||||
FrameAngAcc,
|
||||
}
|
||||
public AvailableSensors SensorType;
|
||||
public MjBody Body;
|
||||
|
||||
[Tooltip("Should the Frame sensors use the inertial or the regular frame of reference.")]
|
||||
public bool UseInertialFrame;
|
||||
|
||||
public Vector3 SensorReading { get; private set; }
|
||||
|
||||
protected override XmlElement ToMjcf(XmlDocument doc) {
|
||||
if (Body == null) {
|
||||
throw new NullReferenceException("Missing a reference to a MjBody.");
|
||||
}
|
||||
var tag = SensorType.ToString().ToLower();
|
||||
var mjcf = doc.CreateElement(tag);
|
||||
if (tag.Contains("frame")) {
|
||||
mjcf.SetAttribute("objtype", UseInertialFrame ? "body" : "xbody");
|
||||
mjcf.SetAttribute("objname", Body.MujocoName);
|
||||
} else {
|
||||
mjcf.SetAttribute("body", Body.MujocoName);
|
||||
}
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
if (!Enum.TryParse(mjcf.Name, ignoreCase: true, result: out SensorType)) {
|
||||
throw new ArgumentException($"Unknown sensor type {mjcf.Name}.");
|
||||
}
|
||||
if (mjcf.Name.Contains("frame")) {
|
||||
UseInertialFrame = mjcf.HasAttribute("body"); // as opposed to xbody
|
||||
Body = mjcf.GetObjectReferenceAttribute<MjBody>("objname");
|
||||
} else {
|
||||
Body = mjcf.GetObjectReferenceAttribute<MjBody>("body");
|
||||
}
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
SensorReading = MjEngineTool.UnityVector3(data->sensordata, _sensorAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 537df637b11f44df2a20bea718a015a6
|
||||
timeCreated: 1548092931
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjBoxShape : IMjShape {
|
||||
public Vector3 Extents = Vector3.one * 0.5f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
var scaledExtents = MjEngineTool.MjExtents(Vector3.Scale(Extents, transform.lossyScale));
|
||||
mjcf.SetAttribute("size", MjEngineTool.Vector3ToMjcf(scaledExtents));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
Extents = MjEngineTool.UnityExtents(
|
||||
mjcf.GetVector3Attribute("size", defaultValue: Vector3.one * 0.5f));
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
var extent = (toPoint - fromPoint).magnitude * 0.5f;
|
||||
Extents = Vector3.one * extent;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildBox(extents: Extents);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return Extents;
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, transform.lossyScale);
|
||||
Gizmos.DrawWireCube(Vector3.zero, Extents * 2.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9783bb0e781e341e5914cdbac095b35e
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjCapsuleShape : IMjShape {
|
||||
public float Radius = 0.5f;
|
||||
public float HalfHeight = 0.5f;
|
||||
private const float _scaleTolerance = 1e-5f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
if (Math.Abs(transform.lossyScale.x - transform.lossyScale.z) > _scaleTolerance) {
|
||||
Debug.LogWarning(
|
||||
$"{transform.name}: Capsule shapes require uniform scaling of XZ plane. Using the" +
|
||||
" value of X and Y components.", transform);
|
||||
}
|
||||
mjcf.SetAttribute(
|
||||
"size", $"{Radius * transform.lossyScale.x} {HalfHeight * transform.lossyScale.y}");
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
var components = mjcf.GetFloatArrayAttribute("size", defaultValue: new float[] { 0.5f, 0.5f });
|
||||
Radius = components[0];
|
||||
HalfHeight = components[1];
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
HalfHeight = 0.5f * (toPoint - fromPoint).magnitude;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildCapsule(radius: Radius, height: (HalfHeight + Radius) * 2.0f);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return new Vector4(Radius, HalfHeight, 1, 0);
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
MjGizmos.DrawWireCapsule(
|
||||
Vector3.zero, Radius * transform.lossyScale.x, 2 * HalfHeight * transform.lossyScale.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b0a2054e1eec409fbf34c796a572b38
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// The base class for all components that represent Mujoco scene nodes.
|
||||
[DisallowMultipleComponent]
|
||||
public abstract class MjComponent : MonoBehaviour {
|
||||
|
||||
// Unique name assigned to each Mujoco component.
|
||||
public string MujocoName { get; private set; }
|
||||
|
||||
// Id of the geom in Mujoco's internal data structures.
|
||||
public int MujocoId { get; protected set; }
|
||||
|
||||
public abstract MujocoLib.mjtObj ObjectType { get; }
|
||||
|
||||
// Some components (Inertial frames for example) cannot have the name attribute
|
||||
// added to the generated Mjcf.
|
||||
protected virtual bool _suppressNameAttribute => false;
|
||||
|
||||
// Binds this component to the compiled Mujoco model.
|
||||
public unsafe void BindToRuntime(MujocoLib.mjModel_* model, MujocoLib.mjData_* data) {
|
||||
MujocoId = MujocoLib.mj_name2id(model, (int)ObjectType, MujocoName);
|
||||
if (MujocoId == -1 && !_suppressNameAttribute) {
|
||||
throw new NullReferenceException($"element name {MujocoName} not found");
|
||||
}
|
||||
OnBindToRuntime(model, data);
|
||||
}
|
||||
|
||||
// Generates the XML element that corresponds to this scene node.
|
||||
public XmlElement GenerateMjcf(string name, XmlDocument doc) {
|
||||
MujocoName = name;
|
||||
|
||||
var mjcf = OnGenerateMjcf(doc);
|
||||
if (!_suppressNameAttribute) {
|
||||
mjcf.SetAttribute("name", name);
|
||||
}
|
||||
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
// Parse the component settings from an external Mjcf.
|
||||
public void ParseMjcf(XmlElement mjcf) {
|
||||
// I would like to preserve the naming dualism - external mechanisms
|
||||
// calling a method, and the internal implementation implementing
|
||||
// the method with an "On" prefix.
|
||||
OnParseMjcf(mjcf);
|
||||
}
|
||||
|
||||
// Parse the component settings from an external Mjcf.
|
||||
protected abstract void OnParseMjcf(XmlElement mjcf);
|
||||
|
||||
// Generate implementation specific XML element.
|
||||
protected abstract XmlElement OnGenerateMjcf(XmlDocument doc);
|
||||
|
||||
// Perform bind time initialization of the component.
|
||||
protected virtual unsafe void OnBindToRuntime(MujocoLib.mjModel_* model, MujocoLib.mjData_* data) {}
|
||||
|
||||
// Synchronize the state of the component.
|
||||
public virtual unsafe void OnSyncState(MujocoLib.mjData_* data) {}
|
||||
|
||||
private bool _sceneExcludesMe = false;
|
||||
|
||||
protected unsafe virtual void Start() {
|
||||
if (MjScene.Instance == null) {
|
||||
throw new Exception("MuJoCo Scene not found");
|
||||
}
|
||||
if (MjScene.Instance.Model != null) {
|
||||
_sceneExcludesMe = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Update() {
|
||||
if (_sceneExcludesMe) {
|
||||
MjScene.Instance.SceneRecreationAtLateUpdateRequested = true;
|
||||
_sceneExcludesMe = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _exiting = false;
|
||||
public void OnApplicationQuit() {
|
||||
_exiting = true;
|
||||
}
|
||||
|
||||
public void OnDisable() {
|
||||
if (!_exiting) {
|
||||
MjScene.Instance.SceneRecreationAtLateUpdateRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cb8afbea854147a8bf92a08d5f6577b
|
||||
timeCreated: 1536879723
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public class MjConnect : MjBaseConstraint {
|
||||
public MjBaseBody Body1;
|
||||
public MjBaseBody Body2;
|
||||
protected override string _constraintName => "connect";
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
Body1 = mjcf.GetObjectReferenceAttribute<MjBaseBody>("body1");
|
||||
Body2 = mjcf.GetObjectReferenceAttribute<MjBaseBody>("body2");
|
||||
if (mjcf.GetStringAttribute("anchor") != null) {
|
||||
Debug.Log($"anchor in connect {name} ignored. Set Transforms in the editor.");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate implementation specific XML element.
|
||||
protected override void ToMjcf(XmlElement mjcf) {
|
||||
if (Body1 == null || Body2 == null) {
|
||||
throw new NullReferenceException($"Both bodies in connect {name} are required.");
|
||||
}
|
||||
mjcf.SetAttribute("body1", Body1.MujocoName);
|
||||
mjcf.SetAttribute("body2", Body2.MujocoName);
|
||||
}
|
||||
|
||||
public void OnValidate() {
|
||||
if (Body1 != null && Body1 == Body2) {
|
||||
Debug.LogError("Body1 and Body2 can't be the same - resetting Body2.", this);
|
||||
Body2 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee6081d65578643bf8a26f200299a51a
|
||||
timeCreated: 1552313909
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjCylinderShape : IMjShape {
|
||||
public float Radius = 0.5f;
|
||||
public float HalfHeight = 1.0f;
|
||||
private const float _scaleTolerance = 1e-6f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
if (Math.Abs(transform.lossyScale.x - transform.lossyScale.z) > _scaleTolerance) {
|
||||
Debug.LogWarning(
|
||||
$"{transform.name}: Cylinder shapes require uniform scaling of XZ plane. Using the" +
|
||||
" value of X and Y components.", transform);
|
||||
}
|
||||
mjcf.SetAttribute(
|
||||
"size", $"{Radius * transform.lossyScale.x} {HalfHeight * transform.lossyScale.y}");
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
var components = mjcf.GetFloatArrayAttribute("size", defaultValue: new float[] { 0.5f, 1.0f });
|
||||
Radius = components[0];
|
||||
HalfHeight = components[1];
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
HalfHeight = (toPoint - fromPoint).magnitude * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildCylinder(radius: Radius, height: HalfHeight * 2);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return new Vector4(Radius, HalfHeight, 1, 0);
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
MjGizmos.DrawWireCylinder(
|
||||
Vector3.zero, Radius * transform.lossyScale.x, HalfHeight * 2 * transform.lossyScale.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd3a2b00a0a2f4ba9be8bd2f054a3835
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public class MjDistance : MjBaseConstraint {
|
||||
public MjGeom Geom1;
|
||||
public MjGeom Geom2;
|
||||
protected override string _constraintName => "distance";
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
Geom1 = mjcf.GetObjectReferenceAttribute<MjGeom>("geom1");
|
||||
Geom2 = mjcf.GetObjectReferenceAttribute<MjGeom>("geom2");
|
||||
}
|
||||
|
||||
protected override void ToMjcf(XmlElement mjcf) {
|
||||
if (Geom1 == null || Geom2 == null) {
|
||||
throw new NullReferenceException($"Both geoms in distance {name} must be assigned.");
|
||||
}
|
||||
|
||||
mjcf.SetAttribute("geom1", Geom1.MujocoName);
|
||||
mjcf.SetAttribute("geom2", Geom2.MujocoName);
|
||||
}
|
||||
|
||||
public void OnValidate() {
|
||||
if (Geom1 != null && Geom1 == Geom2) {
|
||||
Debug.LogError("Geom1 and Geom2 can't be the same - resetting Geom2.", this);
|
||||
Geom2 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad0689a32f102481fb189836c11931f9
|
||||
timeCreated: 1552309644
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjEllipsoidShape : IMjShape {
|
||||
public Vector3 Radiuses = Vector3.one * 0.5f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
var scaledRadiuses = MjEngineTool.MjExtents(Vector3.Scale(Radiuses, transform.lossyScale));
|
||||
mjcf.SetAttribute("size", MjEngineTool.Vector3ToMjcf(scaledRadiuses));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
Radiuses = MjEngineTool.UnityExtents(
|
||||
mjcf.GetVector3Attribute("size", defaultValue: Vector3.one * 0.5f));
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
var radius = (toPoint - fromPoint).magnitude * 0.5f;
|
||||
Radiuses = Vector3.one * radius;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildSphere(scale: Radiuses);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return Radiuses;
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(
|
||||
transform.position,
|
||||
transform.rotation,
|
||||
Vector3.Scale(Radiuses, transform.lossyScale));
|
||||
Gizmos.DrawWireSphere(Vector3.zero, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8316fb5037f4a450c9270267e5428567
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public class MjExclude : MjComponent {
|
||||
|
||||
[Tooltip("A body whose contacts with other body are ignored")]
|
||||
public MjBody Body1;
|
||||
[Tooltip("Other body whose contacts with first body are ignored")]
|
||||
public MjBody Body2;
|
||||
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_EXCLUDE;
|
||||
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
var body1Name = mjcf.GetStringAttribute("body1", defaultValue: string.Empty);
|
||||
if (!string.IsNullOrEmpty(body1Name)) {
|
||||
Body1 = MjHierarchyTool.FindComponentOfTypeAndName<MjBody>(body1Name);
|
||||
}
|
||||
var body2Name = mjcf.GetStringAttribute("body2", defaultValue: string.Empty);
|
||||
if (!string.IsNullOrEmpty(body2Name)) {
|
||||
Body2 = MjHierarchyTool.FindComponentOfTypeAndName<MjBody>(body2Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate implementation specific XML element.
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
if (Body1 == null || Body2 == null) {
|
||||
throw new NullReferenceException($"Both bodies in {name} must be assigned.");
|
||||
}
|
||||
|
||||
var mjcf = (XmlElement)doc.CreateElement("exclude");
|
||||
mjcf.SetAttribute("body1", Body1.MujocoName);
|
||||
mjcf.SetAttribute("body2", Body2.MujocoName);
|
||||
return mjcf;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d271c366e5c04a259f36a15b42f78a5
|
||||
timeCreated: 1550051492
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
[Serializable]
|
||||
public class FixedTendonEntry {
|
||||
[Tooltip("Only scalar joints can be used - hinge and slide.")]
|
||||
public MjBaseJoint Joint;
|
||||
|
||||
[Tooltip("Multiplier for the value of the joint.")]
|
||||
public float Coefficient = 1.0f;
|
||||
}
|
||||
|
||||
public class MjFixedTendon : MjBaseTendon {
|
||||
|
||||
[Tooltip("In (scaled) radians/meters.")]
|
||||
public float RangeLower;
|
||||
[Tooltip("In (scaled) radians/meters.")]
|
||||
public float RangeUpper;
|
||||
|
||||
public List<FixedTendonEntry> JointList = new List<FixedTendonEntry>() {};
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
foreach (var child in mjcf.Cast<XmlNode>().OfType<XmlElement>()) {
|
||||
var fixedTendonEntry = new FixedTendonEntry();
|
||||
fixedTendonEntry.Joint = child.GetObjectReferenceAttribute<MjBaseJoint>("joint");
|
||||
fixedTendonEntry.Coefficient = child.GetFloatAttribute("coef", defaultValue: 1.0f);
|
||||
JointList.Add(fixedTendonEntry);
|
||||
}
|
||||
var rangeValues = mjcf.GetFloatArrayAttribute("range", defaultValue: new float[] { 0, 0 });
|
||||
RangeLower = rangeValues[0];
|
||||
RangeUpper = rangeValues[1];
|
||||
}
|
||||
|
||||
protected override XmlElement ToMjcf(XmlDocument doc) {
|
||||
if (JointList.Count < 1) {
|
||||
throw new ArgumentOutOfRangeException($"Fixed tendon {name} needs at least one joint.");
|
||||
}
|
||||
var mjcf = doc.CreateElement("fixed");
|
||||
foreach (FixedTendonEntry fixedTendonEntry in JointList) {
|
||||
var jointMjcf = doc.CreateElement("joint");
|
||||
jointMjcf.SetAttribute("joint", fixedTendonEntry.Joint.MujocoName);
|
||||
jointMjcf.SetAttribute("coef", $"{fixedTendonEntry.Coefficient}");
|
||||
mjcf.AppendChild(jointMjcf);
|
||||
}
|
||||
if (RangeLower > RangeUpper) {
|
||||
throw new ArgumentException("Lower range value can't be bigger than Higher");
|
||||
}
|
||||
mjcf.SetAttribute("range", $"{RangeLower} {RangeUpper}");
|
||||
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
public void OnValidate() {
|
||||
foreach (FixedTendonEntry fixedTendonEntry in JointList) {
|
||||
if (!(fixedTendonEntry.Joint is MjHingeJoint) &&
|
||||
!(fixedTendonEntry.Joint is MjSlideJoint)) {
|
||||
Debug.LogError("Only scalar joints (hinge or slide) are allowed.", this);
|
||||
fixedTendonEntry.Joint = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e49529c3d80714a368699287bbdd8f72
|
||||
timeCreated: 1549990455
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// The component represents a joint with 6 degrees of freedom.
|
||||
public class MjFreeJoint : MjBaseJoint {
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {}
|
||||
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
return (XmlElement)doc.CreateElement("freejoint");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ef627bd3075d42d886135a149a187a2
|
||||
timeCreated: 1537540954
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// The component represents a physical shape and models its inertia and material properties.
|
||||
public class MjGeom : MjShapeComponent {
|
||||
[Tooltip("If larger than zero, Density has no effect.")]
|
||||
public float Mass = 0.0f;
|
||||
|
||||
[Tooltip("Material density. Set to 0 for a zero-mass geom.")]
|
||||
public float Density = 1000.0f;
|
||||
|
||||
[Tooltip("Advanced settings.")]
|
||||
public MjGeomSettings Settings = MjGeomSettings.Default;
|
||||
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_GEOM;
|
||||
private MjTransformation _geomInGlobalFrame = new MjTransformation();
|
||||
private MjTransformation _comTransform;
|
||||
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
ShapeFromMjcf(mjcf);
|
||||
Mass = mjcf.GetFloatAttribute("mass", defaultValue: 0.0f);
|
||||
Density = mjcf.GetFloatAttribute("density", defaultValue: 1000.0f);
|
||||
MjEngineTool.ParseTransformMjcf(mjcf, transform);
|
||||
Settings.FromMjcf(mjcf);
|
||||
}
|
||||
|
||||
// The MuJoCo compiler shifts meshes' frames, so we need to cache this transformation at init and
|
||||
// apply it at runtime.
|
||||
protected override unsafe void OnBindToRuntime(MujocoLib.mjModel_* model, MujocoLib.mjData_* data) {
|
||||
var MjParent = MjHierarchyTool.FindParentComponent<MjBaseBody>(this);
|
||||
if (MjParent != null) {
|
||||
var comInParentFrame = new MjTransformation(
|
||||
translation: MjEngineTool.UnityVector3(model->geom_pos, MujocoId),
|
||||
rotation: MjEngineTool.UnityQuaternion(model->geom_quat, MujocoId));
|
||||
|
||||
// We don't want to bother calculating global transform in mujoco from mjModel,
|
||||
// so we'll assume it's the same as the Unity transfor (it's the beginning of simulation after
|
||||
// all).
|
||||
var globalParentFrame = MjTransformation.LoadGlobal(transform.parent);
|
||||
var comInGlobalFrame = globalParentFrame * comInParentFrame;
|
||||
var globalFrame = MjTransformation.LoadGlobal(transform);
|
||||
_comTransform = comInGlobalFrame.Inverse() * globalFrame;
|
||||
}
|
||||
}
|
||||
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = (XmlElement)doc.CreateElement("geom");
|
||||
if (Mass > 0) {
|
||||
mjcf.SetAttribute("mass", $"{Mass}");
|
||||
} else {
|
||||
mjcf.SetAttribute("density", $"{Density}");
|
||||
}
|
||||
ShapeToMjcf(mjcf, transform);
|
||||
MjEngineTool.PositionRotationToMjcf(mjcf, this);
|
||||
Settings.ToMjcf(mjcf);
|
||||
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
if (ShapeType == ShapeTypes.Mesh) {
|
||||
_geomInGlobalFrame.Set(
|
||||
translation: MjEngineTool.UnityVector3(data->geom_xpos, MujocoId),
|
||||
rotation: MjEngineTool.UnityQuaternionFromMatrix(data->geom_xmat, MujocoId));
|
||||
var comInGlobalFrame = _geomInGlobalFrame * _comTransform;
|
||||
comInGlobalFrame.StoreGlobal(transform);
|
||||
} else {
|
||||
transform.position = MjEngineTool.UnityVector3(data->geom_xpos, MujocoId);
|
||||
transform.rotation = MjEngineTool.UnityQuaternionFromMatrix(data->geom_xmat, MujocoId);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDrawGizmosSelected() {
|
||||
Gizmos.color = Color.blue;
|
||||
DrawGizmos(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba8757a90afe442d9acfbd600758b843
|
||||
timeCreated: 1536743564
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
public class MjGeomQuaternionSensor : MjBaseSensor {
|
||||
public MjGeom Geom;
|
||||
|
||||
public Quaternion SensorReading { get; private set; }
|
||||
|
||||
protected override XmlElement ToMjcf(XmlDocument doc) {
|
||||
if (Geom == null) {
|
||||
throw new NullReferenceException("Missing a reference to a MjGeom.");
|
||||
}
|
||||
var mjcf = doc.CreateElement("framequat");
|
||||
mjcf.SetAttribute("objtype", "geom");
|
||||
mjcf.SetAttribute("objname", Geom.MujocoName);
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
Geom = mjcf.GetObjectReferenceAttribute<MjGeom>("objname");
|
||||
if (Geom == null) {
|
||||
throw new NullReferenceException("Missing a reference to a MjGeom.");
|
||||
}
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
SensorReading = MjEngineTool.UnityQuaternion(data->sensordata, _sensorAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71b28b4bc4c27423aaba5b98863e0c92
|
||||
timeCreated: 1548170856
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// Advanced settings of MjGeom component.
|
||||
[Serializable]
|
||||
public struct MjGeomSettings {
|
||||
[Tooltip("Solver accuracy settings.")]
|
||||
public GeomSolver Solver;
|
||||
|
||||
[Tooltip("Collision filtering settings.")]
|
||||
public CollisionFiltering Filtering;
|
||||
|
||||
[Tooltip("Contact friction parameters for dynamically generated contact pairs.")]
|
||||
public GeomFriction Friction;
|
||||
|
||||
// Default geom settings.
|
||||
public static MjGeomSettings Default = new MjGeomSettings() {
|
||||
Solver = GeomSolver.Default,
|
||||
Filtering = CollisionFiltering.Default,
|
||||
Friction = GeomFriction.Default
|
||||
};
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
// Contact filtering settings.
|
||||
Filtering.Contype = (int)mjcf.GetFloatAttribute("contype", CollisionFiltering.Default.Contype);
|
||||
Filtering.Conaffinity = (int)mjcf.GetFloatAttribute(
|
||||
"conaffinity", CollisionFiltering.Default.Conaffinity);
|
||||
Filtering.Group = (int)mjcf.GetFloatAttribute("group", CollisionFiltering.Default.Group);
|
||||
|
||||
// Solver settings.
|
||||
Solver.ConDim = (int)mjcf.GetFloatAttribute("condim", GeomSolver.Default.ConDim);
|
||||
Solver.SolMix = mjcf.GetFloatAttribute("solmix", GeomSolver.Default.SolMix);
|
||||
var solref = mjcf.GetFloatArrayAttribute(
|
||||
"solref", new float[] { GeomSolver.Default.SolRef.TimeConst,
|
||||
GeomSolver.Default.SolRef.DampRatio });
|
||||
Solver.SolRef.TimeConst = solref[0];
|
||||
Solver.SolRef.DampRatio = solref[1];
|
||||
var solimp = mjcf.GetFloatArrayAttribute(
|
||||
"solimp", new float[] { GeomSolver.Default.SolImp.DMin, GeomSolver.Default.SolImp.DMax,
|
||||
GeomSolver.Default.SolImp.Width });
|
||||
Solver.SolImp.DMin = solimp[0];
|
||||
Solver.SolImp.DMax = solimp[1];
|
||||
Solver.SolImp.Width = solimp[2];
|
||||
Solver.Margin = mjcf.GetFloatAttribute("margin", GeomSolver.Default.Margin);
|
||||
Solver.Gap = mjcf.GetFloatAttribute("gap", GeomSolver.Default.Gap);
|
||||
|
||||
// Inertia and friction settings.
|
||||
var friction = mjcf.GetFloatArrayAttribute(
|
||||
"friction", new float[] { GeomFriction.Default.Sliding, GeomFriction.Default.Torsional,
|
||||
GeomFriction.Default.Rolling });
|
||||
Friction.Sliding = friction[0];
|
||||
Friction.Torsional = friction[1];
|
||||
Friction.Rolling = friction[2];
|
||||
}
|
||||
|
||||
public void ToMjcf(XmlElement mjcf) {
|
||||
// Contact filtering settings.
|
||||
mjcf.SetAttribute("contype", $"{Filtering.Contype}");
|
||||
mjcf.SetAttribute("conaffinity", $"{Filtering.Conaffinity}");
|
||||
mjcf.SetAttribute("group", $"{Filtering.Group}");
|
||||
|
||||
// Solver settings.
|
||||
mjcf.SetAttribute("condim", $"{Solver.ConDim}");
|
||||
mjcf.SetAttribute("solmix", $"{Solver.SolMix}");
|
||||
mjcf.SetAttribute("solref", $"{Solver.SolRef.TimeConst} {Solver.SolRef.DampRatio}");
|
||||
mjcf.SetAttribute(
|
||||
"solimp", $"{Solver.SolImp.DMin} {Solver.SolImp.DMax} {Solver.SolImp.Width}");
|
||||
mjcf.SetAttribute("margin", $"{Solver.Margin}");
|
||||
mjcf.SetAttribute("gap", $"{Solver.Gap}");
|
||||
|
||||
// Inertia and friction settings.
|
||||
mjcf.SetAttribute("friction", $"{Friction.Sliding} {Friction.Torsional} {Friction.Rolling}");
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct GeomFriction {
|
||||
public float Sliding;
|
||||
public float Torsional;
|
||||
public float Rolling;
|
||||
|
||||
public static GeomFriction Default = new GeomFriction {
|
||||
Sliding = 1.0f, Torsional = 0.005f, Rolling = 0.0001f
|
||||
};
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct CollisionFiltering {
|
||||
|
||||
[Tooltip("Bitmasks used for contact filtering of dynamically generated contact pairs.")]
|
||||
public int Contype;
|
||||
|
||||
[Tooltip("Bitmask for contact filtering")]
|
||||
public int Conaffinity;
|
||||
|
||||
// The only effect on the physics is at compile time, when body masses and inertias are inferred
|
||||
// from geoms selected based on their group.
|
||||
[Tooltip("Group to which the geom belongs.")]
|
||||
public int Group;
|
||||
|
||||
public static CollisionFiltering Default = new CollisionFiltering() {
|
||||
Contype = 1, Conaffinity = 1, Group = 0
|
||||
};
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct GeomSolver {
|
||||
[Tooltip("The dimensionality of the contact space.")]
|
||||
public int ConDim;
|
||||
|
||||
[Tooltip("Weight used for averaging of constraint solver parameters.")]
|
||||
public float SolMix;
|
||||
|
||||
[Tooltip("Solver function d(r) reference parameters.")]
|
||||
public SolverReference SolRef;
|
||||
|
||||
[Tooltip("Solver function d(r) impedance parameters.")]
|
||||
public SolverImpedance SolImp;
|
||||
|
||||
[Tooltip("Distance threshold below which contacts are detected.")]
|
||||
public float Margin;
|
||||
|
||||
[Tooltip("Positive value enables generation of inactive contacts.")]
|
||||
public float Gap;
|
||||
|
||||
public static GeomSolver Default = new GeomSolver() {
|
||||
ConDim = 3, SolMix = 1.0f, SolRef = SolverReference.Default, SolImp = SolverImpedance.Default,
|
||||
Margin = 0.0f, Gap = 0.0f
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d472b07eedb654ed5a83bfd6822587a3
|
||||
timeCreated: 1538655839
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public class MjGeomVectorSensor : MjBaseSensor {
|
||||
// NOTE: These names must match sensor names listed on mujoco.org.
|
||||
// Changing them is justified only when Mujoco is upgraded to a new version.
|
||||
public enum AvailableSensors {
|
||||
FramePos,
|
||||
FrameXAxis,
|
||||
FrameYAxis,
|
||||
FrameZAxis,
|
||||
FrameLinVel,
|
||||
FrameAngVel,
|
||||
FrameLinAcc,
|
||||
FrameAngAcc,
|
||||
}
|
||||
|
||||
public AvailableSensors SensorType;
|
||||
|
||||
public MjGeom Geom;
|
||||
|
||||
public Vector3 SensorReading { get; private set; }
|
||||
|
||||
protected override XmlElement ToMjcf(XmlDocument doc) {
|
||||
if (Geom == null) {
|
||||
throw new NullReferenceException("Missing a reference to a MjGeom.");
|
||||
}
|
||||
var mjcf = doc.CreateElement(SensorType.ToString().ToLower());
|
||||
mjcf.SetAttribute("objtype", "geom");
|
||||
mjcf.SetAttribute("objname", Geom.MujocoName);
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
protected override void FromMjcf(XmlElement mjcf) {
|
||||
if (!Enum.TryParse(mjcf.Name, ignoreCase: true, result: out SensorType)) {
|
||||
throw new ArgumentException($"Unknown sensor type {mjcf.Name}.");
|
||||
}
|
||||
Geom = mjcf.GetObjectReferenceAttribute<MjGeom>("objname");
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
SensorReading = MjEngineTool.UnityVector3(data->sensordata, _sensorAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c63b35fff51454145b2c5cabf4a81669
|
||||
timeCreated: 1548107238
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// Additional Debug Gizmos.
|
||||
public static class MjGizmos {
|
||||
|
||||
private const float _vectorPerpendicularityMagnitudeThreshold = 1e-6f;
|
||||
|
||||
public static int NumCircleSegments = 32;
|
||||
|
||||
private static Vector3[] _circumferenceVertices = new Vector3[] {
|
||||
Vector3.right, -Vector3.right, Vector3.forward, -Vector3.forward,
|
||||
};
|
||||
|
||||
public static Action<Vector3 /*from*/, Vector3 /*to*/> DrawLine { get; set; } = Gizmos.DrawLine;
|
||||
|
||||
public static Action<Vector3 /*position*/, float /*radius*/, Vector3 /*axis*/, float /*length*/,
|
||||
float /*startAngle*/> DrawArc { get; set; } = MjGizmos.DrawArcInternal;
|
||||
|
||||
// Draws a wireframe capsule of the specified radius and height.
|
||||
// The capsule's height will be aligned with the global Y axis.
|
||||
// The specified height represents the height of the cylinder between two hemisphere bases.
|
||||
public static void DrawWireCapsule(Vector3 position, float radius, float height) {
|
||||
var offset = DrawHollowWireCylinder(position, radius, height);
|
||||
// Arches marking the bases of the capsule.
|
||||
DrawArc(position - offset, radius, Vector3.right, 0.5f, 0.0f);
|
||||
DrawArc(position + offset, radius, Vector3.right, 0.5f, 180.0f);
|
||||
DrawArc(position + offset, radius, Vector3.forward, 0.5f, -90.0f);
|
||||
DrawArc(position - offset, radius, Vector3.forward, 0.5f, 90.0f);
|
||||
}
|
||||
|
||||
// Draws a wireframe cylinder of the specified radius and height.
|
||||
// The cylinder's height will be aligned with the global Y axis.
|
||||
public static void DrawWireCylinder(Vector3 position, float radius, float height) {
|
||||
var offset = DrawHollowWireCylinder(position, radius, height);
|
||||
// Lines marking the bases of the cylinder.
|
||||
for (var i = 0; i < _circumferenceVertices.Length / 2; ++i) {
|
||||
var vertex = _circumferenceVertices[i * 2] * radius;
|
||||
var nextVertex = _circumferenceVertices[i * 2 + 1] * radius;
|
||||
DrawLine(position + offset + vertex, position + offset + nextVertex);
|
||||
DrawLine(position - offset + vertex, position - offset + nextVertex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector3 DrawHollowWireCylinder(Vector3 position, float radius, float height) {
|
||||
var offset = Vector3.up * (height * 0.5f);
|
||||
// Cylinder bases.
|
||||
DrawArc(position + offset, radius, Vector3.up, 1.0f, 0.0f);
|
||||
DrawArc(position - offset, radius, Vector3.up, 1.0f, 0.0f);
|
||||
// Lines marking the sides of the cylinder.
|
||||
for (var i = 0; i < _circumferenceVertices.Length; ++i) {
|
||||
// Lines marking the sides of the cylinder.
|
||||
var vertex = _circumferenceVertices[i] * radius;
|
||||
DrawLine(position + offset + vertex, position - offset + vertex);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Draws an arc in the XZ plane.
|
||||
// 'length' parameter, in range (0, 1], allows to define how much of the circumference should be
|
||||
// drawn. In other words, it allows to draw arbitrary arcs.
|
||||
// Two additional parameters, 'axis' and 'startAngle', allow to orient the figure without having
|
||||
// to manipulate the value of Gizmos.matrix.
|
||||
public static void DrawArcInternal(
|
||||
Vector3 position, float radius, Vector3 axis, float length, float startAngle) {
|
||||
var deltaAngle = 360.0f / NumCircleSegments;
|
||||
var numSegments = Math.Ceiling(length * NumCircleSegments);
|
||||
|
||||
var perpendicular = Vector3.Cross(axis, Vector3.right);
|
||||
if (perpendicular.magnitude <= _vectorPerpendicularityMagnitudeThreshold) {
|
||||
perpendicular = Vector3.Cross(axis, Vector3.up);
|
||||
}
|
||||
perpendicular.Normalize();
|
||||
|
||||
var offset = perpendicular * radius;
|
||||
var start = Quaternion.AngleAxis(startAngle, axis) * offset;
|
||||
var angle = startAngle + deltaAngle;
|
||||
|
||||
for (var segment = 0; segment < numSegments; ++segment, angle += deltaAngle) {
|
||||
var end = Quaternion.AngleAxis(angle, axis) * offset;
|
||||
DrawLine(position + start, position + end);
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawWirePlane(Vector3 position, float width, float height) {
|
||||
var quadPoints = new Vector3[] {
|
||||
new Vector3(-0.5f * width, 0, -0.5f * height),
|
||||
new Vector3(0.5f * width, 0, -0.5f * height),
|
||||
new Vector3(0.5f * width, 0, 0.5f * height),
|
||||
new Vector3(-0.5f * width, 0, 0.5f * height),
|
||||
new Vector3(-0.5f * width, 0, -0.5f * height),
|
||||
new Vector3(0.5f * width, 0, 0.5f * height),
|
||||
};
|
||||
for (var i = 0; i < quadPoints.Length - 1; ++i) {
|
||||
DrawLine(position + quadPoints[i], position + quadPoints[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user