Initial release of the Unity plugin.
PiperOrigin-RevId: 425947505
This commit is contained in:
committed by
Saran Tunyasuvunakool
parent
8ec3dd97a4
commit
6de9eafa13
@@ -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:
|
||||
Reference in New Issue
Block a user