From 02f300c76257221d54be61eda4aaf38aca0721eb Mon Sep 17 00:00:00 2001 From: Tom Erez Date: Sun, 29 May 2022 00:23:57 -0700 Subject: [PATCH] Refactor: separate buffer indexing and swizzling. This also fixes a sensor data reading bug: previously, _sensorIndex was multiplied by 3 by MjEngineTool. PiperOrigin-RevId: 451699887 Change-Id: I864bcbf8159857764854b25311a26ec5672125d9 --- unity/Editor/Components/MjMouseSpring.cs | 3 +- unity/Runtime/Components/Bodies/MjBody.cs | 6 +- .../Runtime/Components/Bodies/MjMocapBody.cs | 6 +- .../Sensors/MjBodyQuaternionSensor.cs | 2 +- .../Components/Sensors/MjBodyVectorSensor.cs | 2 +- .../Sensors/MjGeomQuaternionSensor.cs | 2 +- .../Components/Sensors/MjGeomVectorSensor.cs | 2 +- .../Sensors/MjSiteQuaternionSensor.cs | 2 +- .../Components/Sensors/MjSiteVectorSensor.cs | 3 +- unity/Runtime/Components/Shapes/MjGeom.cs | 18 ++- unity/Runtime/Components/Shapes/MjSite.cs | 6 +- .../Components/Tendons/MjSpatialTendon.cs | 6 +- unity/Runtime/Tools/MjEngineTool.cs | 76 +++++------ unity/Tests/Editor/Tools/MjEngineTests.cs | 31 ++--- unity/Tests/Runtime/MjSensorPlayTests.cs | 123 ++++++++++++++++++ unity/Tests/Runtime/MjSensorPlayTests.cs.meta | 11 ++ 16 files changed, 213 insertions(+), 86 deletions(-) create mode 100644 unity/Tests/Runtime/MjSensorPlayTests.cs create mode 100644 unity/Tests/Runtime/MjSensorPlayTests.cs.meta diff --git a/unity/Editor/Components/MjMouseSpring.cs b/unity/Editor/Components/MjMouseSpring.cs index e6ee279f..b2264727 100644 --- a/unity/Editor/Components/MjMouseSpring.cs +++ b/unity/Editor/Components/MjMouseSpring.cs @@ -159,7 +159,8 @@ namespace Mujoco { 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); + bodyVel = MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(res, 1)); } float springStiffness = 100; diff --git a/unity/Runtime/Components/Bodies/MjBody.cs b/unity/Runtime/Components/Bodies/MjBody.cs index acde7e11..8d6a8cd3 100644 --- a/unity/Runtime/Components/Bodies/MjBody.cs +++ b/unity/Runtime/Components/Bodies/MjBody.cs @@ -35,8 +35,10 @@ namespace Mujoco { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - transform.position = MjEngineTool.UnityVector3(data->xpos, MujocoId); - transform.rotation = MjEngineTool.UnityQuaternion(data->xquat, MujocoId); + transform.position = MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(data->xpos, MujocoId)); + transform.rotation = MjEngineTool.UnityQuaternion( + MjEngineTool.MjQuaternionAtEntry(data->xquat, MujocoId)); } } } diff --git a/unity/Runtime/Components/Bodies/MjMocapBody.cs b/unity/Runtime/Components/Bodies/MjMocapBody.cs index 4977a1d6..09ac2966 100644 --- a/unity/Runtime/Components/Bodies/MjMocapBody.cs +++ b/unity/Runtime/Components/Bodies/MjMocapBody.cs @@ -35,8 +35,10 @@ namespace Mujoco { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - MjEngineTool.SetMjVector3(data->mocap_pos, transform.position, MujocoId); - MjEngineTool.SetMjQuaternion(data->mocap_quat, transform.rotation, MujocoId); + MjEngineTool.SetMjVector3( + MjEngineTool.MjVector3AtEntry(data->mocap_pos, MujocoId), transform.position); + MjEngineTool.SetMjQuaternion( + MjEngineTool.MjQuaternionAtEntry(data->mocap_quat, MujocoId), transform.rotation); } } } diff --git a/unity/Runtime/Components/Sensors/MjBodyQuaternionSensor.cs b/unity/Runtime/Components/Sensors/MjBodyQuaternionSensor.cs index 4cf32d27..23b25889 100644 --- a/unity/Runtime/Components/Sensors/MjBodyQuaternionSensor.cs +++ b/unity/Runtime/Components/Sensors/MjBodyQuaternionSensor.cs @@ -46,7 +46,7 @@ public class MjBodyQuaternionSensor : MjBaseSensor { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - SensorReading = MjEngineTool.UnityQuaternion(data->sensordata, _sensorAddress); + SensorReading = MjEngineTool.UnityQuaternion(data->sensordata + _sensorAddress); } } } diff --git a/unity/Runtime/Components/Sensors/MjBodyVectorSensor.cs b/unity/Runtime/Components/Sensors/MjBodyVectorSensor.cs index dd0cc1f4..7606a880 100644 --- a/unity/Runtime/Components/Sensors/MjBodyVectorSensor.cs +++ b/unity/Runtime/Components/Sensors/MjBodyVectorSensor.cs @@ -71,7 +71,7 @@ public class MjBodyVectorSensor : MjBaseSensor { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - SensorReading = MjEngineTool.UnityVector3(data->sensordata, _sensorAddress); + SensorReading = MjEngineTool.UnityVector3(data->sensordata + _sensorAddress); } } } diff --git a/unity/Runtime/Components/Sensors/MjGeomQuaternionSensor.cs b/unity/Runtime/Components/Sensors/MjGeomQuaternionSensor.cs index 26039420..4473c3b5 100644 --- a/unity/Runtime/Components/Sensors/MjGeomQuaternionSensor.cs +++ b/unity/Runtime/Components/Sensors/MjGeomQuaternionSensor.cs @@ -41,7 +41,7 @@ public class MjGeomQuaternionSensor : MjBaseSensor { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - SensorReading = MjEngineTool.UnityQuaternion(data->sensordata, _sensorAddress); + SensorReading = MjEngineTool.UnityQuaternion(data->sensordata + _sensorAddress); } } } diff --git a/unity/Runtime/Components/Sensors/MjGeomVectorSensor.cs b/unity/Runtime/Components/Sensors/MjGeomVectorSensor.cs index 4e983215..abfd30c8 100644 --- a/unity/Runtime/Components/Sensors/MjGeomVectorSensor.cs +++ b/unity/Runtime/Components/Sensors/MjGeomVectorSensor.cs @@ -57,7 +57,7 @@ public class MjGeomVectorSensor : MjBaseSensor { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - SensorReading = MjEngineTool.UnityVector3(data->sensordata, _sensorAddress); + SensorReading = MjEngineTool.UnityVector3(data->sensordata + _sensorAddress); } } } diff --git a/unity/Runtime/Components/Sensors/MjSiteQuaternionSensor.cs b/unity/Runtime/Components/Sensors/MjSiteQuaternionSensor.cs index 325874df..05f36412 100644 --- a/unity/Runtime/Components/Sensors/MjSiteQuaternionSensor.cs +++ b/unity/Runtime/Components/Sensors/MjSiteQuaternionSensor.cs @@ -43,7 +43,7 @@ public class MjSiteQuaternionSensor : MjBaseSensor { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - SensorReading = MjEngineTool.UnityQuaternion(data->sensordata, _sensorAddress); + SensorReading = MjEngineTool.UnityQuaternion(data->sensordata + _sensorAddress); } } } diff --git a/unity/Runtime/Components/Sensors/MjSiteVectorSensor.cs b/unity/Runtime/Components/Sensors/MjSiteVectorSensor.cs index 758213fe..49c01a5f 100644 --- a/unity/Runtime/Components/Sensors/MjSiteVectorSensor.cs +++ b/unity/Runtime/Components/Sensors/MjSiteVectorSensor.cs @@ -69,8 +69,7 @@ public class MjSiteVectorSensor : MjBaseSensor { } public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - Vector3 mjReading = new Vector3((float)data->sensordata[_sensorAddress], (float)data->sensordata[_sensorAddress + 1], (float)data->sensordata[_sensorAddress + 2]); - SensorReading = MjEngineTool.UnityVector3(mjReading); + SensorReading = MjEngineTool.UnityVector3(data->sensordata + _sensorAddress); } } } diff --git a/unity/Runtime/Components/Shapes/MjGeom.cs b/unity/Runtime/Components/Shapes/MjGeom.cs index 6e67ee64..3787db6f 100644 --- a/unity/Runtime/Components/Shapes/MjGeom.cs +++ b/unity/Runtime/Components/Shapes/MjGeom.cs @@ -48,8 +48,10 @@ public class MjGeom : MjShapeComponent { var MjParent = MjHierarchyTool.FindParentComponent(this); if (MjParent != null) { var comInParentFrame = new MjTransformation( - translation: MjEngineTool.UnityVector3(model->geom_pos, MujocoId), - rotation: MjEngineTool.UnityQuaternion(model->geom_quat, MujocoId)); + translation: MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(model->geom_pos, MujocoId)), + rotation: MjEngineTool.UnityQuaternion( + MjEngineTool.MjQuaternionAtEntry(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 @@ -78,13 +80,17 @@ public class MjGeom : MjShapeComponent { 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)); + translation: MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(data->geom_xpos, MujocoId)), + rotation: MjEngineTool.UnityQuaternionFromMatrix( + MjEngineTool.MjMatrixAtEntry(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); + transform.position = MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(data->geom_xpos, MujocoId)); + transform.rotation = MjEngineTool.UnityQuaternionFromMatrix( + MjEngineTool.MjMatrixAtEntry(data->geom_xmat, MujocoId)); } } diff --git a/unity/Runtime/Components/Shapes/MjSite.cs b/unity/Runtime/Components/Shapes/MjSite.cs index 2608bd27..040fb9ac 100644 --- a/unity/Runtime/Components/Shapes/MjSite.cs +++ b/unity/Runtime/Components/Shapes/MjSite.cs @@ -40,8 +40,10 @@ public class MjSite : MjShapeComponent { // Synchronize the state of the component. public override unsafe void OnSyncState(MujocoLib.mjData_* data) { - transform.position = MjEngineTool.UnityVector3(data->site_xpos, MujocoId); - transform.rotation = MjEngineTool.UnityQuaternionFromMatrix(data->site_xmat, MujocoId); + transform.position = MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(data->site_xpos, MujocoId)); + transform.rotation = MjEngineTool.UnityQuaternionFromMatrix( + MjEngineTool.MjMatrixAtEntry(data->site_xmat, MujocoId)); } public void OnDrawGizmosSelected() { diff --git a/unity/Runtime/Components/Tendons/MjSpatialTendon.cs b/unity/Runtime/Components/Tendons/MjSpatialTendon.cs index c95bb7b2..73e5fa17 100644 --- a/unity/Runtime/Components/Tendons/MjSpatialTendon.cs +++ b/unity/Runtime/Components/Tendons/MjSpatialTendon.cs @@ -115,8 +115,10 @@ public class MjSpatialTendon : MjBaseTendon { if (data->wrap_obj[readHead] == -2 || data->wrap_obj[readHead + 1] == -2) { readHead += 1; } else { - Vector3 start = MjEngineTool.UnityVector3(data->wrap_xpos, readHead); - Vector3 end = MjEngineTool.UnityVector3(data->wrap_xpos, readHead + 1); + Vector3 start = MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(data->wrap_xpos, readHead)); + Vector3 end = MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(data->wrap_xpos, readHead + 1)); Gizmos.DrawLine(start, end); if (data->wrap_obj[readHead + 1] >= 0) { // a wrap geom readHead += 2; diff --git a/unity/Runtime/Tools/MjEngineTool.cs b/unity/Runtime/Tools/MjEngineTool.cs index 01e42538..159d09f5 100644 --- a/unity/Runtime/Tools/MjEngineTool.cs +++ b/unity/Runtime/Tools/MjEngineTool.cs @@ -29,7 +29,6 @@ public static class MjEngineTool { private const int _elementsPerTransform = 7; private static double[] _mjQuat = new double[4]; private static double[] _mjMat = new double[9]; - private const int _MjSuccess = 1; public static string Sanitize(string name) { return name.Replace('/', '_'); @@ -71,25 +70,30 @@ public static class MjEngineTool { } // Stores a unity vector in a Mujoco target buffer. - // The vector coordinates will be stored at the 'entryIndex * 3' of the 'mjTarget' array. - public static unsafe void SetMjVector3(double* mjTarget, Vector3 unityVec, int entryIndex) { - var startOffset = entryIndex * _elementsPerPosition; + public static unsafe void SetMjVector3(double* mjTarget, Vector3 unityVec) { var mjVec = MjVector3(unityVec); - mjTarget[startOffset] = mjVec[0]; - mjTarget[startOffset + 1] = mjVec[1]; - mjTarget[startOffset + 2] = mjVec[2]; + mjTarget[0] = mjVec[0]; + mjTarget[1] = mjVec[1]; + mjTarget[2] = mjVec[2]; } // Stores a unity quaternion in a Mujoco target buffer. - // The vector coordinates will be stored at the 'entryIndex * 4' of the 'mjTarget' array. - public static unsafe void SetMjQuaternion( - double* mjTarget, Quaternion unityQuat, int entryIndex) { - var startOffset = entryIndex * _elementsPerRotation; + public static unsafe void SetMjQuaternion(double* mjTarget, Quaternion unityQuat) { var mjQuat = MjQuaternion(unityQuat); - mjTarget[startOffset] = mjQuat.w; - mjTarget[startOffset + 1] = mjQuat.x; - mjTarget[startOffset + 2] = mjQuat.y; - mjTarget[startOffset + 3] = mjQuat.z; + mjTarget[0] = mjQuat.w; + mjTarget[1] = mjQuat.x; + mjTarget[2] = mjQuat.y; + mjTarget[3] = mjQuat.z; + } + + // Returns a pointer to an entry in the MuJoCo field that's 3*offsetEntry down the buffer. + public static unsafe double* MjVector3AtEntry(double* mjTarget, int offsetEntry) { + return mjTarget + offsetEntry * _elementsPerPosition; + } + + // Returns a pointer to an entry in the MuJoCo field that's 4*offsetEntry down the buffer. + public static unsafe double* MjQuaternionAtEntry(double* mjTarget, int offsetEntry) { + return mjTarget + offsetEntry * _elementsPerRotation; } // Returns a pointer to an entry in the MuJoCo field that's 7*offsetEntry down the buffer. @@ -97,6 +101,11 @@ public static class MjEngineTool { return mjTarget + offsetEntry * _elementsPerTransform; } + // Returns a pointer to an entry in the MuJoCo field that's 9*offsetEntry down the buffer. + public static unsafe double* MjMatrixAtEntry(double* mjTarget, int offsetEntry) { + return mjTarget + offsetEntry * _mjMat.Length; + } + // Stores a unity transform (position+rotation) in a Mujoco target buffer. public static unsafe void SetMjTransform( double* mjTarget, Vector3 unityVec, Quaternion unityQuat) { @@ -117,10 +126,8 @@ public static class MjEngineTool { } // Converts a Mujoco vector to a Unity vector. - public static unsafe Vector3 UnityVector3(double* coords, int entryIndex) { - var startOffset = entryIndex * _elementsPerPosition; - return new Vector3( - (float)coords[startOffset], (float)coords[startOffset + 2], (float)coords[startOffset + 1]); + public static unsafe Vector3 UnityVector3(double* mjVector) { + return new Vector3((float)mjVector[0], (float)mjVector[2], (float)mjVector[1]); } // Converts a Mujoco vector to a Unity vector. @@ -142,21 +149,9 @@ public static class MjEngineTool { } // Converts a Mujoco quaternion to a Unity quaternion. - // The quaternions is read starting at 'entryIndex * 4' of the 'coords' array. - public static Quaternion UnityQuaternion(double[] coords, int entryIndex) { - var startOffset = entryIndex * _elementsPerRotation; + public static unsafe Quaternion UnityQuaternion(double* mjQuat) { return new Quaternion( - x:(float)coords[startOffset + 1], y:(float)coords[startOffset + 3], - z:(float)coords[startOffset + 2], w:(float)-coords[startOffset]); - } - - // Converts a Mujoco quaternion to a Unity quaternion. - // The quaternion is read starting at 'entryIndex * 4' of the 'coords' array. - public static unsafe Quaternion UnityQuaternion(double* coords, int entryIndex) { - var startOffset = entryIndex * _elementsPerRotation; - return new Quaternion( - x:(float)coords[startOffset + 1], y:(float)coords[startOffset + 3], - z:(float)coords[startOffset + 2], w:(float)-coords[startOffset]); + x:(float)mjQuat[1], y:(float)mjQuat[3], z:(float)mjQuat[2], w:(float)-mjQuat[0]); } // Converts a Mujoco quaternion to a Unity quaternion. @@ -166,17 +161,16 @@ public static class MjEngineTool { } // Converts a Mujoco matrix to a Unity quaternion. - // The matrix coordinates are at the 'entryIndex * 9' of the 'coords' array. - public static unsafe Quaternion UnityQuaternionFromMatrix(double* coords, int entryIndex) { - var startOffset = entryIndex * _mjMat.Length; + // The matrix coordinates are at the 'entryIndex * 9' of the 'mjMat' array. + public static unsafe Quaternion UnityQuaternionFromMatrix(double* mjMat) { for (var j = 0; j < _mjMat.Length; ++j) { - _mjMat[j] = coords[startOffset + j]; + _mjMat[j] = mjMat[j]; } - fixed (double* a = _mjQuat) - fixed (double* b = _mjMat) { - MujocoLib.mju_mat2Quat(a, b); + fixed (double* q_out = _mjQuat) + fixed (double* m_in = _mjMat) { + MujocoLib.mju_mat2Quat(q_out, m_in); + return UnityQuaternion(q_out); } - return UnityQuaternion(_mjQuat, 0); } // Converts a Unity extents Vector3 to a Mujoco extents vector. diff --git a/unity/Tests/Editor/Tools/MjEngineTests.cs b/unity/Tests/Editor/Tools/MjEngineTests.cs index ec8e6511..48e052b2 100644 --- a/unity/Tests/Editor/Tools/MjEngineTests.cs +++ b/unity/Tests/Editor/Tools/MjEngineTests.cs @@ -47,7 +47,8 @@ namespace Mujoco { 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); + var recreatedVec = MjEngineTool.UnityVector3( + MjEngineTool.MjVector3AtEntry(MjArrPtr, 1)); Assert.That(recreatedVec, Is.EqualTo(vec)); } } @@ -64,23 +65,6 @@ namespace Mujoco { 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)] @@ -94,7 +78,8 @@ namespace Mujoco { 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 recreatedQuat = MjEngineTool.UnityQuaternion( + MjEngineTool.MjQuaternionAtEntry(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)); @@ -138,8 +123,8 @@ namespace Mujoco { 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); + MjEngineTool.SetMjQuaternion(unsafeBuffer, quat); + result = MjEngineTool.UnityQuaternion(unsafeBuffer); } Assert.That(quat, Is.EqualTo(result)); } @@ -154,8 +139,8 @@ namespace Mujoco { 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); + MjEngineTool.SetMjVector3(unsafeBuffer, vec); + result = MjEngineTool.UnityVector3(unsafeBuffer); } Assert.That(vec, Is.EqualTo(result)); } diff --git a/unity/Tests/Runtime/MjSensorPlayTests.cs b/unity/Tests/Runtime/MjSensorPlayTests.cs new file mode 100644 index 00000000..fae6dade --- /dev/null +++ b/unity/Tests/Runtime/MjSensorPlayTests.cs @@ -0,0 +1,123 @@ +// 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. + +#if UNITY_EDITOR + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Xml; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.TestTools.Utils; + +namespace Mujoco { + +[TestFixture] +public class MjSensorPlayTests { + private MjBody _body; + private MjSite _site; + private MjGeom _geom; + private MjHingeJoint _joint; + private MjSiteQuaternionSensor _siteQuatSensor0; + private MjSiteQuaternionSensor _siteQuatSensor; + private MjSiteVectorSensor _siteVectorSensor; + private MjGeomQuaternionSensor _geomQuatSensor; + private MjGeomVectorSensor _geomVectorSensor; + private MjBodyQuaternionSensor _bodyQuatSensor; + private MjBodyVectorSensor _bodyVectorSensor; + private MjScene _scene; + + [SetUp] + public void SetUp() { + _scene = MjScene.Instance; + _body = new GameObject("body").AddComponent(); + _site = new GameObject("site").AddComponent(); + _geom = new GameObject("geom").AddComponent(); + _siteQuatSensor0 = new GameObject("sq0").AddComponent(); + _siteQuatSensor0.Site = _site; + _siteQuatSensor = new GameObject("sq").AddComponent(); + _siteQuatSensor.Site = _site; + _siteVectorSensor = new GameObject("sv").AddComponent(); + _siteVectorSensor.Site = _site; + _siteVectorSensor.SensorType = MjSiteVectorSensor.AvailableSensors.FramePos; + _geomQuatSensor = new GameObject("gq").AddComponent(); + _geomQuatSensor.Geom = _geom; + _geomVectorSensor = new GameObject("gv").AddComponent(); + _geomVectorSensor.Geom = _geom; + _geomVectorSensor.SensorType = MjGeomVectorSensor.AvailableSensors.FramePos; + _bodyQuatSensor = new GameObject("bq").AddComponent(); + _bodyQuatSensor.Body = _body; + _bodyVectorSensor = new GameObject("bv").AddComponent(); + _bodyVectorSensor.Body = _body; + _bodyVectorSensor.SensorType = MjBodyVectorSensor.AvailableSensors.FramePos; + _site.transform.position = new Vector3(1, 2, 3); + _site.transform.rotation = new Quaternion(0, 1, 2, 3); + _geom.transform.position = new Vector3(4, 5, 6); + _geom.transform.rotation = new Quaternion(4, 5, 6, 7); + _body.transform.position = new Vector3(7, 8, 9); + _body.transform.rotation = new Quaternion(7, 8, 9, 9); + _scene.CreateScene(); // compilation happens here + } + + [TearDown] + public void TearDown() { + GameObject.DestroyImmediate(_site.gameObject); + GameObject.DestroyImmediate(_body.gameObject); + GameObject.DestroyImmediate(_geom.gameObject); + GameObject.DestroyImmediate(_siteQuatSensor0.gameObject); + GameObject.DestroyImmediate(_siteQuatSensor.gameObject); + GameObject.DestroyImmediate(_siteVectorSensor.gameObject); + GameObject.DestroyImmediate(_geomQuatSensor.gameObject); + GameObject.DestroyImmediate(_geomVectorSensor.gameObject); + GameObject.DestroyImmediate(_bodyQuatSensor.gameObject); + GameObject.DestroyImmediate(_bodyVectorSensor.gameObject); + GameObject.DestroyImmediate(_scene.gameObject); + GameObject.DestroyImmediate(MjScene.Instance); + } + + [UnityTest] + public IEnumerator CheckSensorData() { + yield return new WaitForFixedUpdate(); // updating the transform + yield return new WaitForFixedUpdate(); // updating the transform + var vecComparer = new Vector3EqualityComparer(1e-4f); + var quatComparer = new Vector4EqualityComparer(1e-4f); + Assert.That( + _siteVectorSensor.SensorReading, + Is.EqualTo(_site.transform.position).Using(vecComparer)); + Assert.That( + _geomVectorSensor.SensorReading, + Is.EqualTo(_geom.transform.position).Using(vecComparer)); + Assert.That( + _bodyVectorSensor.SensorReading, + Is.EqualTo(_body.transform.position).Using(vecComparer)); + + Assert.That( + _siteQuatSensor0.SensorReading, + Is.EqualTo(_site.transform.rotation.normalized).Using(quatComparer)); + Assert.That( + _siteQuatSensor.SensorReading, + Is.EqualTo(_site.transform.rotation.normalized).Using(quatComparer)); + Assert.That( + _geomQuatSensor.SensorReading, + Is.EqualTo(_geom.transform.rotation.normalized).Using(quatComparer)); + Assert.That( + _bodyQuatSensor.SensorReading, + Is.EqualTo(_body.transform.rotation.normalized).Using(quatComparer)); + } +} +} + +#endif diff --git a/unity/Tests/Runtime/MjSensorPlayTests.cs.meta b/unity/Tests/Runtime/MjSensorPlayTests.cs.meta new file mode 100644 index 00000000..2d78a1e9 --- /dev/null +++ b/unity/Tests/Runtime/MjSensorPlayTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62bfed53c131e77cfa2845ad38c35a4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: