Initial release of the Unity plugin.

PiperOrigin-RevId: 425947505
This commit is contained in:
Tom Erez
2022-02-02 20:17:13 +00:00
committed by Saran Tunyasuvunakool
parent 8ec3dd97a4
commit 6de9eafa13
298 changed files with 20617 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
# Changelog
## [2.1.1] - 2022-02-02
### Initial release
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e0f2a83d7d240967bb5945d478c8b7f5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 19efbd5e1b0bbc43b9f6a3defa20bb80
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -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:
+199
View File
@@ -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:
+77
View File
@@ -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:
+58
View File
@@ -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);
}
}
}
}
+13
View File
@@ -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:
+176
View File
@@ -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:
+8
View File
@@ -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:
+36
View File
@@ -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:
+127
View File
@@ -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:
+16
View File
@@ -0,0 +1,16 @@
{
"name": "Mujoco.Editor",
"rootNamespace": "",
"references": [
"Mujoco"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7bdf927c2041ed42ba41bdbbf914db24
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -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).
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: aefae1ccbed1b0597a0ac21f37888aa3
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fa19c3a835699cca3a2b4f2aabbe14b7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -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:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 49cbe951a7bc505f39a1762fe932f068
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+46
View File
@@ -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);
}
}
+13
View File
@@ -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:
+360
View File
@@ -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:
+394
View File
@@ -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:
+52
View File
@@ -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:
+24
View File
@@ -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:
+63
View File
@@ -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:
+80
View File
@@ -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:
+65
View File
@@ -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:
+42
View File
@@ -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);
}
}
}
+13
View File
@@ -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:
+55
View File
@@ -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:
+108
View File
@@ -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:
+51
View File
@@ -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:
+48
View File
@@ -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:
+54
View File
@@ -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:
+81
View File
@@ -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:
+29
View File
@@ -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:
+96
View File
@@ -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);
}
}
}
+13
View File
@@ -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:
+148
View File
@@ -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:
+116
View File
@@ -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]);
}
}
}
}
+13
View File
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f9b1794ad59f445418c86157f57519bd
timeCreated: 1536771527
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,378 @@
// 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.Runtime.InteropServices;
using System.Text;
using System.Xml;
using UnityEngine;
using UnityEngine.Profiling;
namespace Mujoco {
// The MuJoCo parser is case-sensitive. The case format of the entries in the enums below is
// exactly what the documentation specifies: http://mujoco.org/book/XMLreference.html#option .
public enum IntegratorType {
Euler,
RK4
}
public enum CollisionCheckType {
all
}
public enum FrictionConeType {
pyramidal,
elliptic
}
public enum JacobianType {
dense,
sparse,
auto
}
public enum ConstraintSolverType {
PGS,
CG,
Newton
}
public enum EnableDisableFlag {
enable,
disable
}
[Serializable]
public struct MjcfOptionFlag {
public EnableDisableFlag Constraint;
public EnableDisableFlag Equality;
public EnableDisableFlag FrictionLoss;
public EnableDisableFlag Limit;
public EnableDisableFlag Contact;
public EnableDisableFlag Passive;
public EnableDisableFlag Gravity;
public EnableDisableFlag ClampCtrl;
public EnableDisableFlag WarmStart;
public EnableDisableFlag FilterParent;
public EnableDisableFlag Actuation;
public EnableDisableFlag RefSafe;
public EnableDisableFlag Override;
public EnableDisableFlag Energy;
public EnableDisableFlag FwdInv;
public EnableDisableFlag SensorNoise;
public static MjcfOptionFlag Default = new MjcfOptionFlag() {
Constraint = EnableDisableFlag.enable,
Equality = EnableDisableFlag.enable,
FrictionLoss = EnableDisableFlag.enable,
Limit = EnableDisableFlag.enable,
Contact = EnableDisableFlag.enable,
Passive = EnableDisableFlag.enable,
Gravity = EnableDisableFlag.enable,
ClampCtrl = EnableDisableFlag.enable,
WarmStart = EnableDisableFlag.enable,
FilterParent = EnableDisableFlag.enable,
Actuation = EnableDisableFlag.enable,
RefSafe = EnableDisableFlag.enable,
Override = EnableDisableFlag.disable,
Energy = EnableDisableFlag.disable,
FwdInv = EnableDisableFlag.disable,
SensorNoise = EnableDisableFlag.disable
};
public void FromMjcf(XmlElement mjcf) {
var localDefault = MjcfOptionFlag.Default;
Constraint = mjcf.GetEnumAttribute<EnableDisableFlag>("constraint", localDefault.Constraint);
Equality = mjcf.GetEnumAttribute<EnableDisableFlag>("equality", localDefault.Equality);
FrictionLoss = mjcf.GetEnumAttribute<EnableDisableFlag>("frictionloss",
localDefault.FrictionLoss);
Limit = mjcf.GetEnumAttribute<EnableDisableFlag>("limit", localDefault.Limit);
Contact = mjcf.GetEnumAttribute<EnableDisableFlag>("contact", localDefault.Contact);
Passive = mjcf.GetEnumAttribute<EnableDisableFlag>("passive", localDefault.Passive);
Gravity = mjcf.GetEnumAttribute<EnableDisableFlag>("gravity", localDefault.Gravity);
ClampCtrl = mjcf.GetEnumAttribute<EnableDisableFlag>("clampctrl", localDefault.ClampCtrl);
WarmStart = mjcf.GetEnumAttribute<EnableDisableFlag>("warmstart", localDefault.WarmStart);
FilterParent = mjcf.GetEnumAttribute<EnableDisableFlag>("filterparent",
localDefault.FilterParent);
Actuation = mjcf.GetEnumAttribute<EnableDisableFlag>("actuation", localDefault.Actuation);
RefSafe = mjcf.GetEnumAttribute<EnableDisableFlag>("refsafe", localDefault.RefSafe);
Override = mjcf.GetEnumAttribute<EnableDisableFlag>("override", localDefault.Override);
Energy = mjcf.GetEnumAttribute<EnableDisableFlag>("energy", localDefault.Energy);
FwdInv = mjcf.GetEnumAttribute<EnableDisableFlag>("fwdinv", localDefault.FwdInv);
SensorNoise = mjcf.GetEnumAttribute<EnableDisableFlag>("sensornoise", localDefault.SensorNoise);
}
public void ToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("constraint", Constraint.ToString());
mjcf.SetAttribute("equality", Equality.ToString());
mjcf.SetAttribute("frictionloss", FrictionLoss.ToString());
mjcf.SetAttribute("limit", Limit.ToString());
mjcf.SetAttribute("contact", Contact.ToString());
mjcf.SetAttribute("passive", Passive.ToString());
mjcf.SetAttribute("gravity", Gravity.ToString());
mjcf.SetAttribute("clampctrl", ClampCtrl.ToString());
mjcf.SetAttribute("warmstart", WarmStart.ToString());
mjcf.SetAttribute("filterparent", FilterParent.ToString());
mjcf.SetAttribute("actuation", Actuation.ToString());
mjcf.SetAttribute("refsafe", RefSafe.ToString());
mjcf.SetAttribute("override", Override.ToString());
mjcf.SetAttribute("energy", Energy.ToString());
mjcf.SetAttribute("fwdinv", FwdInv.ToString());
mjcf.SetAttribute("sensornoise", SensorNoise.ToString());
}
}
[Serializable]
public struct MjSizeStruct {
public int Njmax;
public int Nconmax;
public int Nstack;
public static MjSizeStruct Default = new MjSizeStruct() {
Njmax = 0,
Nconmax = 0,
Nstack = 0
};
public void ParseMjcf(XmlElement mjcf) {
var localDefault = MjSizeStruct.Default;
Njmax = (int)mjcf.GetFloatAttribute("njmax", localDefault.Njmax);
Nconmax = (int)mjcf.GetFloatAttribute("nconmax", localDefault.Nconmax);
Nstack = (int)mjcf.GetFloatAttribute("nstack", localDefault.Nstack);
}
public XmlElement ToMjcf(XmlElement mjcf) {
if (Njmax > 0) {
mjcf.SetAttribute("njmax", $"{Njmax}");
}
if (Nconmax > 0) {
mjcf.SetAttribute("nconmax", $"{Nconmax}");
}
if (Nstack > 0) {
mjcf.SetAttribute("nstack", $"{Nstack}");
}
return mjcf;
}
}
[Serializable]
public struct MjOptionStruct {
// "timestep" and "gravity" come from global settings.
// "apirate" is only relevant to HAPTIX.
[Tooltip("Ratio of frictional-to-normal constraint impedance.")]
public float ImpRatio;
[Tooltip("Global magnetic flux used by magnetometer sensors.")]
public Vector3 Magnetic;
[Tooltip("Velocity vector of the medium, scales with the values of Density and Viscosity below.")]
public Vector3 Wind;
[Tooltip("Density of the medium for lift and drag forces. 0 disables lift and drag forces.")]
public float Density;
[Tooltip("Viscosity of the medium for viscous forces. 0 disables viscous forces.")]
public float Viscosity;
[Tooltip("Contact pair margin used when ContactOverride flag is set.")]
public float OverrideMargin;
[Tooltip("Contact pair solref used when ContactOverride flag is set.")]
public SolverReference OverrideSolRef;
[Tooltip("Contact pair solimp used when ContactOverride flag is set.")]
public SolverImpedance OverrideSolImp;
[Tooltip("Numerical integrator.")]
public IntegratorType Integrator;
[Tooltip("What collision types should be checked.")]
public CollisionCheckType Collision;
[Tooltip("How to model the friction cone.")]
public FrictionConeType Cone;
[Tooltip("How to represent the constraint Jacobian.")]
public JacobianType Jacobian;
[Tooltip("Constraint solver algorithm.")]
public ConstraintSolverType Solver;
[Tooltip("Maximum iterations for the constraint solver.")]
public int Iterations;
[Tooltip("Threshold used for early termination of the constraint solver.")]
public float Tolerance;
[Tooltip("Maximum iterations for the no-slip phase of the constraint solver.")]
public int NoSlipIterations;
[Tooltip("Threshold used for early termination of the Noslip solver.")]
public float NoSlipTolerance;
[Tooltip("Maximum iterations for convex mesh collisions.")]
public int MprIterations;
[Tooltip("Threshold used for early termination of the MPR algorithm.")]
public float MprTolerance;
public MjcfOptionFlag Flag;
// Default values copied from the "XML reference" page in the MuJoCo documentation.
public static MjOptionStruct Default = new MjOptionStruct() {
ImpRatio = 1.0f,
Magnetic = Vector3.zero,
Wind = new Vector3(0.0f, -0.5f, 0.0f),
Density = 0.0f,
Viscosity = 0.0f,
OverrideMargin = 0.0f,
OverrideSolRef = SolverReference.Default,
OverrideSolImp = SolverImpedance.Default,
Integrator = IntegratorType.Euler,
Collision = CollisionCheckType.all,
Cone = FrictionConeType.pyramidal,
Jacobian = JacobianType.auto,
Solver = ConstraintSolverType.Newton,
Iterations = 100,
Tolerance = 1e-8f,
NoSlipIterations = 0,
NoSlipTolerance = 1e-6f,
MprIterations = 50,
MprTolerance = 1e-6f,
Flag = MjcfOptionFlag.Default
};
public void ParseMjcf(XmlElement mjcf) {
if (mjcf.HasAttribute("timestep")) {
var mjTimestep = mjcf.GetFloatAttribute("timestep", Time.fixedDeltaTime);
if (mjTimestep != Time.fixedDeltaTime) {
Debug.unityLogger.LogWarning(
"MuJoCo",
$"MuJoCo's timestep ({mjTimestep}s) ignored, please set fixedDeltaTime manually.",
null);
}
}
if (mjcf.HasAttribute("gravity")) {
var mjGravity = mjcf.GetVector3Attribute("gravity", Vector3.zero);
Debug.unityLogger.LogWarning(
"MuJoCo",
$"MuJoCo's gravity ({mjGravity.x} {mjGravity.z} {mjGravity.y}) ignored, " +
"please set gravity in the Physics Manager.",
null);
}
var localDefault = MjOptionStruct.Default;
ImpRatio = mjcf.GetFloatAttribute("impratio", localDefault.ImpRatio);
Magnetic = mjcf.GetVector3Attribute("magnetic", localDefault.Magnetic);
Wind = mjcf.GetVector3Attribute("wind", localDefault.Wind);
Density = mjcf.GetFloatAttribute("density", localDefault.Density);
Viscosity = mjcf.GetFloatAttribute("viscosity", localDefault.Viscosity);
OverrideMargin = mjcf.GetFloatAttribute("o_margin", localDefault.OverrideMargin);
OverrideSolRef.FromMjcf(mjcf, "o_solref");
OverrideSolImp.FromMjcf(mjcf, "o_solimp");
Integrator = mjcf.GetEnumAttribute<IntegratorType>("integrator", localDefault.Integrator);
Collision = mjcf.GetEnumAttribute<CollisionCheckType>("collision", localDefault.Collision);
Cone = mjcf.GetEnumAttribute<FrictionConeType>("cone", localDefault.Cone);
Jacobian = mjcf.GetEnumAttribute<JacobianType>("jacobian", localDefault.Jacobian);
Solver = mjcf.GetEnumAttribute<ConstraintSolverType>("solver", localDefault.Solver);
Iterations = (int)mjcf.GetFloatAttribute("iterations", localDefault.Iterations);
Tolerance = mjcf.GetFloatAttribute("tolerance", localDefault.Tolerance);
NoSlipIterations = (int)mjcf.GetFloatAttribute(
"noslip_iterations", localDefault.NoSlipIterations);
NoSlipTolerance = mjcf.GetFloatAttribute("noslip_tolerance", localDefault.NoSlipTolerance);
MprIterations = (int)mjcf.GetFloatAttribute("mpr_iterations", localDefault.MprIterations);
MprTolerance = mjcf.GetFloatAttribute("mpr_tolerance", localDefault.MprTolerance);
var flagElements = mjcf.GetElementsByTagName("flag");
if (flagElements.Count == 1) {
Flag.FromMjcf(flagElements[0] as XmlElement);
} else if (flagElements.Count > 1) {
throw new ArgumentException("More than one <flag> node is not supported.");
}
}
public XmlElement ToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("impratio", $"{ImpRatio}");
mjcf.SetAttribute("magnetic", $"{Magnetic.x} {Magnetic.y} {Magnetic.z}");
mjcf.SetAttribute("wind", $"{Wind.x} {Wind.y} {Wind.z}");
mjcf.SetAttribute("density", $"{Density}");
mjcf.SetAttribute("viscosity", $"{Viscosity}");
mjcf.SetAttribute("o_margin", $"{OverrideMargin}");
OverrideSolRef.ToMjcf(mjcf, "o_solref");
OverrideSolImp.ToMjcf(mjcf, "o_solimp");
mjcf.SetAttribute("integrator", Integrator.ToString());
mjcf.SetAttribute("collision", Collision.ToString());
mjcf.SetAttribute("cone", Cone.ToString());
mjcf.SetAttribute("jacobian", Jacobian.ToString());
mjcf.SetAttribute("solver", Solver.ToString());
mjcf.SetAttribute("iterations", $"{Iterations}");
mjcf.SetAttribute("tolerance", $"{Tolerance}");
mjcf.SetAttribute("noslip_iterations", $"{NoSlipIterations}");
mjcf.SetAttribute("noslip_tolerance", $"{NoSlipTolerance}");
mjcf.SetAttribute("mpr_iterations", $"{MprIterations}");
mjcf.SetAttribute("mpr_tolerance", $"{MprTolerance}");
var flags = (XmlElement)mjcf.AppendChild(
mjcf.OwnerDocument.CreateElement("flag"));
Flag.ToMjcf(flags);
return mjcf;
}
}
public class MjGlobalSettings : MonoBehaviour {
[Tooltip("Filename for the generated scene XML.")]
public string DebugFileName;
[Tooltip("Scales the force applied by the mouse spring.")]
public float MouseSpringStiffness = 100;
public MjOptionStruct GlobalOptions = MjOptionStruct.Default;
public MjSizeStruct GlobalSizes = MjSizeStruct.Default;
public static MjGlobalSettings Instance {
get {
if (_instance == null) {
var instances = FindObjectsOfType<MjGlobalSettings>();
if (instances.Length > 1) {
throw new InvalidOperationException(
"Only one MjGlobalSettings instance is allowed - please resolve manually.");
} else if (instances.Length == 1) {
_instance = instances[0];
}
}
return _instance;
}
}
private static MjGlobalSettings _instance = null;
public void Awake() {
if (_instance == null) {
_instance = this;
} else if (_instance != this) {
throw new InvalidOperationException(
"At most one MjGlobalSettings should be present.");
}
}
public void ParseOptionSizeMjcf(XmlElement option, XmlElement size) {
if (option != null) {
GlobalOptions.ParseMjcf(option);
}
if (size != null) {
GlobalSizes.ParseMjcf(size);
}
}
public void OptionSizeToMjcf(XmlElement option, XmlElement size) {
GlobalOptions.ToMjcf(option);
GlobalSizes.ToMjcf(size);
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: aafd577af429d4011831f681fe16df0b
timeCreated: 1548950233
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More