Version 2.1.2: Python bindings, OBJ assets support, bugfixes.
PiperOrigin-RevId: 434731612 Change-Id: I0cfda3e7a3d1c72036764986efc252ffa1b8c6b0
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// Functionality that needs to be implemented for each shape type.
|
||||
public interface IMjShape {
|
||||
|
||||
// Generate the Mjcf representation of the shape. Infer the scale of the shape from the transform.
|
||||
void ToMjcf(XmlElement mjcf, Transform transform);
|
||||
|
||||
// Parse the shape settings from the specified Mjcf node.
|
||||
void FromMjcf(XmlElement mjcf);
|
||||
|
||||
// Build a parametric mesh (vertices, triangles) of this shape.
|
||||
Tuple<Vector3[], int[]> BuildMesh();
|
||||
|
||||
// Generate a timestamp that can be used to quickly compare if the settings of a shape
|
||||
// have changed.
|
||||
// The timestamp should also allow to distinguish between different types of shapes.
|
||||
//
|
||||
// Because most of shapes use at most 3 floating point values as parameters, we will
|
||||
// use the first 3 components of the returned Vector4 to store those values. We will store
|
||||
// the id of the shape in the 4th component. That id will be derrived from MjShapeComponent.ShapeTypes
|
||||
// enum.
|
||||
Vector4 GetChangeStamp();
|
||||
|
||||
// Draw a debug gizmo for the shape.
|
||||
void DebugDraw(Transform transform);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4b869d65e8404f3aa92042bbd7b9c43
|
||||
timeCreated: 1547647082
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,360 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
public static class MeshGenerators {
|
||||
|
||||
// Creates a sphere mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// scale: Non-uniform sphere scale. Allows to create ellipsoid shapes.
|
||||
// numVerticalSlices: How many vertices should there be in a single horizontal slice.
|
||||
// numHorizontalSlices: How many horizontal slices should the sphere consist of.
|
||||
public static Tuple<Vector3[], int[]> BuildSphere(
|
||||
Vector3 scale, int numVerticalSlices = 16, int numHorizontalSlices = 16) {
|
||||
// Generate the vertices.
|
||||
Vector3[] vertices;
|
||||
int[] triangles;
|
||||
GenerateSphereMeshSlice(
|
||||
scale: scale,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
numHorizontalSlices: numHorizontalSlices,
|
||||
firstSliceY: -1.0f,
|
||||
lastSliceY: 1.0f,
|
||||
vertices: out vertices,
|
||||
triangles: out triangles);
|
||||
return Tuple.Create(vertices, triangles);
|
||||
}
|
||||
|
||||
// Creates a cylinder mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// radius: Radius of the cylider's body.
|
||||
// height: Height of the cylinder, from its bottom to its top base.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
public static Tuple<Vector3[], int[]> BuildCylinder(
|
||||
float radius, float height, int numVerticalSlices = 16) {
|
||||
Vector3[] bodyVertices;
|
||||
int[] bodyTriangles;
|
||||
GenerateCylinderBody(radius, height, numVerticalSlices, out bodyVertices, out bodyTriangles);
|
||||
|
||||
Vector3[] baseCapVertices;
|
||||
int[] baseCapTriangles;
|
||||
GenerateCylinderBaseCaps(
|
||||
radius, height, numVerticalSlices, out baseCapVertices, out baseCapTriangles);
|
||||
|
||||
var merger = new MeshMerger();
|
||||
merger.Add(bodyVertices, bodyTriangles);
|
||||
merger.Add(baseCapVertices, baseCapTriangles);
|
||||
return Tuple.Create(merger.Vertices, merger.Triangles);
|
||||
}
|
||||
|
||||
// Creates a capsule mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// radius: Radius of the capsule's body.
|
||||
// height: Height of the capsule, from its bottom to its top base.
|
||||
// numVerticalSlices: How many vertices should there be in a single horizontal slice.
|
||||
// numHorizontalSlices: How many vertical slices should the sphere consist of.
|
||||
public static Tuple<Vector3[], int[]> BuildCapsule(
|
||||
float radius, float height, int numVerticalSlices = 16, int numHorizontalSlices = 16) {
|
||||
var baseHalfHeight = Math.Max(0.0f, height * 0.5f - radius);
|
||||
Vector3[] topCapVertices;
|
||||
int[] topCapTriangles;
|
||||
GenerateSphereMeshSlice(
|
||||
scale: Vector3.one * radius,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
numHorizontalSlices: numHorizontalSlices / 2,
|
||||
firstSliceY: -1.0f,
|
||||
lastSliceY: 0.0f,
|
||||
vertices: out topCapVertices,
|
||||
triangles: out topCapTriangles);
|
||||
|
||||
Vector3[] bottomCapVertices;
|
||||
int[] bottomCapTriangles;
|
||||
GenerateSphereMeshSlice(
|
||||
scale: Vector3.one * radius,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
numHorizontalSlices: numHorizontalSlices / 2,
|
||||
firstSliceY: 0.0f,
|
||||
lastSliceY: 1.0f,
|
||||
vertices: out bottomCapVertices,
|
||||
triangles: out bottomCapTriangles);
|
||||
|
||||
Vector3[] bodyVertices;
|
||||
int[] bodyTriangles;
|
||||
GenerateCylinderBody(
|
||||
radius: radius,
|
||||
height: baseHalfHeight * 2.0f,
|
||||
numVerticalSlices: numVerticalSlices,
|
||||
vertices: out bodyVertices,
|
||||
triangles: out bodyTriangles);
|
||||
|
||||
var merger = new MeshMerger();
|
||||
merger.AddAndTranslate(topCapVertices, topCapTriangles, Vector3.up * baseHalfHeight * -1.0f);
|
||||
merger.AddAndTranslate(
|
||||
bottomCapVertices, bottomCapTriangles, Vector3.up * baseHalfHeight * 1.0f);
|
||||
merger.Add(bodyVertices, bodyTriangles);
|
||||
return Tuple.Create(merger.Vertices, merger.Triangles);
|
||||
}
|
||||
|
||||
// Creates a box mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// extents: Extents of the box, along each major axis.
|
||||
public static Tuple<Vector3[], int[]> BuildBox(Vector3 extents) {
|
||||
// In order to ensure the box renders with flat faces, we need to make sure the triangles
|
||||
// do not share vertices. We'll accomplish that by assigning a unique vertex to every triangle
|
||||
// apex.
|
||||
// We define a set of 8 vertex positions that form the box, and then sample from that set using
|
||||
// triangle indices.
|
||||
var vertexPositions = new Vector3[] {
|
||||
Vector3.Scale(new Vector3(-1, -1, -1), extents),
|
||||
Vector3.Scale(new Vector3(1, -1, -1), extents),
|
||||
Vector3.Scale(new Vector3(-1, -1, 1), extents),
|
||||
Vector3.Scale(new Vector3(1, -1, 1), extents),
|
||||
Vector3.Scale(new Vector3(-1, 1, -1), extents),
|
||||
Vector3.Scale(new Vector3(1, 1, -1), extents),
|
||||
Vector3.Scale(new Vector3(-1, 1, 1), extents),
|
||||
Vector3.Scale(new Vector3(1, 1, 1), extents),
|
||||
};
|
||||
var vertexSamplingPattern = new int[] {
|
||||
0, 1, 3, 0, 3, 2,
|
||||
4, 7, 5, 4, 6, 7,
|
||||
0, 5, 1, 0, 4, 5,
|
||||
1, 7, 3, 1, 5, 7,
|
||||
3, 6, 2, 3, 7, 6,
|
||||
2, 4, 0, 2, 6, 4,
|
||||
};
|
||||
|
||||
var vertices = vertexSamplingPattern.Select(index => vertexPositions[index]).ToArray();
|
||||
var triangles = Enumerable.Range(0, vertexSamplingPattern.Length).ToArray();
|
||||
return Tuple.Create(vertices, triangles);
|
||||
}
|
||||
|
||||
// Creates a plane mesh geometry.
|
||||
//
|
||||
// Args:
|
||||
// width: Width of the plane, along the OX axis.
|
||||
// height: Height of the plane, along the OZ axis.
|
||||
public static Tuple<Vector3[], int[]> BuildPlane(float width, float height) {
|
||||
var vertices = new Vector3[] {
|
||||
new Vector3(-0.5f * width, 0, -0.5f * height),
|
||||
new Vector3(0.5f * width, 0, -0.5f * height),
|
||||
new Vector3(-0.5f * width, 0, 0.5f * height),
|
||||
new Vector3(0.5f * width, 0, 0.5f * height),
|
||||
};
|
||||
var triangles = new int[] {
|
||||
0, 3, 1,
|
||||
0, 2, 3,
|
||||
};
|
||||
return Tuple.Create(vertices, triangles);
|
||||
}
|
||||
|
||||
// Generates a slice of a sphere mesh.
|
||||
// Conceptually, the algorithm generates a sphere with a unit radius (spanning from -1 to 1 along
|
||||
// each of the major axes). Then, it cuts it using 2 planes parallel to the XZ plane, located at
|
||||
// distances defined by 'firstSliceY' and 'lastSliceY' parameters respectively. It then returns
|
||||
// the section of the mesh contained between those planes.
|
||||
//
|
||||
// This allows to use the method to generate variants of the sphere - full sphere, hemispheres.
|
||||
//
|
||||
// Args:
|
||||
// scale: Non-uniform sphere scale. Allows to create ellipsoid shapes.
|
||||
// numVerticalSlices: How many vertices should there be in a single horizontal slice.
|
||||
// numHorizontalSlices: How many horizontal slices should the sphere consist of.
|
||||
// firstSliceY: Vertical position of the first slice. Must be a value in range <-1, 1>.
|
||||
// lastSliceY: Vertical position of the last slice. Must a value in range <-1, 1>.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
// triangles: (Out) Array with the sphere triangle connectivity.
|
||||
private static void GenerateSphereMeshSlice(
|
||||
Vector3 scale, int numVerticalSlices, int numHorizontalSlices, float firstSliceY,
|
||||
float lastSliceY, out Vector3[] vertices, out int[] triangles) {
|
||||
numVerticalSlices = Math.Max(3, numVerticalSlices);
|
||||
numHorizontalSlices = Math.Max(3, numHorizontalSlices);
|
||||
triangles = new int[(numHorizontalSlices - 1) * numVerticalSlices * 6];
|
||||
vertices = new Vector3[numVerticalSlices * numHorizontalSlices];
|
||||
if (firstSliceY < -1.0f || firstSliceY > 1.0f) {
|
||||
throw new IOException("firstSliceY should be a value in range <-1, 1>");
|
||||
}
|
||||
if (lastSliceY < -1.0f || lastSliceY > 1.0f) {
|
||||
throw new IOException("lastSliceY should be a value in range <-1, 1>");
|
||||
}
|
||||
if (firstSliceY > lastSliceY) {
|
||||
throw new IOException("Value of firstSliceY should be lower than the value of lastSliceY.");
|
||||
}
|
||||
// Generate the vertices.
|
||||
var deltaY = (lastSliceY - firstSliceY) / (numHorizontalSlices - 1);
|
||||
var deltaYaw = 360.0f / numVerticalSlices;
|
||||
for (var slice = 0; slice < numHorizontalSlices; ++slice) {
|
||||
var y = firstSliceY + deltaY * slice;
|
||||
var radius = (float)Math.Sqrt(1 - Math.Min(1.0f, y * y));
|
||||
for (var vertex = 0; vertex < numVerticalSlices; ++vertex) {
|
||||
var position = Quaternion.AngleAxis(deltaYaw * vertex, Vector3.up) * Vector3.right * radius;
|
||||
position.y = y;
|
||||
vertices[slice * numVerticalSlices + vertex] = Vector3.Scale(position, scale);
|
||||
}
|
||||
}
|
||||
// Build the triangles.
|
||||
for (var slice = 0; slice < (numHorizontalSlices - 1); ++slice) {
|
||||
var firstVertexInSlice = slice * numVerticalSlices;
|
||||
for (var vertex = 0; vertex < numVerticalSlices; ++vertex) {
|
||||
var index1 = firstVertexInSlice + vertex;
|
||||
var index2 = (vertex + 1 == numVerticalSlices) ? firstVertexInSlice : index1 + 1;
|
||||
var index3 = index1 + numVerticalSlices;
|
||||
var index4 = index2 + numVerticalSlices;
|
||||
var quadBaseAddress = (slice * numVerticalSlices + vertex) * 6;
|
||||
triangles[quadBaseAddress] = index1;
|
||||
triangles[quadBaseAddress + 1] = index2;
|
||||
triangles[quadBaseAddress + 2] = index4;
|
||||
triangles[quadBaseAddress + 3] = index1;
|
||||
triangles[quadBaseAddress + 4] = index4;
|
||||
triangles[quadBaseAddress + 5] = index3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the cylinder mesh vertices.
|
||||
// This functionality will be shared between the cylinder body and cylinder base caps generators.
|
||||
//
|
||||
// Args:
|
||||
// radius: Cylinder radius.
|
||||
// height: Cylinder height.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
private static void GenerateCylinderVertices(
|
||||
float radius, float height, int numVerticalSlices, out Vector3[] vertices) {
|
||||
vertices = new Vector3[numVerticalSlices * 2];
|
||||
var dYaw = 360.0f / numVerticalSlices;
|
||||
var yaw = 0.0f;
|
||||
var vertexIndex = 0;
|
||||
for (var y = 0; y <= 1; ++y) {
|
||||
var yPos = (y - 0.5f) * height;
|
||||
for (var i = 0; i < numVerticalSlices; ++i, yaw += dYaw) {
|
||||
var vertexPosition = Quaternion.AngleAxis(yaw, Vector3.up) * Vector3.right * radius;
|
||||
vertexPosition.y = yPos;
|
||||
vertices[vertexIndex++] = vertexPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the meshes for the cylinder body, excluding its bases.
|
||||
//
|
||||
// Args:
|
||||
// radius: Cylinder radius.
|
||||
// height: Cylinder height.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
// triangles: (Out) Array with the sphere triangle connectivity.
|
||||
private static void GenerateCylinderBody(
|
||||
float radius, float height, int numVerticalSlices, out Vector3[] vertices,
|
||||
out int[] triangles) {
|
||||
GenerateCylinderVertices(radius, height, numVerticalSlices, out vertices);
|
||||
triangles = new int[numVerticalSlices * 6];
|
||||
var apexIndex = 0;
|
||||
for (var i = 0; i < numVerticalSlices; ++i) {
|
||||
var v1 = i;
|
||||
var v2 = (i + 1 == numVerticalSlices) ? 0 : v1 + 1;
|
||||
var v3 = v1 + numVerticalSlices;
|
||||
var v4 = v2 + numVerticalSlices;
|
||||
triangles[apexIndex++] = v1;
|
||||
triangles[apexIndex++] = v2;
|
||||
triangles[apexIndex++] = v4;
|
||||
triangles[apexIndex++] = v1;
|
||||
triangles[apexIndex++] = v4;
|
||||
triangles[apexIndex++] = v3;
|
||||
}
|
||||
}
|
||||
|
||||
// Generates the meshes for the cylinder base circles.
|
||||
//
|
||||
// Args:
|
||||
// radius: Cylinder radius.
|
||||
// height: Cylinder height.
|
||||
// numVerticalSlices: How many vertices should there be around the base's circumference.
|
||||
// vertices: (Out) Array of sphere vertex positions.
|
||||
// triangles: (Out) Array with the sphere triangle connectivity.
|
||||
private static void GenerateCylinderBaseCaps(
|
||||
float radius, float height, int numVerticalSlices, out Vector3[] vertices,
|
||||
out int[] triangles) {
|
||||
GenerateCylinderVertices(radius, height, numVerticalSlices, out vertices);
|
||||
triangles = new int[(numVerticalSlices - 1) * 6];
|
||||
var apexIndex = 0;
|
||||
for (var baseIdx = 0; baseIdx <= 1; ++baseIdx) {
|
||||
var v1 = baseIdx * numVerticalSlices;
|
||||
for (var i = 1; i < numVerticalSlices; ++i) {
|
||||
var v2 = v1 + (i % numVerticalSlices);
|
||||
var v3 = v1 + ((i + 1) % numVerticalSlices);
|
||||
triangles[apexIndex++] = v1;
|
||||
triangles[apexIndex++] = baseIdx == 0 ? v3 : v2;
|
||||
triangles[apexIndex++] = baseIdx == 0 ? v2 : v3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A tool used to merge the geometry of separate meshes.
|
||||
public class MeshMerger {
|
||||
private List<Vector3> _vertices = new List<Vector3>();
|
||||
private List<int> _triangles = new List<int>();
|
||||
|
||||
// The vertices of the merged mesh.
|
||||
public Vector3[] Vertices => _vertices.ToArray();
|
||||
|
||||
// The connectivity array of the merged mesh.
|
||||
public int[] Triangles => _triangles.ToArray();
|
||||
|
||||
// Adds a new submesh.
|
||||
//
|
||||
// Args:
|
||||
// vertices: Array of submesh vertices.
|
||||
// vertices: Triangles connectivity array of the submesh.
|
||||
public void Add(Vector3[] vertices, int[] triangles) {
|
||||
var triangleOffset = _triangles.Count;
|
||||
var vertexOffset = _vertices.Count;
|
||||
_vertices.AddRange(vertices);
|
||||
_triangles.AddRange(triangles);
|
||||
for (var i = triangleOffset; i < _triangles.Count; ++i) {
|
||||
_triangles[i] += vertexOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a new submesh, translating its vertices by a specified amount.
|
||||
//
|
||||
// Args:
|
||||
// vertices: Array of submesh vertices.
|
||||
// vertices: Triangles connectivity array of the submesh.
|
||||
// translation: Additional translation to be applied to the submesh.
|
||||
public void AddAndTranslate(Vector3[] vertices, int[] triangles, Vector3 translation) {
|
||||
var triangleOffset = _triangles.Count;
|
||||
var vertexOffset = _vertices.Count;
|
||||
_vertices.AddRange(vertices);
|
||||
for (var i = vertexOffset; i < _vertices.Count; ++i) {
|
||||
_vertices[i] += translation;
|
||||
}
|
||||
_triangles.AddRange(triangles);
|
||||
for (var i = triangleOffset; i < _triangles.Count; ++i) {
|
||||
_triangles[i] += vertexOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: deb4d8c01485d4e44bc815b133e707c0
|
||||
timeCreated: 1539115944
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjBoxShape : IMjShape {
|
||||
public Vector3 Extents = Vector3.one * 0.5f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
var scaledExtents = MjEngineTool.MjExtents(Vector3.Scale(Extents, transform.lossyScale));
|
||||
mjcf.SetAttribute("size", MjEngineTool.Vector3ToMjcf(scaledExtents));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
Extents = MjEngineTool.UnityExtents(
|
||||
mjcf.GetVector3Attribute("size", defaultValue: Vector3.one * 0.5f));
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
var extent = (toPoint - fromPoint).magnitude * 0.5f;
|
||||
Extents = Vector3.one * extent;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildBox(extents: Extents);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return Extents;
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, transform.lossyScale);
|
||||
Gizmos.DrawWireCube(Vector3.zero, Extents * 2.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9783bb0e781e341e5914cdbac095b35e
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjCapsuleShape : IMjShape {
|
||||
public float Radius = 0.5f;
|
||||
public float HalfHeight = 0.5f;
|
||||
private const float _scaleTolerance = 1e-5f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
if (Math.Abs(transform.lossyScale.x - transform.lossyScale.z) > _scaleTolerance) {
|
||||
Debug.LogWarning(
|
||||
$"{transform.name}: Capsule shapes require uniform scaling of XZ plane. Using the" +
|
||||
" value of X and Y components.", transform);
|
||||
}
|
||||
mjcf.SetAttribute(
|
||||
"size", MjEngineTool.MakeLocaleInvariant($"{Radius * transform.lossyScale.x} {HalfHeight * transform.lossyScale.y}"));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
var components = mjcf.GetFloatArrayAttribute("size", defaultValue: new float[] { 0.5f, 0.5f });
|
||||
Radius = components[0];
|
||||
HalfHeight = components[1];
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
HalfHeight = 0.5f * (toPoint - fromPoint).magnitude;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildCapsule(radius: Radius, height: (HalfHeight + Radius) * 2.0f);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return new Vector4(Radius, HalfHeight, 1, 0);
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
MjGizmos.DrawWireCapsule(
|
||||
Vector3.zero, Radius * transform.lossyScale.x, 2 * HalfHeight * transform.lossyScale.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b0a2054e1eec409fbf34c796a572b38
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjCylinderShape : IMjShape {
|
||||
public float Radius = 0.5f;
|
||||
public float HalfHeight = 1.0f;
|
||||
private const float _scaleTolerance = 1e-6f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
if (Math.Abs(transform.lossyScale.x - transform.lossyScale.z) > _scaleTolerance) {
|
||||
Debug.LogWarning(
|
||||
$"{transform.name}: Cylinder shapes require uniform scaling of XZ plane. Using the" +
|
||||
" value of X and Y components.", transform);
|
||||
}
|
||||
mjcf.SetAttribute(
|
||||
"size", MjEngineTool.MakeLocaleInvariant($"{Radius * transform.lossyScale.x} {HalfHeight * transform.lossyScale.y}"));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
var components = mjcf.GetFloatArrayAttribute("size", defaultValue: new float[] { 0.5f, 1.0f });
|
||||
Radius = components[0];
|
||||
HalfHeight = components[1];
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
HalfHeight = (toPoint - fromPoint).magnitude * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildCylinder(radius: Radius, height: HalfHeight * 2);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return new Vector4(Radius, HalfHeight, 1, 0);
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
MjGizmos.DrawWireCylinder(
|
||||
Vector3.zero, Radius * transform.lossyScale.x, HalfHeight * 2 * transform.lossyScale.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd3a2b00a0a2f4ba9be8bd2f054a3835
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjEllipsoidShape : IMjShape {
|
||||
public Vector3 Radiuses = Vector3.one * 0.5f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
var scaledRadiuses = MjEngineTool.MjExtents(Vector3.Scale(Radiuses, transform.lossyScale));
|
||||
mjcf.SetAttribute("size", MjEngineTool.Vector3ToMjcf(scaledRadiuses));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
Radiuses = MjEngineTool.UnityExtents(
|
||||
mjcf.GetVector3Attribute("size", defaultValue: Vector3.one * 0.5f));
|
||||
|
||||
Vector3 fromPoint, toPoint;
|
||||
if (MjEngineTool.ParseFromToMjcf(mjcf, out fromPoint, out toPoint)) {
|
||||
var radius = (toPoint - fromPoint).magnitude * 0.5f;
|
||||
Radiuses = Vector3.one * radius;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildSphere(scale: Radiuses);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return Radiuses;
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(
|
||||
transform.position,
|
||||
transform.rotation,
|
||||
Vector3.Scale(Radiuses, transform.lossyScale));
|
||||
Gizmos.DrawWireSphere(Vector3.zero, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8316fb5037f4a450c9270267e5428567
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// The component represents a physical shape and models its inertia and material properties.
|
||||
public class MjGeom : MjShapeComponent {
|
||||
[Tooltip("If larger than zero, Density has no effect.")]
|
||||
public float Mass = 0.0f;
|
||||
|
||||
[Tooltip("Material density. Set to 0 for a zero-mass geom.")]
|
||||
public float Density = 1000.0f;
|
||||
|
||||
[Tooltip("Advanced settings.")]
|
||||
public MjGeomSettings Settings = MjGeomSettings.Default;
|
||||
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_GEOM;
|
||||
private MjTransformation _geomInGlobalFrame = new MjTransformation();
|
||||
private MjTransformation _comTransform;
|
||||
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
ShapeFromMjcf(mjcf);
|
||||
Mass = mjcf.GetFloatAttribute("mass", defaultValue: 0.0f);
|
||||
Density = mjcf.GetFloatAttribute("density", defaultValue: 1000.0f);
|
||||
MjEngineTool.ParseTransformMjcf(mjcf, transform);
|
||||
Settings.FromMjcf(mjcf);
|
||||
}
|
||||
|
||||
// The MuJoCo compiler shifts meshes' frames, so we need to cache this transformation at init and
|
||||
// apply it at runtime.
|
||||
protected override unsafe void OnBindToRuntime(MujocoLib.mjModel_* model, MujocoLib.mjData_* data) {
|
||||
var MjParent = MjHierarchyTool.FindParentComponent<MjBaseBody>(this);
|
||||
if (MjParent != null) {
|
||||
var comInParentFrame = new MjTransformation(
|
||||
translation: MjEngineTool.UnityVector3(model->geom_pos, MujocoId),
|
||||
rotation: MjEngineTool.UnityQuaternion(model->geom_quat, MujocoId));
|
||||
|
||||
// We don't want to bother calculating global transform in mujoco from mjModel,
|
||||
// so we'll assume it's the same as the Unity transfor (it's the beginning of simulation after
|
||||
// all).
|
||||
var globalParentFrame = MjTransformation.LoadGlobal(transform.parent);
|
||||
var comInGlobalFrame = globalParentFrame * comInParentFrame;
|
||||
var globalFrame = MjTransformation.LoadGlobal(transform);
|
||||
_comTransform = comInGlobalFrame.Inverse() * globalFrame;
|
||||
}
|
||||
}
|
||||
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = (XmlElement)doc.CreateElement("geom");
|
||||
if (Mass > 0) {
|
||||
mjcf.SetAttribute("mass", MjEngineTool.MakeLocaleInvariant($"{Mass}"));
|
||||
} else {
|
||||
mjcf.SetAttribute("density", MjEngineTool.MakeLocaleInvariant($"{Density}"));
|
||||
}
|
||||
ShapeToMjcf(mjcf, transform);
|
||||
MjEngineTool.PositionRotationToMjcf(mjcf, this);
|
||||
Settings.ToMjcf(mjcf);
|
||||
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
|
||||
if (ShapeType == ShapeTypes.Mesh) {
|
||||
_geomInGlobalFrame.Set(
|
||||
translation: MjEngineTool.UnityVector3(data->geom_xpos, MujocoId),
|
||||
rotation: MjEngineTool.UnityQuaternionFromMatrix(data->geom_xmat, MujocoId));
|
||||
var comInGlobalFrame = _geomInGlobalFrame * _comTransform;
|
||||
comInGlobalFrame.StoreGlobal(transform);
|
||||
} else {
|
||||
transform.position = MjEngineTool.UnityVector3(data->geom_xpos, MujocoId);
|
||||
transform.rotation = MjEngineTool.UnityQuaternionFromMatrix(data->geom_xmat, MujocoId);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDrawGizmosSelected() {
|
||||
Gizmos.color = Color.blue;
|
||||
DrawGizmos(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba8757a90afe442d9acfbd600758b843
|
||||
timeCreated: 1536743564
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,148 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// Advanced settings of MjGeom component.
|
||||
[Serializable]
|
||||
public struct MjGeomSettings {
|
||||
[Tooltip("Solver accuracy settings.")]
|
||||
public GeomSolver Solver;
|
||||
|
||||
[Tooltip("Collision filtering settings.")]
|
||||
public CollisionFiltering Filtering;
|
||||
|
||||
[Tooltip("Contact friction parameters for dynamically generated contact pairs.")]
|
||||
public GeomFriction Friction;
|
||||
|
||||
// Default geom settings.
|
||||
public static MjGeomSettings Default = new MjGeomSettings() {
|
||||
Solver = GeomSolver.Default,
|
||||
Filtering = CollisionFiltering.Default,
|
||||
Friction = GeomFriction.Default
|
||||
};
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
// Contact filtering settings.
|
||||
Filtering.Contype = (int)mjcf.GetFloatAttribute("contype", CollisionFiltering.Default.Contype);
|
||||
Filtering.Conaffinity = (int)mjcf.GetFloatAttribute(
|
||||
"conaffinity", CollisionFiltering.Default.Conaffinity);
|
||||
Filtering.Group = (int)mjcf.GetFloatAttribute("group", CollisionFiltering.Default.Group);
|
||||
|
||||
// Solver settings.
|
||||
Solver.ConDim = (int)mjcf.GetFloatAttribute("condim", GeomSolver.Default.ConDim);
|
||||
Solver.SolMix = mjcf.GetFloatAttribute("solmix", GeomSolver.Default.SolMix);
|
||||
var solref = mjcf.GetFloatArrayAttribute(
|
||||
"solref", new float[] { GeomSolver.Default.SolRef.TimeConst,
|
||||
GeomSolver.Default.SolRef.DampRatio });
|
||||
Solver.SolRef.TimeConst = solref[0];
|
||||
Solver.SolRef.DampRatio = solref[1];
|
||||
var solimp = mjcf.GetFloatArrayAttribute(
|
||||
"solimp", new float[] { GeomSolver.Default.SolImp.DMin, GeomSolver.Default.SolImp.DMax,
|
||||
GeomSolver.Default.SolImp.Width });
|
||||
Solver.SolImp.DMin = solimp[0];
|
||||
Solver.SolImp.DMax = solimp[1];
|
||||
Solver.SolImp.Width = solimp[2];
|
||||
Solver.Margin = mjcf.GetFloatAttribute("margin", GeomSolver.Default.Margin);
|
||||
Solver.Gap = mjcf.GetFloatAttribute("gap", GeomSolver.Default.Gap);
|
||||
|
||||
// Inertia and friction settings.
|
||||
var friction = mjcf.GetFloatArrayAttribute(
|
||||
"friction", new float[] { GeomFriction.Default.Sliding, GeomFriction.Default.Torsional,
|
||||
GeomFriction.Default.Rolling });
|
||||
Friction.Sliding = friction[0];
|
||||
Friction.Torsional = friction[1];
|
||||
Friction.Rolling = friction[2];
|
||||
}
|
||||
|
||||
public void ToMjcf(XmlElement mjcf) {
|
||||
// Contact filtering settings.
|
||||
mjcf.SetAttribute("contype", $"{Filtering.Contype}");
|
||||
mjcf.SetAttribute("conaffinity", $"{Filtering.Conaffinity}");
|
||||
mjcf.SetAttribute("group", $"{Filtering.Group}");
|
||||
|
||||
// Solver settings.
|
||||
mjcf.SetAttribute("condim", $"{Solver.ConDim}");
|
||||
mjcf.SetAttribute("solmix", MjEngineTool.MakeLocaleInvariant($"{Solver.SolMix}"));
|
||||
mjcf.SetAttribute("solref", MjEngineTool.MakeLocaleInvariant($"{Solver.SolRef.TimeConst} {Solver.SolRef.DampRatio}"));
|
||||
mjcf.SetAttribute(
|
||||
"solimp", MjEngineTool.MakeLocaleInvariant($"{Solver.SolImp.DMin} {Solver.SolImp.DMax} {Solver.SolImp.Width}"));
|
||||
mjcf.SetAttribute("margin", MjEngineTool.MakeLocaleInvariant($"{Solver.Margin}"));
|
||||
mjcf.SetAttribute("gap", MjEngineTool.MakeLocaleInvariant($"{Solver.Gap}"));
|
||||
|
||||
// Inertia and friction settings.
|
||||
mjcf.SetAttribute("friction", MjEngineTool.MakeLocaleInvariant($"{Friction.Sliding} {Friction.Torsional} {Friction.Rolling}"));
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct GeomFriction {
|
||||
public float Sliding;
|
||||
public float Torsional;
|
||||
public float Rolling;
|
||||
|
||||
public static GeomFriction Default = new GeomFriction {
|
||||
Sliding = 1.0f, Torsional = 0.005f, Rolling = 0.0001f
|
||||
};
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct CollisionFiltering {
|
||||
|
||||
[Tooltip("Bitmasks used for contact filtering of dynamically generated contact pairs.")]
|
||||
public int Contype;
|
||||
|
||||
[Tooltip("Bitmask for contact filtering")]
|
||||
public int Conaffinity;
|
||||
|
||||
// The only effect on the physics is at compile time, when body masses and inertias are inferred
|
||||
// from geoms selected based on their group.
|
||||
[Tooltip("Group to which the geom belongs.")]
|
||||
public int Group;
|
||||
|
||||
public static CollisionFiltering Default = new CollisionFiltering() {
|
||||
Contype = 1, Conaffinity = 1, Group = 0
|
||||
};
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct GeomSolver {
|
||||
[Tooltip("The dimensionality of the contact space.")]
|
||||
public int ConDim;
|
||||
|
||||
[Tooltip("Weight used for averaging of constraint solver parameters.")]
|
||||
public float SolMix;
|
||||
|
||||
[Tooltip("Solver function d(r) reference parameters.")]
|
||||
public SolverReference SolRef;
|
||||
|
||||
[Tooltip("Solver function d(r) impedance parameters.")]
|
||||
public SolverImpedance SolImp;
|
||||
|
||||
[Tooltip("Distance threshold below which contacts are detected.")]
|
||||
public float Margin;
|
||||
|
||||
[Tooltip("Positive value enables generation of inactive contacts.")]
|
||||
public float Gap;
|
||||
|
||||
public static GeomSolver Default = new GeomSolver() {
|
||||
ConDim = 3, SolMix = 1.0f, SolRef = SolverReference.Default, SolImp = SolverImpedance.Default,
|
||||
Margin = 0.0f, Gap = 0.0f
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d472b07eedb654ed5a83bfd6822587a3
|
||||
timeCreated: 1538655839
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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 UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[RequireComponent(typeof(MjShapeComponent))]
|
||||
[RequireComponent(typeof(MeshFilter))]
|
||||
[ExecuteInEditMode]
|
||||
public class MjMeshFilter : MonoBehaviour {
|
||||
private MjShapeComponent _geom;
|
||||
private Vector4 _shapeChangeStamp;
|
||||
private MeshFilter _meshFilter;
|
||||
|
||||
protected void Awake() {
|
||||
_geom = GetComponent<MjShapeComponent>();
|
||||
_shapeChangeStamp = new Vector4(0, 0, 0, -1);
|
||||
_meshFilter = GetComponent<MeshFilter>();
|
||||
}
|
||||
|
||||
protected void Update() {
|
||||
var currentChangeStamp = _geom.GetChangeStamp();
|
||||
if ((_shapeChangeStamp - currentChangeStamp).magnitude <= 1e-3f) {
|
||||
return;
|
||||
}
|
||||
|
||||
_shapeChangeStamp = currentChangeStamp;
|
||||
Tuple<Vector3[], int[]> meshData = _geom.BuildMesh();
|
||||
|
||||
if (meshData == null) {
|
||||
throw new ArgumentException("Unsupported geom shape detected");
|
||||
}
|
||||
|
||||
DisposeCurrentMesh();
|
||||
|
||||
var mesh = new Mesh();
|
||||
// Name this mesh to easily track resources in Unity analysis tools.
|
||||
mesh.name = $"Mujoco mesh for {gameObject.name}, id:{mesh.GetInstanceID()}";
|
||||
_meshFilter.sharedMesh = mesh;
|
||||
mesh.vertices = meshData.Item1;
|
||||
mesh.triangles = meshData.Item2;
|
||||
mesh.RecalculateNormals();
|
||||
mesh.RecalculateTangents();
|
||||
}
|
||||
|
||||
protected void OnDestroy() {
|
||||
DisposeCurrentMesh();
|
||||
}
|
||||
|
||||
// Dynamically created meshes with no references are only disposed automatically on scene changes.
|
||||
// This prevents resource leaks in case the host environment doesn't reload scenes.
|
||||
private void DisposeCurrentMesh() {
|
||||
if (_meshFilter.sharedMesh != null) {
|
||||
#if UNITY_EDITOR
|
||||
DestroyImmediate(_meshFilter.sharedMesh);
|
||||
#else
|
||||
Destroy(_meshFilter.sharedMesh);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c762400e63d394f6fa8f9f7885edb79e
|
||||
timeCreated: 1539770521
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjMeshShape : IMjShape {
|
||||
|
||||
public Mesh Mesh;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
var scene = MjScene.Instance;
|
||||
var assetName = scene.GenerationContext.AddMeshAsset(Mesh);
|
||||
mjcf.SetAttribute("mesh", assetName);
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
// When the asset was parsed and stored its name was sanitized, so we should load it using a
|
||||
// sanitized name:
|
||||
var assetName = MjEngineTool.Sanitize(
|
||||
mjcf.GetStringAttribute("mesh", defaultValue: string.Empty));
|
||||
if (!string.IsNullOrEmpty(assetName)) {
|
||||
Mesh = (Mesh)Resources.Load(assetName);
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return Tuple.Create(Mesh.vertices, Mesh.triangles);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return Vector4.one;
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, transform.lossyScale);
|
||||
Gizmos.DrawWireMesh(Mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74b6655bbc8424b73ab3428b5ea018e6
|
||||
timeCreated: 1549902766
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjPlaneShape : IMjShape {
|
||||
public Vector2 Extents = Vector3.one;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
var extents = new Vector3(Extents.x, Extents.y, 1.0f);
|
||||
var scaledExtents = Vector3.Scale(extents, transform.lossyScale);
|
||||
mjcf.SetAttribute("size", MjEngineTool.MakeLocaleInvariant($"{scaledExtents.x} {scaledExtents.y} {scaledExtents.z}"));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
var components = mjcf.GetFloatArrayAttribute("size", defaultValue: new float[] { 1, 1, 1 });
|
||||
Extents = new Vector2(components[0], components[1]);
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildPlane(Extents.x * 2.0f, Extents.y * 2.0f);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return new Vector4(Extents.x, Extents.y, 1, 0);
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
|
||||
MjGizmos.DrawWirePlane(Vector3.zero, Extents.x * 2.0f, Extents.y * 2.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f4c670eb5b2f4a97826a6a22705ed4d
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// A facade that allows to select a type of Mujoco shape and define that shape's properties.
|
||||
//
|
||||
// To work around the lack of serialization support for polymorphism, the class aggregates
|
||||
// the settings of all available shape types. Coupled with a set of custom property drawers,
|
||||
// it will be presented in the Inspector as a single entity, encapsulating the details of
|
||||
// data persistance, Mjcf parsing and generation and debug visualization.
|
||||
public abstract class MjShapeComponent : MjComponent {
|
||||
|
||||
public enum ShapeTypes {
|
||||
Sphere,
|
||||
Capsule,
|
||||
Ellipsoid,
|
||||
Cylinder,
|
||||
Box,
|
||||
Plane,
|
||||
Mesh
|
||||
}
|
||||
|
||||
public ShapeTypes ShapeType;
|
||||
|
||||
public MjSphereShape Sphere = new MjSphereShape();
|
||||
public MjCapsuleShape Capsule = new MjCapsuleShape();
|
||||
public MjCylinderShape Cylinder = new MjCylinderShape();
|
||||
public MjBoxShape Box = new MjBoxShape();
|
||||
public MjEllipsoidShape Ellipsoid = new MjEllipsoidShape();
|
||||
public MjPlaneShape Plane = new MjPlaneShape();
|
||||
public MjMeshShape Mesh = new MjMeshShape();
|
||||
|
||||
public IMjShape Shape {
|
||||
get {
|
||||
switch (ShapeType) {
|
||||
default:
|
||||
case ShapeTypes.Sphere: return Sphere;
|
||||
case ShapeTypes.Capsule: return Capsule;
|
||||
case ShapeTypes.Cylinder: return Cylinder;
|
||||
case ShapeTypes.Ellipsoid: return Ellipsoid;
|
||||
case ShapeTypes.Box: return Box;
|
||||
case ShapeTypes.Plane: return Plane;
|
||||
case ShapeTypes.Mesh: return Mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ShapeToMjcf(XmlElement mjcf, Transform transform) {
|
||||
mjcf.SetAttribute("type", ShapeType.ToString().ToLower());
|
||||
Shape.ToMjcf(mjcf, transform);
|
||||
}
|
||||
|
||||
public void ShapeFromMjcf(XmlElement mjcf) {
|
||||
ShapeType = mjcf.GetEnumAttribute<ShapeTypes>(
|
||||
"type", ShapeTypes.Sphere, ignoreCase: true);
|
||||
Shape.FromMjcf(mjcf);
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return Shape.BuildMesh();
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
var stamp = Shape.GetChangeStamp();
|
||||
stamp.w = (float)ShapeType;
|
||||
return stamp;
|
||||
}
|
||||
|
||||
public void DrawGizmos(Transform transform) {
|
||||
var oldMatrix = Gizmos.matrix;
|
||||
Shape.DebugDraw(transform);
|
||||
Gizmos.matrix = oldMatrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c4948e0eae49455caa9c131435c0b5b
|
||||
timeCreated: 1547594327
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
// Defines a reference frame and an anchoring point for other elements of the rig.
|
||||
public class MjSite : MjShapeComponent {
|
||||
|
||||
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_SITE;
|
||||
|
||||
// Parse the component settings from an external Mjcf.
|
||||
protected override void OnParseMjcf(XmlElement mjcf) {
|
||||
ShapeFromMjcf(mjcf);
|
||||
MjEngineTool.ParseTransformMjcf(mjcf, transform);
|
||||
}
|
||||
|
||||
// Generate implementation specific XML element.
|
||||
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
|
||||
var mjcf = (XmlElement)doc.CreateElement("site");
|
||||
ShapeToMjcf(mjcf, transform);
|
||||
MjEngineTool.PositionRotationToMjcf(mjcf, this);
|
||||
return mjcf;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
public void OnDrawGizmosSelected() {
|
||||
Gizmos.color = Color.magenta;
|
||||
DrawGizmos(transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f92b229515774d928f13cd3faead49a
|
||||
timeCreated: 1547220273
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 DeepMind Technologies Limited
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mujoco {
|
||||
|
||||
[Serializable]
|
||||
public class MjSphereShape : IMjShape {
|
||||
public float Radius = 0.5f;
|
||||
private const float _scaleTolerance = 1e-6f;
|
||||
|
||||
public void ToMjcf(XmlElement mjcf, Transform transform) {
|
||||
if (Math.Abs(transform.lossyScale.x - transform.lossyScale.y) > _scaleTolerance ||
|
||||
Math.Abs(transform.lossyScale.y - transform.lossyScale.z) > _scaleTolerance) {
|
||||
Debug.LogWarning(
|
||||
$"{transform.name}: Sphere shapes work only with uniform scaling. Using the value of X" +
|
||||
" component.\n Consider using Ellipsoid shape if you want to use non-uniform scaling.",
|
||||
transform);
|
||||
}
|
||||
mjcf.SetAttribute("size", MjEngineTool.MakeLocaleInvariant($"{Radius * transform.lossyScale.x}"));
|
||||
}
|
||||
|
||||
public void FromMjcf(XmlElement mjcf) {
|
||||
var values = mjcf.GetFloatArrayAttribute("size", defaultValue: new float[] { 0.5f });
|
||||
Radius = values[0];
|
||||
}
|
||||
|
||||
public Tuple<Vector3[], int[]> BuildMesh() {
|
||||
return MeshGenerators.BuildSphere(scale: Vector3.one * Radius);
|
||||
}
|
||||
|
||||
public Vector4 GetChangeStamp() {
|
||||
return new Vector4(Radius, 1, 1, 0);
|
||||
}
|
||||
|
||||
public void DebugDraw(Transform transform) {
|
||||
Gizmos.DrawWireSphere(transform.position, Radius * transform.lossyScale.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: accf5ed21337c4fe38417b01fe8c5468
|
||||
timeCreated: 1547647081
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user