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
@@ -0,0 +1,239 @@
// 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.Linq;
using System.Xml;
using NUnit.Framework;
using UnityEngine;
namespace Mujoco {
[TestFixture]
public class FindParentComponentTests {
public class FakeMjComponent : MjComponent {
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_GEOM;
protected override void OnParseMjcf(XmlElement mjcf) {}
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
return null;
}
}
public class FakeGenericComponent : MonoBehaviour {}
private FakeMjComponent _parent;
private FakeMjComponent _child;
private FakeMjComponent _secondChild;
private FakeGenericComponent _hierarchyBreaker;
[SetUp]
public void SetUp() {
_parent =
new GameObject("parent", typeof(FakeMjComponent)).GetComponent<FakeMjComponent>();
_child =
new GameObject("child", typeof(FakeMjComponent)).GetComponent<FakeMjComponent>();
_secondChild = new GameObject("secondChild", typeof(FakeMjComponent))
.GetComponent<FakeMjComponent>();
_hierarchyBreaker = new GameObject("hierarchyBreaker", typeof(FakeGenericComponent))
.GetComponent<FakeGenericComponent>();
}
[TearDown]
public void TearDown() {
GameObject.DestroyImmediate(_secondChild.gameObject);
GameObject.DestroyImmediate(_child.gameObject);
GameObject.DestroyImmediate(_hierarchyBreaker.gameObject);
GameObject.DestroyImmediate(_parent.gameObject);
}
[Test]
public void FindParentWithMjComponentAsImmediateParent() {
_child.transform.parent = _parent.transform;
Assert.That(MjHierarchyTool.FindParentComponent(_child), Is.EqualTo(_parent));
}
[Test]
public void FindParentWithHavingToSkipAParent() {
_hierarchyBreaker.transform.parent = _parent.transform;
_child.transform.parent = _hierarchyBreaker.transform;
Assert.That(MjHierarchyTool.FindParentComponent(_child), Is.EqualTo(_parent));
}
[Test]
public void FindParentWitNoMjComponentParent() {
_child.transform.parent = _hierarchyBreaker.transform;
Assert.That(MjHierarchyTool.FindParentComponent(_child), Is.Null);
}
[Test]
public void InChainOfThreeTheCorrectImmediateParentIsPicked() {
_child.transform.parent = _parent.transform;
_secondChild.transform.parent = _child.transform;
Assert.That(MjHierarchyTool.FindParentComponent(_secondChild), Is.EqualTo(_child));
}
}
[TestFixture]
public class HierachyLinearizationTests {
public class FakeMjComponent : MjComponent {
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_GEOM;
protected override void OnParseMjcf(XmlElement mjcf) {}
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
return null;
}
}
public class FakeGenericComponent : MonoBehaviour {}
private FakeMjComponent _root;
private FakeMjComponent _node01;
private FakeMjComponent _node02;
private FakeMjComponent _node11;
private FakeMjComponent _node21;
private FakeGenericComponent _hierarchyBreaker;
[SetUp]
public void SetUp() {
_root =
new GameObject("root", typeof(FakeMjComponent)).GetComponent<FakeMjComponent>();
_node01 =
new GameObject("node01", typeof(FakeMjComponent)).GetComponent<FakeMjComponent>();
_node02 =
new GameObject("node02", typeof(FakeMjComponent)).GetComponent<FakeMjComponent>();
_node11 =
new GameObject("node11", typeof(FakeMjComponent)).GetComponent<FakeMjComponent>();
_node21 =
new GameObject("node21", typeof(FakeMjComponent)).GetComponent<FakeMjComponent>();
_hierarchyBreaker = new GameObject("hierarchyBreaker", typeof(FakeGenericComponent))
.GetComponent<FakeGenericComponent>();
}
[TearDown]
public void TearDown() {
GameObject.DestroyImmediate(_node21.gameObject);
GameObject.DestroyImmediate(_node11.gameObject);
GameObject.DestroyImmediate(_node02.gameObject);
GameObject.DestroyImmediate(_node01.gameObject);
GameObject.DestroyImmediate(_hierarchyBreaker.gameObject);
GameObject.DestroyImmediate(_root.gameObject);
}
[Test]
public void OneNodeHierarchy() {
var hierarchy = MjHierarchyTool.LinearizeHierarchyBFS(_root.transform).ToArray();
Assert.That(hierarchy, Has.Length.EqualTo(1));
Assert.That(hierarchy[0], Is.EqualTo(_root));
}
[Test]
public void OneLevelDeepTreeExclusivelyWithMjComponents() {
_node01.transform.parent = _root.transform;
_node02.transform.parent = _root.transform;
var hierarchy = MjHierarchyTool.LinearizeHierarchyBFS(_root.transform).ToArray();
Assert.That(hierarchy, Has.Length.EqualTo(3));
Assert.That(hierarchy[0], Is.EqualTo(_root));
Assert.That(hierarchy[1], Is.EqualTo(_node01));
Assert.That(hierarchy[2], Is.EqualTo(_node02));
}
[Test]
public void DeepTreeExclusivelyWithMjComponents() {
_node01.transform.parent = _root.transform;
_node02.transform.parent = _root.transform;
_node11.transform.parent = _node01.transform;
_node21.transform.parent = _node02.transform;
var hierarchy = MjHierarchyTool.LinearizeHierarchyBFS(_root.transform).ToArray();
Assert.That(hierarchy, Has.Length.EqualTo(5));
Assert.That(hierarchy[0], Is.EqualTo(_root));
Assert.That(hierarchy[1], Is.EqualTo(_node01));
Assert.That(hierarchy[2], Is.EqualTo(_node02));
Assert.That(hierarchy[3], Is.EqualTo(_node11));
Assert.That(hierarchy[4], Is.EqualTo(_node21));
}
[Test]
public void TreeWithAGenericComponent() {
_node01.transform.parent = _root.transform;
_hierarchyBreaker.transform.parent = _root.transform;
_node02.transform.parent = _hierarchyBreaker.transform;
var hierarchy = MjHierarchyTool.LinearizeHierarchyBFS(_root.transform).ToArray();
Assert.That(hierarchy, Has.Length.EqualTo(3));
Assert.That(hierarchy[0], Is.EqualTo(_root));
Assert.That(hierarchy[1], Is.EqualTo(_node01));
Assert.That(hierarchy[2], Is.EqualTo(_node02));
}
}
[TestFixture]
public class GetComponentsInImmediateChildrenTests {
public class FakeComponentA : MonoBehaviour {}
public class FakeComponentB : MonoBehaviour {}
private FakeComponentA _root;
private FakeComponentA _validChild;
private FakeComponentB _invalidChild;
private FakeComponentA _validGrandchild;
[SetUp]
public void SetUp() {
_root = new GameObject("root", typeof(FakeComponentA)).GetComponent<FakeComponentA>();
_validChild =
new GameObject("validChild", typeof(FakeComponentA)).GetComponent<FakeComponentA>();
_invalidChild =
new GameObject("invalidChild", typeof(FakeComponentB)).GetComponent<FakeComponentB>();
_validGrandchild =
new GameObject("validGrandchild", typeof(FakeComponentA)).GetComponent<FakeComponentA>();
_validGrandchild.transform.parent = _validChild.transform;
_validChild.transform.parent = _root.transform;
}
[TearDown]
public void TearDown() {
GameObject.DestroyImmediate(_validGrandchild.gameObject);
GameObject.DestroyImmediate(_invalidChild.gameObject);
GameObject.DestroyImmediate(_validChild.gameObject);
GameObject.DestroyImmediate(_root.gameObject);
}
[Test]
public void RootNodeIsIgnoredInTheSearch() {
var components =
MjHierarchyTool.GetComponentsInImmediateChildren<FakeComponentA>(_root.transform);
Assert.That(components, Has.No.Member(_root));
}
[Test]
public void ImmediateValidChildIsReturned() {
var components =
MjHierarchyTool.GetComponentsInImmediateChildren<FakeComponentA>(_root.transform);
Assert.That(components, Has.Member(_validChild));
}
[Test]
public void ChildWithAnInvalidTypeIsIgnored() {
var components =
MjHierarchyTool.GetComponentsInImmediateChildren<FakeComponentA>(_root.transform);
Assert.That(components, Has.No.Member(_invalidChild));
}
[Test]
public void GrandchildrenAreIgnoredDuringSearch() {
var components =
MjHierarchyTool.GetComponentsInImmediateChildren<FakeComponentA>(_root.transform);
Assert.That(components, Has.No.Member(_validGrandchild));
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 2eca32299982b4901a0b01568b992fed
timeCreated: 1537463659
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+392
View File
@@ -0,0 +1,392 @@
// 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.Runtime.InteropServices;
using System.Xml;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools.Utils;
namespace Mujoco {
[TestFixture]
public class MjEngineToolXmlLoadingTests {
[Test]
public unsafe void LoadingSceneFromAProvidedAsset() {
var modelFile = Resources.Load<TextAsset>("ValidModel");
var modelPtr = MjEngineTool.LoadModelFromString(modelFile.text);
var model =
(MujocoLib.mjModel_)Marshal.PtrToStructure(new IntPtr(modelPtr), typeof(MujocoLib.mjModel_));
Assert.That(model.nbody, Is.EqualTo(3));
}
}
[TestFixture]
public class MjEngineToolTransformHelpersTests {
[TestCase(1, 2, 3)]
[TestCase(-1, -2, -3)]
[TestCase(-1, 2, 3)]
[TestCase(1, -2, 3)]
[TestCase(1, 2, -3)]
public unsafe void RoundrobinConversionOfVector3(float x, float y, float z) {
var vec = new Vector3(x, y, z);
var MjVec = MjEngineTool.MjVector3(vec);
var MjVecAsArray = new double[] { 10, 11, 12, MjVec.x, MjVec.y, MjVec.z };
fixed (double* MjArrPtr = MjVecAsArray) {
var recreatedVec = MjEngineTool.UnityVector3(MjArrPtr, 1);
Assert.That(recreatedVec, Is.EqualTo(vec));
}
}
[TestCase(1, 2, 3)]
[TestCase(-1, -2, -3)]
[TestCase(-1, 2, 3)]
[TestCase(1, -2, 3)]
[TestCase(1, 2, -3)]
public void RoundrobinConversionOfVector3UsingCoreType(float x, float y, float z) {
var vec = new Vector3(x, y, z);
var MjVec = MjEngineTool.MjVector3(vec);
var unityVec = MjEngineTool.UnityVector3(MjVec);
Assert.That(unityVec, Is.EqualTo(vec));
}
[TestCase(0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(-0.1f, -0.2f, -0.3f, -0.4f)]
[TestCase(-0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, -0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, -0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, 0.3f, -0.4f)]
public unsafe void RoundrobinConversionOfQuaternion(float x, float y, float z, float w) {
var quat = new Quaternion(x, y, z, w);
var MjQuat = MjEngineTool.MjQuaternion(quat);
var MjQuatAsArray =
new double[] { 10, 20, 30, 40, MjQuat.w, MjQuat.x, MjQuat.y, MjQuat.z };
var recreatedQuat = MjEngineTool.UnityQuaternion(MjQuatAsArray, 1);
var q1 = new Vector4(quat.x, quat.y, quat.z, quat.w);
var q2 = new Vector4(recreatedQuat.x, recreatedQuat.y, recreatedQuat.z, recreatedQuat.w);
Assert.That(q1, Is.EqualTo(q2));
}
[TestCase(0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(-0.1f, -0.2f, -0.3f, -0.4f)]
[TestCase(-0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, -0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, -0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, 0.3f, -0.4f)]
public unsafe void RoundrobinConversionOfQuaternionUsingUnsafeArrays(float x, float y, float z,
float w) {
var quat = new Quaternion(x, y, z, w);
var MjQuat = MjEngineTool.MjQuaternion(quat);
var MjQuatAsArray =
new double[] { 10, 20, 30, 40, MjQuat.w, MjQuat.x, MjQuat.y, MjQuat.z };
fixed (double* MjArrPtr = MjQuatAsArray) {
var recreatedQuat = MjEngineTool.UnityQuaternion(MjArrPtr, 1);
var q1 = new Vector4(quat.x, quat.y, quat.z, quat.w);
var q2 = new Vector4(recreatedQuat.x, recreatedQuat.y, recreatedQuat.z, recreatedQuat.w);
Assert.That(q1, Is.EqualTo(q2));
}
}
[TestCase(0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(-0.1f, -0.2f, -0.3f, -0.4f)]
[TestCase(-0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, -0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, -0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, 0.3f, -0.4f)]
public void RoundrobinConversionOfQuaternionUsingCoreType(float x, float y, float z, float w) {
var quat = new Quaternion(x, y, z, w);
var MjQuat = MjEngineTool.MjQuaternion(quat);
var unityQuat = MjEngineTool.UnityQuaternion(MjQuat);
Assert.That(unityQuat, Is.EqualTo(quat));
}
[TestCase(1, 2, 3)]
[TestCase(-1, -2, -3)]
[TestCase(-1, 2, 3)]
[TestCase(1, -2, 3)]
[TestCase(1, 2, -3)]
public void RoundrobinConversionOfExtents(float x, float y, float z) {
var extents = new Vector3(x, y, z);
var MjExtents = MjEngineTool.MjExtents(extents);
var unityExtents = MjEngineTool.UnityExtents(MjExtents);
Assert.That(MjExtents, Is.Not.EqualTo(extents));
Assert.That(unityExtents, Is.EqualTo(extents));
}
[TestCase(0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(-0.1f, -0.2f, -0.3f, -0.4f)]
[TestCase(-0.1f, 0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, -0.2f, 0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, -0.3f, 0.4f)]
[TestCase(0.1f, 0.2f, 0.3f, -0.4f)]
public unsafe void SettingAndRetrievingQuaternions(float x, float y, float z, float w) {
var quat = new Quaternion(x, y, z, w);
var result = Quaternion.identity;
var buffer = new double[4];
fixed (double* unsafeBuffer = buffer) {
MjEngineTool.SetMjQuaternion(unsafeBuffer, quat, entryIndex: 0);
result = MjEngineTool.UnityQuaternion(unsafeBuffer, entryIndex: 0);
}
Assert.That(quat, Is.EqualTo(result));
}
[TestCase(1, 2, 3)]
[TestCase(-1, -2, -3)]
[TestCase(-1, 2, 3)]
[TestCase(1, -2, 3)]
[TestCase(1, 2, -3)]
public unsafe void SettingAndRetrievingVectors(float x, float y, float z) {
var vec = new Vector3(x, y, z);
var result = Vector3.zero;
double[] buffer = new double[3];
fixed (double* unsafeBuffer = buffer) {
MjEngineTool.SetMjVector3(unsafeBuffer, vec, entryIndex: 0);
result = MjEngineTool.UnityVector3(unsafeBuffer, entryIndex: 0);
}
Assert.That(vec, Is.EqualTo(result));
}
}
[TestFixture]
public class MjEngineToolTransformSerializationTests {
public class FakeMjComponent : MjComponent {
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_GEOM;
protected override void OnParseMjcf(XmlElement mjcf) {}
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
return doc.CreateElement("geom");
}
}
private GameObject _rootObject;
private GameObject _intermediateObject;
private FakeMjComponent _component;
private Vector3EqualityComparer _vectorComparer;
private Vector4EqualityComparer _quaternionComparer;
[SetUp]
public void SetUp() {
_rootObject = new GameObject("rootObject");
_intermediateObject = new GameObject("intermediateObject");
_component = new GameObject("component").AddComponent<FakeMjComponent>();
var epsilon = 1e-3f;
_vectorComparer = new Vector3EqualityComparer(epsilon);
_quaternionComparer = new Vector4EqualityComparer(epsilon);
}
[TearDown]
public void TearDown() {
UnityEngine.Object.DestroyImmediate(_component.gameObject);
UnityEngine.Object.DestroyImmediate(_intermediateObject);
UnityEngine.Object.DestroyImmediate(_rootObject);
MjSceneImportSettings.AnglesInDegrees = true;
}
[Test]
public void SerializingPositionRotationOfRootComponent() {
_component.transform.position = new Vector3(1, 2, 3);
_component.transform.rotation = Quaternion.AngleAxis(45, Vector3.up);
var doc = new XmlDocument();
var mjcf = doc.CreateElement("element");
MjEngineTool.PositionRotationToMjcf(mjcf, _component);
Assert.That(mjcf.GetAttribute("pos"), Is.EqualTo("1 3 2"));
Assert.That(mjcf.GetQuaternionAttribute("quat", Quaternion.identity),
Is.EqualTo(new Quaternion(w:-0.9238795f, x:0, y:0, z:0.3826835f))
.Using(_quaternionComparer));
}
[Test]
public void SerializingPositionRotationOfRootComponentParentedToGameObject() {
_component.transform.parent = _rootObject.transform;
_rootObject.transform.position = new Vector3(1, 2, 3);
_rootObject.transform.rotation = Quaternion.AngleAxis(45, Vector3.up);
var doc = new XmlDocument();
var mjcf = doc.CreateElement("element");
MjEngineTool.PositionRotationToMjcf(mjcf, _component);
Assert.That(mjcf.GetAttribute("pos"), Is.EqualTo("1 3 2"));
Assert.That(mjcf.GetQuaternionAttribute("quat", Quaternion.identity),
Is.EqualTo(new Quaternion(w:-0.9238795f, x:0, y:0, z:0.3826835f))
.Using(_quaternionComparer));
}
[Test]
public void SerializingPositionRotationOfChildComponentParentedThroughGameObject() {
_component.transform.parent = _intermediateObject.transform;
_intermediateObject.transform.parent = _rootObject.transform;
_intermediateObject.transform.position = new Vector3(1, 2, 3);
_intermediateObject.transform.rotation = Quaternion.AngleAxis(45, Vector3.up);
var doc = new XmlDocument();
var mjcf = doc.CreateElement("element");
MjEngineTool.PositionRotationToMjcf(mjcf, _component);
Assert.That(mjcf.GetAttribute("pos"), Is.EqualTo("1 3 2"));
Assert.That(mjcf.GetQuaternionAttribute("quat", Quaternion.identity),
Is.EqualTo(new Quaternion(w:-0.9238795f, x:0, y:0, z:0.3826835f))
.Using(_quaternionComparer));
}
[Test]
public void SerializingPositionAxisOfRootComponent() {
_component.transform.position = new Vector3(1, 2, 3);
_component.transform.rotation = Quaternion.AngleAxis(45, Vector3.up);
var doc = new XmlDocument();
var mjcf = doc.CreateElement("element");
MjEngineTool.PositionAxisToMjcf(mjcf, _component);
Assert.That(mjcf.GetAttribute("pos"), Is.EqualTo("1 3 2"));
Assert.That(mjcf.GetVector3Attribute("axis", Vector3.zero),
Is.EqualTo(new Vector3(0.7071068f, -0.7071069f, 0)).Using(_vectorComparer));
Assert.That(mjcf.GetAttribute("ref"), Is.EqualTo("0"));
}
[Test]
public void SerializingPositionAxisOfRootComponentParentedToGameObject() {
_component.transform.parent = _rootObject.transform;
_rootObject.transform.position = new Vector3(1, 2, 3);
_rootObject.transform.rotation = Quaternion.AngleAxis(45, Vector3.up);
var doc = new XmlDocument();
var mjcf = doc.CreateElement("element");
MjEngineTool.PositionAxisToMjcf(mjcf, _component);
Assert.That(mjcf.GetAttribute("pos"), Is.EqualTo("1 3 2"));
Assert.That(mjcf.GetVector3Attribute("axis", Vector3.zero),
Is.EqualTo(new Vector3(0.7071068f, -0.7071069f, 0)).Using(_vectorComparer));
Assert.That(mjcf.GetAttribute("ref"), Is.EqualTo("0"));
}
[Test]
public void SerializingPositionAxisOfChildComponentParentedThroughGameObject() {
_component.transform.parent = _intermediateObject.transform;
_intermediateObject.transform.parent = _rootObject.transform;
_intermediateObject.transform.position = new Vector3(1, 2, 3);
_intermediateObject.transform.rotation = Quaternion.AngleAxis(45, Vector3.up);
var doc = new XmlDocument();
var mjcf = doc.CreateElement("element");
MjEngineTool.PositionAxisToMjcf(mjcf, _component);
Assert.That(mjcf.GetAttribute("pos"), Is.EqualTo("1 3 2"));
Assert.That(mjcf.GetVector3Attribute("axis", Vector3.zero),
Is.EqualTo(new Vector3(0.7071068f, -0.7071069f, 0)).Using(_vectorComparer));
Assert.That(mjcf.GetAttribute("ref"), Is.EqualTo("0"));
}
[Test]
public void ParsePosition() {
var mjcf = new XmlDocument().CreateElement("element");
mjcf.SetAttribute("pos", "1 3 2");
MjEngineTool.ParseTransformMjcf(mjcf, _rootObject.transform);
Assert.That(_rootObject.transform.position, Is.EqualTo(new Vector3(1, 2, 3)));
}
[Test]
public void ParseRotationFromQuat() {
var mjcf = new XmlDocument().CreateElement("element");
mjcf.SetAttribute("quat", "-0.9238795 0 0 0.3826835");
MjEngineTool.ParseTransformMjcf(mjcf, _rootObject.transform);
var expectedRotation = Quaternion.AngleAxis(45, Vector3.up);
Assert.That(_rootObject.transform.rotation.x, Is.EqualTo(expectedRotation.x).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.y, Is.EqualTo(expectedRotation.y).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.z, Is.EqualTo(expectedRotation.z).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.w, Is.EqualTo(expectedRotation.w).Within(1e-5f));
// Assert.That(
// _rootObject.transform.rotation,
// Is.EqualTo(Quaternion.AngleAxis(45, Vector3.up))
// .Using(_quaternionComparer));
}
[Test]
public void ParseRotationFromZAxis() {
var mjcf = new XmlDocument().CreateElement("element");
mjcf.SetAttribute("zaxis", "0 1 0");
MjEngineTool.ParseTransformMjcf(mjcf, _rootObject.transform);
var expectedRotation = Quaternion.AngleAxis(90, Vector3.right);
Assert.That(_rootObject.transform.rotation.x, Is.EqualTo(expectedRotation.x).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.y, Is.EqualTo(expectedRotation.y).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.z, Is.EqualTo(expectedRotation.z).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.w, Is.EqualTo(expectedRotation.w).Within(1e-5f));
// Assert.That(
// _rootObject.transform.rotation,
// Is.EqualTo(Quaternion.AngleAxis(90, Vector3.right))
// .Using(_quaternionComparer));
}
[TestCase(true, 45, 45)]
[TestCase(false, 0.785398f, 45)]
[TestCase(true, 90, 90)]
[TestCase(false, 1.570796f, 90)]
public void ParseRotationFromAxisAngle(bool useDegrees, float MjAngle,
float expectedAngle) {
MjSceneImportSettings.AnglesInDegrees = useDegrees;
var mjcf = new XmlDocument().CreateElement("element");
mjcf.SetAttribute("axisangle", $"1 0 0 {MjAngle}");
MjEngineTool.ParseTransformMjcf(mjcf, _rootObject.transform);
var expectedRotation = Quaternion.AngleAxis(expectedAngle, Vector3.right);
Assert.That(_rootObject.transform.rotation.x, Is.EqualTo(expectedRotation.x).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.y, Is.EqualTo(expectedRotation.y).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.z, Is.EqualTo(expectedRotation.z).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.w, Is.EqualTo(expectedRotation.w).Within(1e-5f));
// Assert.That(
// _rootObject.transform.rotation,
// Is.EqualTo(Quaternion.AngleAxis(expectedAngle, Vector3.right))
// .Using(_quaternionComparer));
}
[TestCase(true, 0, 45, 0, 0, 0, -45)]
[TestCase(false, 0, 0.785398f, 0, 0, 0, -45)]
[TestCase(true, 0, 0, 45, 0, 45, 0)]
[TestCase(false, 0, 0, 0.785398f, 0, 45, 0)]
[TestCase(true, 45, 0, 0, 45, 0, 0)]
[TestCase(false, 0.785398f, 0, 0, 45, 0, 0)]
public void ParseRotationFromEuler(bool useDegrees, float mx, float my, float mz, float ex,
float ey, float ez) {
MjSceneImportSettings.AnglesInDegrees = useDegrees;
var mjcf = new XmlDocument().CreateElement("element");
mjcf.SetAttribute("euler", $"{mx} {my} {mz}");
MjEngineTool.ParseTransformMjcf(mjcf, _rootObject.transform);
var expectedRotation = Quaternion.Euler(ex, ey, ez);
Assert.That(_rootObject.transform.rotation.x, Is.EqualTo(expectedRotation.x).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.y, Is.EqualTo(expectedRotation.y).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.z, Is.EqualTo(expectedRotation.z).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.w, Is.EqualTo(expectedRotation.w).Within(1e-5f));
// Assert.That(
// _rootObject.transform.rotation,
// Is.EqualTo(Quaternion.Euler(ex, ey, ez))
// .Using(_quaternionComparer));
}
[Test]
public void ParseRotationFromDirectionVector() {
var mjcf = new XmlDocument().CreateElement("element");
mjcf.SetAttribute("fromto", $"0 0 0 1 1 0");
MjEngineTool.ParseTransformMjcf(mjcf, _rootObject.transform);
var expectedRotation = new Quaternion(0.5f, 0, -0.5f, 0.7071068f);
Assert.That(_rootObject.transform.rotation.x, Is.EqualTo(expectedRotation.x).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.y, Is.EqualTo(expectedRotation.y).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.z, Is.EqualTo(expectedRotation.z).Within(1e-5f));
Assert.That(_rootObject.transform.rotation.w, Is.EqualTo(expectedRotation.w).Within(1e-5f));
// Assert.That(
// _rootObject.transform.rotation,
// Is.EqualTo(new Quaternion(0.5f, 0, -0.5f, 0.7071068f))
// .Using(_quaternionComparer));
}
[Test]
public void ParsePositionFromDirectionVector() {
var mjcf = new XmlDocument().CreateElement("element");
mjcf.SetAttribute("fromto", $"1 2 3 5 7 9");
MjEngineTool.ParseTransformMjcf(mjcf, _rootObject.transform);
Assert.That(_rootObject.transform.position, Is.EqualTo(new Vector3(3, 6, 4.5f)));
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: ff03f770fd9aa43cd95e04660fd82bb3
timeCreated: 1536938636
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,159 @@
// 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 NUnit.Framework;
using UnityEngine;
namespace Mujoco {
[TestFixture]
public class MjTransformationTests {
[Test]
public void ConcatenateTranslation() {
var t1 = new MjTransformation(new Vector3(10, 0, 0), Quaternion.identity);
var t2 = new MjTransformation(new Vector3(0, 20, 0), Quaternion.identity);
var result = t1 * t2;
var expectedResult = new MjTransformation(new Vector3(10, 20, 0), Quaternion.identity);
CompareTransforms(result, expectedResult);
}
[Test]
public void ConcatenateRotation() {
var t1 = new MjTransformation(Vector3.zero, Quaternion.Euler(0, 45, 0));
var t2 = new MjTransformation(Vector3.zero, Quaternion.Euler(0, 15, 0));
var result = t1 * t2;
var expectedResult = new MjTransformation(Vector3.zero, Quaternion.Euler(0, 60, 0));
CompareTransforms(result, expectedResult);
}
[Test]
public void ConcatenateTranslationAndRotation() {
var t1 = new MjTransformation(new Vector3(10, 0, 0), Quaternion.Euler(0, 45, 0));
var t2 = new MjTransformation(new Vector3(20, 0, 0), Quaternion.Euler(0, 15, 0));
var result = t1 * t2;
// T2 is some translation and rotation in T1's reference frame. We want to learn what that
// transform would look like in global space - as if there was no T1.
// T1 is located at (10, 0, 0), and rotates its children by 45 deg around the Y axis.
// That means it will rotate T2 translation (20, 0, 0) by 45 degrees. Since the rotation
// is performed around the Y axis, the X coordinate 20 will be projected onto X and Z axis
// in amount [cos(45deg) * 20, -sin(45deg) * 20] = [14.14213, -14.14213].
// Next, the rotation will be added to that of the parent: 45 + 15 = 60 deg.
var expectedResult = new MjTransformation(new Vector3(24.14213f, 0, -14.14213f),
Quaternion.Euler(0, 60, 0));
CompareTransforms(result, expectedResult);
}
[Test]
public void ConcatenatingRightInverse() {
var t1 = new MjTransformation(new Vector3(10, 0, 0), Quaternion.Euler(0, 45, 0));
var result = t1 * t1.Inverse();
var expectedResult = new MjTransformation(Vector3.zero, Quaternion.identity);
CompareTransforms(result, expectedResult);
}
[Test]
public void ConcatenatingLeftInverse() {
var t1 = new MjTransformation(new Vector3(10, 0, 0), Quaternion.Euler(0, 45, 0));
var result = t1.Inverse() * t1;
var expectedResult = new MjTransformation(Vector3.zero, Quaternion.identity);
CompareTransforms(result, expectedResult);
}
[Test]
public void MjTransformationsConcatenationIsTheSameAsUnitys() {
_parent.transform.position = new Vector3(10, 0, 0);
_parent.transform.rotation = Quaternion.Euler(0, 45, 0);
_child.transform.localPosition = new Vector3(20, 0, 0);
_child.transform.localRotation = Quaternion.Euler(0, 15, 0);
var t1 = MjTransformation.LoadGlobal(_parent.transform);
var t2 = MjTransformation.LoadLocal(_child.transform);
var result = t1 * t2;
var expectedResult = MjTransformation.LoadGlobal(_child.transform);
var expectedExplicitResult = new MjTransformation(new Vector3(24.14213f, 0, -14.14213f),
Quaternion.Euler(0, 60, 0));
CompareTransforms(result, expectedResult);
CompareTransforms(result, expectedExplicitResult);
}
[Test]
public void LoadingGlobalTransform() {
var result = MjTransformation.LoadGlobal(_parent.transform);
CompareTransforms(result, _parent.transform.position, _parent.transform.rotation);
}
[Test]
public void LoadingLocalTransform() {
var result = MjTransformation.LoadLocal(_parent.transform);
CompareTransforms(result, _parent.transform.localPosition, _parent.transform.localRotation);
}
[Test]
public void StoringGlobalTransform() {
var transform = new MjTransformation(new Vector3(1, 2, 3), Quaternion.Euler(0, 45, 0));
transform.StoreGlobal(_parent.transform);
CompareTransforms(transform, _parent.transform.position, _parent.transform.rotation);
}
[Test]
public void StoringLocalTransform() {
var transform = new MjTransformation(new Vector3(1, 2, 3), Quaternion.Euler(0, 45, 0));
transform.StoreGlobal(_parent.transform);
CompareTransforms(transform, _parent.transform.localPosition,
_parent.transform.localRotation);
}
#region Setup
private GameObject _parent;
private GameObject _child;
[SetUp]
public void SetUp() {
_parent = new GameObject("parent");
_child = new GameObject("child");
_child.transform.parent = _parent.transform;
}
[TearDown]
public void TearDown() {
UnityEngine.Object.DestroyImmediate(_parent);
}
private void CompareTransforms(MjTransformation lhs, MjTransformation rhs) {
Assert.That(lhs.Translation.x, Is.EqualTo(rhs.Translation.x).Within(1e-5f));
Assert.That(lhs.Translation.y, Is.EqualTo(rhs.Translation.y).Within(1e-5f));
Assert.That(lhs.Translation.z, Is.EqualTo(rhs.Translation.z).Within(1e-5f));
Assert.That(lhs.Rotation.x, Is.EqualTo(rhs.Rotation.x).Within(1e-5f));
Assert.That(lhs.Rotation.y, Is.EqualTo(rhs.Rotation.y).Within(1e-5f));
Assert.That(lhs.Rotation.z, Is.EqualTo(rhs.Rotation.z).Within(1e-5f));
Assert.That(lhs.Rotation.w, Is.EqualTo(rhs.Rotation.w).Within(1e-5f));
}
private void CompareTransforms(MjTransformation lhs, Vector3 position,
Quaternion rotation) {
Assert.That(lhs.Translation.x, Is.EqualTo(position.x).Within(1e-5f));
Assert.That(lhs.Translation.y, Is.EqualTo(position.y).Within(1e-5f));
Assert.That(lhs.Translation.z, Is.EqualTo(position.z).Within(1e-5f));
Assert.That(lhs.Rotation.x, Is.EqualTo(rotation.x).Within(1e-5f));
Assert.That(lhs.Rotation.y, Is.EqualTo(rotation.y).Within(1e-5f));
Assert.That(lhs.Rotation.z, Is.EqualTo(rotation.z).Within(1e-5f));
Assert.That(lhs.Rotation.w, Is.EqualTo(rotation.w).Within(1e-5f));
}
#endregion
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 232da11066f724246bf5b820d6647261
timeCreated: 1551107971
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,62 @@
// 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.Runtime.InteropServices;
using NUnit.Framework;
namespace Mujoco {
using static MujocoLib;
[TestFixture]
public class MjVfsTests {
private MjVfs _vfs;
[SetUp]
public void SetUp() {
_vfs = new MjVfs();
}
[TearDown]
public void TearDown() {
_vfs.Dispose();
_vfs = null;
}
[Test]
public unsafe void AddingNewFile() {
var filename = "filename";
var contents = "contents";
_vfs.AddFile(filename, contents);
Assert.That(_vfs.FilesCount, Is.EqualTo(1));
}
[Test]
public unsafe void LocatingFileUsingMujocoLib() {
var filename = "filename";
_vfs.AddFile(filename, "contents");
Assert.That(_vfs.FindFile(filename), Is.EqualTo(0));
}
[Test]
public unsafe void AddedFileContentsAreCorrect() {
var filename = "filename";
var contents = "contents";
_vfs.AddFile(filename, contents);
var filedata = Marshal.PtrToStringAnsi(_vfs.Data.filedata[0]);
Assert.That(filedata, Is.EqualTo(contents));
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 41b2695e2bc7a4317bc759f1eae39ab2
timeCreated: 1535647485
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: 676e0a086d0ff32f6b91e644a9fc1f23
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,30 @@
<!--
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.
-->
<mujoco>
<option gravity="0 -9.81 0" />
<worldbody>
<geom name="ground" type="plane" size="5 5 1" euler="-90 0 0"/>
<body name="parent" pos="0 10 0">
<geom name="parent_shape" type="sphere" size="0.5"/>
<joint type="free" />
<body name="child" pos="2 1 0">
<geom name="child_shape" type="sphere" size="0.5"/>
<joint type="hinge" axis="1 0 0"/>
</body>
</body>
</worldbody>
</mujoco>
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4ec05e5bec3e158bb9cf4cc868f75f03
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,193 @@
// 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.Xml;
using NUnit.Framework;
using UnityEngine;
namespace Mujoco {
[TestFixture]
public class XmlElementExtensionsEditorTests {
public enum TestEnum { ValueDefault, ValueA }
private XmlElement _element;
[SetUp]
public void SetUp() {
var doc = new XmlDocument();
_element = doc.CreateElement("element");
}
[Test]
public void ParsingMissingFloatAttribute() {
Assert.That(_element.GetFloatAttribute("attrib", 7.0f), Is.EqualTo(7.0f));
}
[TestCase("5.1", 5.1f)]
[TestCase("-4.83", -4.83f)]
public void ParsingFloatAttribute(string input, float expected) {
_element.SetAttribute("attrib", input);
Assert.That(_element.GetFloatAttribute("attrib", 7.0f), Is.EqualTo(expected));
}
[TestCase("")]
[TestCase("string value")]
public void InvalidFloatValueTriggersAnException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetFloatAttribute("attrib", 7.0f); }, Throws.Exception);
}
[Test]
public void ParsingMissingEnumAttribute() {
Assert.That(_element.GetEnumAttribute<TestEnum>("attrib", TestEnum.ValueDefault),
Is.EqualTo(TestEnum.ValueDefault));
}
[TestCase("ValueA", TestEnum.ValueA)]
[TestCase("valuea", TestEnum.ValueA)]
public void ParsingEnumAttributeIsCaseInsensitive(string input, TestEnum expected) {
_element.SetAttribute("attrib", input);
Assert.That(_element.GetEnumAttribute<TestEnum>("attrib", TestEnum.ValueDefault, true),
Is.EqualTo(expected));
}
[TestCase("")]
[TestCase("wrong value")]
public void InvalidEnumValueTriggersAnException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetEnumAttribute<TestEnum>("attrib", TestEnum.ValueDefault); },
Throws.Exception);
}
[Test]
public void ParsingMissingStringAttribute() {
Assert.That(_element.GetStringAttribute("attrib", "missing"), Is.EqualTo("missing"));
}
[TestCase("value", "value")]
[TestCase("", "")]
public void ParsingStringAttributes(string input, string expected) {
_element.SetAttribute("attrib", input);
Assert.That(_element.GetStringAttribute("attrib", "missing"), Is.EqualTo(expected));
}
[Test]
public void ParsingMissingVector3Attribute() {
Assert.That(_element.GetVector3Attribute("attrib", Vector3.one), Is.EqualTo(Vector3.one));
}
public void ParsingVector3Attributes() {
_element.SetAttribute("attrib", "-1 2 3.5");
var expected = new Vector3(-1, 2, 3.5f);
Assert.That(_element.GetVector3Attribute("attrib", Vector3.one), Is.EqualTo(expected));
}
[TestCase("1 2")]
[TestCase("1")]
[TestCase("")]
public void Vector3WithInsufficientNumberOfComponentsTriggersAnException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetVector3Attribute("attrib", Vector3.one); }, Throws.Exception);
}
[TestCase("a 2 3")]
[TestCase("1 b 3")]
[TestCase("1 2 c")]
public void InvalidVector3ComponentValueTriggersException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetVector3Attribute("attrib", Vector3.one); }, Throws.Exception);
}
public void ParsingVector2Attributes() {
_element.SetAttribute("attrib", "-1 2.5");
var expected = new Vector3(-1, 2.5f);
Assert.That(_element.GetVector2Attribute("attrib", Vector2.one), Is.EqualTo(expected));
}
[TestCase("1")]
[TestCase("")]
public void Vector2WithInsufficientNumberOfComponentsTriggersAnException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetVector2Attribute("attrib", Vector2.one); }, Throws.Exception);
}
[TestCase("a 2")]
[TestCase("1 b")]
public void InvalidVector2ComponentValueTriggersException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetVector2Attribute("attrib", Vector2.one); }, Throws.Exception);
}
[Test]
public void ParsingMissingQuaternionAttribute() {
Assert.That(_element.GetQuaternionAttribute("attrib", Quaternion.identity),
Is.EqualTo(Quaternion.identity));
}
public void ParsingQuaternionAttributes() {
_element.SetAttribute("attrib", "0.1 0.2 0.3 0.4");
var expected = new Quaternion(0.1f, 0.2f, 0.3f, 0.4f);
Assert.That(_element.GetQuaternionAttribute("attrib", Quaternion.identity),
Is.EqualTo(expected));
}
[TestCase("1 2 3")]
[TestCase("1 2")]
[TestCase("1")]
[TestCase("")]
public void QuaternionWithInsufficientNumberOfComponentsTriggersAnException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetQuaternionAttribute("attrib", Quaternion.identity); },
Throws.Exception);
}
[TestCase("a 2 3 4")]
[TestCase("1 b 3 4")]
[TestCase("1 2 c 4")]
[TestCase("1 2 3 d")]
public void InvalidQuaternionComponentValueTriggersException(string input) {
_element.SetAttribute("attrib", input);
Assert.That(() => { _element.GetQuaternionAttribute("attrib", Quaternion.identity); },
Throws.Exception);
}
[TestCase("1 2")]
[TestCase("1 2 ")]
[TestCase(" 1 2")]
public void FloatArraysCanContainArbitraryNumberOfWhitespaces(string input) {
_element.SetAttribute("attrib", input);
Assert.That(_element.GetFloatArrayAttribute("attrib", defaultValue: null),
Is.EqualTo(new float[] { 1, 2 }));
}
public void FloatArraysCanBeFilledUpWithDefaultValues() {
_element.SetAttribute("attrib", "1 2");
var defaultValue = new float[] { 5, 6, 7, 8 };
Assert.That(_element.GetFloatArrayAttribute("attrib", defaultValue: defaultValue),
Is.EqualTo(new float[] { 1, 2, 7, 8 }));
}
public void FloatArrayDefaultFillingCanBeSwitchedOff() {
_element.SetAttribute("attrib", "1 2");
var defaultValue = new float[] { 5, 6, 7, 8 };
var result = _element.GetFloatArrayAttribute("attrib", defaultValue: defaultValue,
fillMissingValues: false);
Assert.That(result, Is.EqualTo(new float[] { 1, 2 }));
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: c5148652c03104473b634d0ec28103d3
timeCreated: 1538662593
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: