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,185 @@
// 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 UnityEditor;
using UnityEngine;
using UnityEngine.TestTools;
namespace Mujoco {
[TestFixture]
public class MjMaterialImportTests {
private GameObject _sceneRoot;
private bool _removeLocal = false;
private bool _removeLocalMujoco = false;
private MjImporterWithAssets _importer;
[SetUp]
public void SetUp() {
_importer = new MjImporterWithAssets();
LogAssert.ignoreFailingMessages = true;
// these two directories may exist from previous imports
string[] assetsRoot = {"Assets"};
var guids = AssetDatabase.FindAssets("Local", assetsRoot);
if (guids.Length == 0) {
_removeLocal = true;
AssetDatabase.CreateFolder("Assets", "Local");
}
string[] assetsLocal = {"Assets/Local"};
guids = AssetDatabase.FindAssets("MjImports", assetsLocal);
if (guids.Length == 0) {
_removeLocalMujoco = true;
}
}
[TearDown]
public void TearDown() {
LogAssert.ignoreFailingMessages = false;
if (_sceneRoot != null) {
UnityEngine.Object.DestroyImmediate(_sceneRoot);
}
// remove main resources folder:
// string[] mainTestResources = {"Assets/Local/Mujoco/MjMaterialImportTests/Resources"};
// foreach (var asset in AssetDatabase.FindAssets("", mainTestResources)) {
// AssetDatabase.DeleteAsset(AssetDatabase.GUIDToAssetPath(asset));
// }
// // remove main resources folder:
// AssetDatabase.DeleteAsset(mainTestResources[0]);
// remove all test folders:
string[] assetLocalMujoco = {"Assets/Local/MjImports"};
foreach (var asset in AssetDatabase.FindAssets("MjMaterialImportTests", assetLocalMujoco)) {
AssetDatabase.DeleteAsset(AssetDatabase.GUIDToAssetPath(asset));
}
if (_removeLocalMujoco) {
AssetDatabase.DeleteAsset("Assets/Local/MjImports");
}
if (_removeLocal) {
AssetDatabase.DeleteAsset("Assets/Local");
}
}
[Test]
public void ParsingMaterialProperties() {
var mjcfString = @"<mujoco>
<asset>
<material name='test_material'
rgba='.7 .5 .3 .1' specular='.5' shininess='.7' reflectance='.2'/>
</asset>
<worldbody>
<body>
<geom name='test_geom' material='test_material' size='1 1 1'/>
</body>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: "MjMaterialImportTests", mjcfString: mjcfString);
var geom = _sceneRoot.GetComponentInChildren<MjGeom>();
Assert.That(geom, Is.Not.Null);
var meshRenderer = geom.GetComponent<MeshRenderer>();
Assert.That(meshRenderer, Is.Not.Null);
Assert.That(meshRenderer.sharedMaterial, Is.Not.Null);
Assert.That(meshRenderer.sharedMaterial.color, Is.EqualTo(new Color(0.7f, 0.5f, 0.3f, 0.1f)));
Assert.That(meshRenderer.sharedMaterial.GetFloat("_Metallic"), Is.EqualTo(0.2f));
Assert.That(meshRenderer.sharedMaterial.GetFloat("_Glossiness"), Is.GreaterThan(0.0f));
}
[Test]
public void ParsingMaterialWithoutEmissivity() {
var mjcfString = @"<mujoco>
<asset>
<material name='test_material' rgba='.7 .5 .3 .1'/>
</asset>
<worldbody>
<body>
<geom name='test_geom' material='test_material' size='1 1 1'/>
</body>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: "MjMaterialImportTests", mjcfString: mjcfString);
var geom = _sceneRoot.GetComponentInChildren<MjGeom>();
Assert.That(geom, Is.Not.Null);
var meshRenderer = geom.GetComponent<MeshRenderer>();
Assert.That(meshRenderer, Is.Not.Null);
Assert.That(meshRenderer.sharedMaterial, Is.Not.Null);
Assert.That(meshRenderer.sharedMaterial.shaderKeywords, Has.No.Member("_EMISSION"));
}
[Test]
public void ParsingMaterialWithEmissivity() {
var mjcfString = @"<mujoco>
<asset>
<material name='test_material' rgba='.7 .5 .3 .1' emission='1'/>
</asset>
<worldbody>
<body>
<geom name='test_geom' material='test_material' size='1 1 1'/>
</body>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: "MjMaterialImportTests", mjcfString: mjcfString);
var geom = _sceneRoot.GetComponentInChildren<MjGeom>();
Assert.That(geom, Is.Not.Null);
var meshRenderer = geom.GetComponent<MeshRenderer>();
Assert.That(meshRenderer, Is.Not.Null);
Assert.That(meshRenderer.sharedMaterial, Is.Not.Null);
Assert.That(meshRenderer.sharedMaterial.shaderKeywords, Has.Member("_EMISSION"));
Assert.That(
meshRenderer.sharedMaterial.GetColor("_EmissionColor"),
Is.EqualTo(new Color(0.7f, 0.5f, 0.3f, 0.1f)));
}
[Test]
public void ResettingColorDatabaseForEachImport() {
var mjcfString1 = @"<mujoco>
<asset>
<material name='test_material' rgba='.2 .2 .2 .2'/>
</asset>
<worldbody>
<body>
<geom name='test_geom' material='test_material' size='1 1 1'/>
</body>
</worldbody>
</mujoco>";
var mjcfString2 = @"<mujoco>
<asset>
<material name='test_material' rgba='.5 .5 .5 .5'/>
</asset>
<worldbody>
<body>
<geom name='test_geom' material='test_material' size='1 1 1'/>
</body>
</worldbody>
</mujoco>";
_sceneRoot = new GameObject();
var importedMesh1 = _importer.ImportString(
name: "MjMaterialImportTests"+$"{UnityEngine.Random.Range(0,999)}", mjcfString: mjcfString1);
importedMesh1.transform.parent = _sceneRoot.transform;
var importedMesh2 = _importer.ImportString(
name: "MjMaterialImportTests"+$"{UnityEngine.Random.Range(0,999)}", mjcfString: mjcfString2);
importedMesh2.transform.parent = _sceneRoot.transform;
var geom = importedMesh2.GetComponentInChildren<MjGeom>();
var meshRenderer = geom.GetComponent<MeshRenderer>();
Assert.That(meshRenderer.sharedMaterial.color, Is.EqualTo(new Color(0.5f, 0.5f, 0.5f, 0.5f)));
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 3f8cfe95d25bf4da0b31bcb772f19c22
timeCreated: 1538645397
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,97 @@
// 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;
using UnityEngine.TestTools;
namespace Mujoco {
[TestFixture]
public class MjStlMeshParserTests {
private Mesh _originalMesh;
[SetUp]
public void SetUp() {
_originalMesh = new Mesh();
_originalMesh.vertices = new Vector3[] {
new Vector3(0, 0, 2), new Vector3(1, 0, 2), new Vector3(0, 1, 2)
};
_originalMesh.triangles = new int[] {0, 1, 2};
_originalMesh.normals = new Vector3[] {
new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, 1)
};
}
[Test]
public void RoundrobinMeshParsing() {
var serializedMesh = StlMeshParser.SerializeBinary(_originalMesh);
var restoredMesh = StlMeshParser.ParseBinary(serializedMesh, Vector3.one);
Assert.That(restoredMesh.vertices, Has.Length.EqualTo(_originalMesh.vertices.Length));
Assert.That(restoredMesh.normals, Has.Length.EqualTo(_originalMesh.normals.Length));
Assert.That(restoredMesh.triangles, Has.Length.EqualTo(_originalMesh.triangles.Length));
}
[Test]
public void VertexPositionsSerialization() {
var serializedMesh = StlMeshParser.SerializeBinary(_originalMesh);
var restoredMesh = StlMeshParser.ParseBinary(serializedMesh, Vector3.one);
Assert.That(
restoredMesh.vertices[restoredMesh.triangles[0]],
Is.EqualTo(_originalMesh.vertices[_originalMesh.triangles[0]]));
Assert.That(
restoredMesh.vertices[restoredMesh.triangles[1]],
Is.EqualTo(_originalMesh.vertices[_originalMesh.triangles[1]]));
Assert.That(
restoredMesh.vertices[restoredMesh.triangles[2]],
Is.EqualTo(_originalMesh.vertices[_originalMesh.triangles[2]]));
}
[Test]
public void ProcessDuplicatesSharedVertices() {
_originalMesh.triangles = new int[] {0, 0, 2};
var serializedMesh = StlMeshParser.SerializeBinary(_originalMesh);
var restoredMesh = StlMeshParser.ParseBinary(serializedMesh, Vector3.one);
Assert.That(
restoredMesh.vertices[restoredMesh.triangles[0]],
Is.EqualTo(_originalMesh.vertices[_originalMesh.triangles[0]]));
Assert.That(
restoredMesh.vertices[restoredMesh.triangles[1]],
Is.EqualTo(_originalMesh.vertices[_originalMesh.triangles[1]]));
Assert.That(
restoredMesh.vertices[restoredMesh.triangles[2]],
Is.EqualTo(_originalMesh.vertices[_originalMesh.triangles[2]]));
}
[Test]
public void VertexNormalsAreRcalculated() {
_originalMesh.normals = new Vector3[] {
new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)
};
var serializedMesh = StlMeshParser.SerializeBinary(_originalMesh);
var restoredMesh = StlMeshParser.ParseBinary(serializedMesh, Vector3.one);
Assert.That(restoredMesh.normals[0], Is.EqualTo(Vector3.forward));
Assert.That(restoredMesh.normals[1], Is.EqualTo(Vector3.forward));
Assert.That(restoredMesh.normals[2], Is.EqualTo(Vector3.forward));
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 95962f380898244f09d871fd35e28c0b
timeCreated: 1561551392
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,256 @@
// 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;
using UnityEngine.TestTools;
namespace Mujoco {
[TestFixture]
public class MjDefaultSelectorsEditorTests {
private XmlElement SetUp(string mjcfString, string selector) {
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
return mjcf.SelectSingleNode(selector) as XmlElement;
}
[TestCase("class")]
[TestCase("childclass")]
public void SelectingTopLevelClassDefault(string qualifier) {
var mjcfString = @"<mujoco>
<worldbody>
<modified QUALIFIER=""class""/>
</worldbody>
</mujoco>".Replace("QUALIFIER", qualifier);
var modifiedNode = SetUp(mjcfString, "/mujoco/worldbody/modified");
var classes = MjXmlModifiers.GetApplicableDefaultClasses(modifiedNode).ToArray();
Assert.That(classes.Length, Is.EqualTo(1));
Assert.That(
classes[0], Is.EqualTo("class"));
}
[TestCase("class")]
[TestCase("childclass")]
public void SelectingNestedClassDefaultWithInheritance(string qualifier) {
var mjcfString = @"<mujoco>
<worldbody>
<some_node childclass=""parent"">
<modified QUALIFIER=""child""/>
</some_node>
</worldbody>
</mujoco>".Replace("QUALIFIER", qualifier);
var modifiedNode = SetUp(mjcfString, "/mujoco/worldbody/some_node/modified");
var classes = MjXmlModifiers.GetApplicableDefaultClasses(modifiedNode).ToArray();
Assert.That(classes.Length, Is.EqualTo(2));
Assert.That(classes[0], Is.EqualTo("child"));
Assert.That(
classes[1], Is.EqualTo("parent"));
}
[TestCase("class")]
[TestCase("childclass")]
public void SelectingNestedClassDefaultWithoutInheritance(string qualifier) {
var mjcfString = @"<mujoco>
<worldbody>
<some_node class=""parent"">
<modified QUALIFIER=""child""/>
</some_node>
</worldbody>
</mujoco>".Replace("QUALIFIER", qualifier);
var modifiedNode = SetUp(mjcfString, "/mujoco/worldbody/some_node/modified");
var classes = MjXmlModifiers.GetApplicableDefaultClasses(modifiedNode).ToArray();
Assert.That(classes.Length, Is.EqualTo(1));
Assert.That(classes[0], Is.EqualTo("child"));
}
}
[TestFixture]
public class MjAttributeCopierEditorTests {
[Test]
public void CopyingXmlElementAttributes() {
var mjcfString = @"<root>
<tag_1 attr_1=""a"" attr_2=""b""/>
<tag_2/>
</root>";
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
var tags = new XmlElement[] {
mjcf.SelectSingleNode("/root/tag_1") as XmlElement,
mjcf.SelectSingleNode("/root/tag_2") as XmlElement
};
MjXmlModifiers.CopyAttributes(tags[0], tags[1]);
// Reselect the element to make sure we're testing the actual part of the DOM, and not its copy.
var testedElement = mjcf.SelectSingleNode("/root/tag_2") as XmlElement;
Assert.That(testedElement.GetStringAttribute("attr_1", "unknown"), Is.EqualTo("a"));
Assert.That(testedElement.GetStringAttribute("attr_2", "unknown"), Is.EqualTo("b"));
}
[Test]
public void CopyingXmlElementAttributesWithoutOverwritingTheExistingOnes() {
var mjcfString = @"<root>
<tag_1 attr_1=""a"" attr_2=""b""/>
<tag_2 attr_1=""c""/>
</root>";
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
var tags = new XmlElement[] {
mjcf.SelectSingleNode("/root/tag_1") as XmlElement,
mjcf.SelectSingleNode("/root/tag_2") as XmlElement
};
MjXmlModifiers.CopyAttributes(tags[0], tags[1]);
// Reselect the element to make sure we're testing the actual part of the DOM, and not its copy.
var testedElement = mjcf.SelectSingleNode("/root/tag_2") as XmlElement;
Assert.That(testedElement.GetStringAttribute("attr_1", "unknown"), Is.EqualTo("c"));
Assert.That(testedElement.GetStringAttribute("attr_2", "unknown"), Is.EqualTo("b"));
}
[Test]
public void CopyingXmlElementAttributesWithOverwritingTheExistingOnes() {
var mjcfString = @"<root>
<tag_1 attr_1=""a"" attr_2=""b""/>
<tag_2 attr_1=""c""/>
</root>";
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
var tags = new XmlElement[] {
mjcf.SelectSingleNode("/root/tag_1") as XmlElement,
mjcf.SelectSingleNode("/root/tag_2") as XmlElement
};
MjXmlModifiers.CopyAttributesOverwriteExisting(tags[0], tags[1]);
// Reselect the element to make sure we're testing the actual part of the DOM, and not its copy.
var testedElement = mjcf.SelectSingleNode("/root/tag_2") as XmlElement;
Assert.That(testedElement.GetStringAttribute("attr_1", "unknown"), Is.EqualTo("a"));
Assert.That(testedElement.GetStringAttribute("attr_2", "unknown"), Is.EqualTo("b"));
}
}
[TestFixture]
public class MjXmlModifiersIntegrationEditorTests {
[Test]
public void ApplyingDefaultsAggregatesAttributesFromMultipleDefaults() {
var mjcfString = @"<mujoco>
<default>
<modified attrib=""a""/>
<default class=""class"">
<modified attrib=""b""/>
</default>
</default>
<worldbody>
<modified class=""class""/>
</worldbody>
</mujoco>";
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
var modifiedElement = mjcf.SelectSingleNode("/mujoco/worldbody/modified") as XmlElement;
var modifiers = new MjXmlModifiers(mjcf);
modifiers.ApplyModifiersToElement(modifiedElement);
modifiedElement = mjcf.SelectSingleNode("/mujoco/worldbody/modified") as XmlElement;
Assert.That(modifiedElement.GetStringAttribute("attrib", string.Empty), Is.EqualTo("b"));
}
[Test]
public void ApplyingDefaultsDoesntOverwriteTheExistingNodeAttribute() {
var mjcfString = @"<mujoco>
<default>
<modified attrib=""b""/>
</default>
<worldbody>
<modified attrib=""a""/>
</worldbody>
</mujoco>";
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
var modifiedElement = mjcf.SelectSingleNode("/mujoco/worldbody/modified") as XmlElement;
var modifiers = new MjXmlModifiers(mjcf);
modifiers.ApplyModifiersToElement(modifiedElement);
modifiedElement = mjcf.SelectSingleNode("/mujoco/worldbody/modified") as XmlElement;
Assert.That(modifiedElement.GetStringAttribute("attrib", string.Empty), Is.EqualTo("a"));
}
[Test]
public void MissingDefaultDoesntBreakInheritanceHierarchy() {
var mjcfString = @"<mujoco>
<default>
<default class=""a"">
<default class=""b"">
<geom type=""sphere""/>
</default>
</default>
</default>
<worldbody>
<body childclass=""a"">
<body childclass=""b"">
<geom name=""thingy""/>
</body>
</body>
</worldbody>
</mujoco>";
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
var modifiedElement = mjcf.SelectSingleNode("descendant::body/geom") as XmlElement;
var modifiers = new MjXmlModifiers(mjcf);
modifiers.ApplyModifiersToElement(modifiedElement);
Assert.That(modifiedElement.GetStringAttribute("name", string.Empty), Is.EqualTo("thingy"));
Assert.That(modifiedElement.GetStringAttribute("type", string.Empty), Is.EqualTo("sphere"));
}
[Test]
public void PropagatePropertiesAcrossDefaultHierarchy() {
var mjcfString = @"<mujoco>
<default>
<default class=""a"">
<geom attrib1=""from_a""/>
<default class=""b"">
<geom attrib2=""from_b""/>
</default>
</default>
</default>
<worldbody>
<body>
<body>
<body>
<geom class=""b"" name=""thingy""/>
</body>
</body>
</body>
</worldbody>
</mujoco>";
var mjcf = new XmlDocument();
mjcf.LoadXml(mjcfString);
var modifiedElement = mjcf.SelectSingleNode("descendant::body/geom") as XmlElement;
var modifiers = new MjXmlModifiers(mjcf);
modifiers.ApplyModifiersToElement(modifiedElement);
Assert.That(modifiedElement.GetStringAttribute("name", string.Empty), Is.EqualTo("thingy"));
Assert.That(modifiedElement.GetStringAttribute("attrib1", string.Empty), Is.EqualTo("from_a"));
Assert.That(modifiedElement.GetStringAttribute("attrib2", string.Empty), Is.EqualTo("from_b"));
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: f585df9e8f9fb4bcd8e5da018f96a3dd
timeCreated: 1539953427
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,346 @@
// 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;
using UnityEngine.TestTools;
namespace Mujoco {
[TestFixture]
public class MjcfImporterTests {
private GameObject _sceneRoot;
private MjcfImporter _importer;
[SetUp]
public void SetUp() {
_importer = new MjcfImporter();
}
[TearDown]
public void TearDown() {
if (_sceneRoot != null) {
UnityEngine.Object.DestroyImmediate(_sceneRoot);
}
}
[Test]
public void SceneRootIsGivenRequestedName() {
var rootName = "rootName";
var mjcfString = "<mujoco><worldbody/></mujoco>";
_sceneRoot = _importer.ImportString(name: rootName, mjcfString: mjcfString);
Assert.That(_sceneRoot.name, Is.EqualTo(rootName));
}
[Test]
public void ParsingAGeom() {
var mjcfString = @"<mujoco>
<worldbody>
<geom size='1'/>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
Assert.That(_sceneRoot.transform.childCount, Is.EqualTo(1));
var geomChild = _sceneRoot.transform.GetChild(0).gameObject;
Assert.That(geomChild, Is.Not.Null);
Assert.That(geomChild.GetComponent<MjGeom>(), Is.Not.Null);
}
[TestCase("hinge", typeof(MjHingeJoint))]
[TestCase("slide", typeof(MjSlideJoint))]
[TestCase("free", typeof(MjFreeJoint))]
public void ParsingJoints(string typeId, Type jointType) {
var mjcfString = string.Format(@"<mujoco>
<worldbody>
<body>
<joint type=""{0}""/>
<geom size='1'/>
</body>
</worldbody>
</mujoco>", typeId);
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
Assert.That(_sceneRoot.transform.childCount, Is.EqualTo(1));
var bodyChild = _sceneRoot.transform.GetChild(0).gameObject;
Assert.That(bodyChild.transform.childCount, Is.EqualTo(2));
var jointChild = bodyChild.transform.GetChild(0).gameObject;
Assert.That(jointChild, Is.Not.Null);
Assert.That(jointChild.GetComponent(jointType), Is.Not.Null);
}
[Test]
public void ParsingAFreeJointStandaloneNode() {
var mjcfString = @"<mujoco>
<worldbody>
<body>
<freejoint/>
<geom size='1'/>
</body>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
Assert.That(_sceneRoot.transform.childCount, Is.EqualTo(1));
var bodyChild = _sceneRoot.transform.GetChild(0).gameObject;
Assert.That(bodyChild.transform.childCount, Is.EqualTo(2));
var jointChild = bodyChild.transform.GetChild(0).gameObject;
Assert.That(jointChild, Is.Not.Null);
Assert.That(jointChild.GetComponent<MjFreeJoint>(), Is.Not.Null);
}
[Test]
public void ParsingABody() {
var mjcfString = @"<mujoco>
<worldbody>
<body/>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
Assert.That(_sceneRoot.transform.childCount, Is.EqualTo(1));
var bodyChild = _sceneRoot.transform.GetChild(0).gameObject;
Assert.That(bodyChild, Is.Not.Null);
Assert.That(bodyChild.GetComponent<MjBody>(), Is.Not.Null);
}
[Test]
public void ParsingChildrenOfABody() {
var mjcfString = @"<mujoco>
<worldbody>
<body>
<geom size='1'/>
<body/>
<joint type=""hinge""/>
</body>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
Assert.That(_sceneRoot.transform.childCount, Is.EqualTo(1));
var bodyChild = _sceneRoot.transform.GetChild(0).gameObject;
Assert.That(bodyChild, Is.Not.Null);
Assert.That(bodyChild.transform.childCount, Is.EqualTo(3));
}
[Test]
public void AnonymousNodeReceivesGeneratedName() {
var mjcfString = @"<mujoco>
<worldbody>
<body/>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var bodyChild = _sceneRoot.transform.GetChild(0).gameObject;
Assert.That(bodyChild.name, Is.EqualTo("body_0"));
}
[Test]
public void AnonymousNodesReceiveTheSameNamesWithEveryImport() {
var mjcfString = @"<mujoco>
<worldbody>
<body/>
</worldbody>
</mujoco>";
var scenes = new GameObject[] {
_importer.ImportString(name: string.Empty, mjcfString: mjcfString),
_importer.ImportString(name: string.Empty, mjcfString: mjcfString)
};
// Ensure that the objects will be destroyed after the test is completed.
_sceneRoot = new GameObject();
foreach (var scene in scenes) {
scene.transform.parent = _sceneRoot.transform;
}
var children = scenes.Select(parent => parent.transform.GetChild(0).gameObject).ToArray();
Assert.That(children[0].name, Is.EqualTo(children[1].name));
}
[Test]
public void ApplyingDefaultOutsideWorldbody() {
var mjcfString = @"<mujoco>
<default>
<general ctrllimited='true' ctrlrange='-1 1'/>
</default>
<worldbody>
<body>
<geom name=""root_geom"" size='1 2'/>
<joint name='j'/>
</body>
</worldbody>
<actuator>
<general joint='j'/>
</actuator>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var actuator = _sceneRoot.GetComponentInChildren<MjActuator>();
Assert.That(actuator.CommonParams.CtrlLimited, Is.True);
}
[Test]
public void ApplyingDefaultSettingsToNodeWithoutClass() {
var mjcfString = @"<mujoco>
<default>
<geom type=""cylinder""/>
</default>
<worldbody>
<geom name=""root_geom"" size='1 2'/>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var rootGeom = _sceneRoot.GetComponentInChildren<MjGeom>();
Assert.That(rootGeom.ShapeType, Is.EqualTo(MjShapeComponent.ShapeTypes.Cylinder));
}
[Test]
public void ApplyingHierarchicalDefaultSettingsToNodeWithClass() {
var mjcfString = @"<mujoco>
<default>
<geom type=""cylinder""/>
<default class=""child"">
<geom type=""box""/>
</default>
</default>
<worldbody>
<body childclass=""child"">
<geom name=""body_geom"" size='1 2 3'/>
</body>
</worldbody>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var bodyGeom = _sceneRoot.GetComponentInChildren<MjGeom>();
Assert.That(bodyGeom.ShapeType, Is.EqualTo(MjShapeComponent.ShapeTypes.Box));
}
[Test]
public void ReadingOption() {
var mjcfString = @"<mujoco>
<option impratio='1.2345'/>
<worldbody/>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var settings = _sceneRoot.GetComponentInChildren<MjGlobalSettings>();
Assert.That(settings.GlobalOptions.ImpRatio, Is.EqualTo(1.2345f));
}
[Test]
public void ReadingSize() {
var mjcfString = @"<mujoco>
<size njmax='1234'/>
<worldbody/>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var settings = _sceneRoot.GetComponentInChildren<MjGlobalSettings>();
Assert.That(settings.GlobalSizes.Njmax, Is.EqualTo(1234));
}
[Test]
public void ReadingOptionAndSize() {
var mjcfString = @"<mujoco>
<option impratio='5.4321'/>
<size njmax='4321'/>
<worldbody/>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var settings = _sceneRoot.GetComponentInChildren<MjGlobalSettings>();
Assert.That(settings.GlobalOptions.ImpRatio, Is.EqualTo(5.4321f));
Assert.That(settings.GlobalSizes.Njmax, Is.EqualTo(4321));
}
[Test]
public void ReadingOptionFlags() {
var mjcfString = @"<mujoco>
<option>
<flag gravity='disable' contact='disable' sensornoise='enable'/>
</option>
<worldbody/>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var settings = _sceneRoot.GetComponentInChildren<MjGlobalSettings>();
Assert.That(settings.GlobalOptions.Flag.Gravity, Is.EqualTo(EnableDisableFlag.disable));
Assert.That(settings.GlobalOptions.Flag.Contact, Is.EqualTo(EnableDisableFlag.disable));
Assert.That(settings.GlobalOptions.Flag.SensorNoise, Is.EqualTo(EnableDisableFlag.enable));
}
[Test]
public void ParsedActuatorsAddedToDedicatedGameObjectAggregate() {
var mjcfString = @"<mujoco>
<worldbody>
<body>
<geom size='1'/>
<joint name='1'/>
</body>
</worldbody>
<actuator>
<motor joint='1'/>
</actuator>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var actuator = _sceneRoot.GetComponentInChildren<MjActuator>();
Assert.That(actuator, Is.Not.Null);
Assert.That(actuator.transform.parent.gameObject.name, Is.EqualTo("actuators"));
}
[Test]
public void ParsedSensorsAddedToDedicatedGameObjectAggregate() {
var mjcfString = @"<mujoco>
<worldbody>
<body name='1'/>
</worldbody>
<sensor>
<framepos objtype='body' objname='1'/>
</sensor>
</mujoco>";
_sceneRoot = _importer.ImportString(
name: string.Empty, mjcfString: mjcfString);
var sensor = _sceneRoot.GetComponentInChildren<MjBaseSensor>();
Assert.That(sensor, Is.Not.Null);
Assert.That(sensor.transform.parent.gameObject.name, Is.EqualTo("sensors"));
}
[Test]
public void NodeHandlerCalledForMatchingelements() {
var mjcfString = @"<mujoco>
<worldbody>
<body>
<CustomTestNode/>
</body>
</worldbody>
</mujoco>";
var customImporter = new MjcfImporter();
bool ranCustomFunction = false;
customImporter.AddNodeHandler("CustomTestNode", (element, o) => {
ranCustomFunction = true;
});
var mjcfXml = new XmlDocument();
mjcfXml.LoadXml(mjcfString);
customImporter.ImportXml(mjcfXml);
Assert.That(ranCustomFunction, Is.True);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15a77617e445153bb847b0c8e91e0c29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: