Add heightfield geom type with Unity terrain, import not yet supported
This commit is contained in:
@@ -30,7 +30,7 @@ public class MjShapeComponentEditor : Editor {
|
|||||||
var shapeType = serializedObject.FindProperty("ShapeType");
|
var shapeType = serializedObject.FindProperty("ShapeType");
|
||||||
EditorGUILayout.PropertyField(shapeType);
|
EditorGUILayout.PropertyField(shapeType);
|
||||||
EditorGUILayout.PropertyField(
|
EditorGUILayout.PropertyField(
|
||||||
serializedObject.FindProperty($"{shapeType.enumDisplayNames[shapeType.enumValueIndex]}"),
|
serializedObject.FindProperty($"{shapeType.enumNames[shapeType.enumValueIndex]}"),
|
||||||
includeChildren: true);
|
includeChildren: true);
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2019 DeepMind Technologies Limited
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Mujoco
|
||||||
|
{
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class MjHeightFieldShape : IMjShape
|
||||||
|
{
|
||||||
|
|
||||||
|
public Terrain terrain;
|
||||||
|
|
||||||
|
[Tooltip("The path, relative to Application.dataPath, where the heightmap data will be save in PNG format.")]
|
||||||
|
public string heightMapExportPath;
|
||||||
|
|
||||||
|
public string FullHeightMapPath => Path.GetFullPath(Path.Combine(Application.dataPath, heightMapExportPath));
|
||||||
|
public int HeightMapWidth => terrain.terrainData.heightmapTexture.width;
|
||||||
|
public int HeightMapHeight => terrain.terrainData.heightmapTexture.height;
|
||||||
|
public Vector3 HeightMapScale => terrain.terrainData.heightmapScale;
|
||||||
|
|
||||||
|
public void ToMjcf(XmlElement mjcf, Transform transform)
|
||||||
|
{
|
||||||
|
ExportHeightMap();
|
||||||
|
var scene = MjScene.Instance;
|
||||||
|
var assetName = scene.GenerationContext.AddHeightFieldAsset(this);
|
||||||
|
mjcf.SetAttribute("hfield", assetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FromMjcf(XmlElement mjcf)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExportHeightMap()
|
||||||
|
{
|
||||||
|
RenderTexture.active = terrain.terrainData.heightmapTexture;
|
||||||
|
Texture2D texture = new Texture2D(RenderTexture.active.width, RenderTexture.active.height);
|
||||||
|
texture.ReadPixels(new Rect(0, 0, RenderTexture.active.width, RenderTexture.active.height), 0, 0);
|
||||||
|
RenderTexture.active = null;
|
||||||
|
File.WriteAllBytes(FullHeightMapPath, texture.EncodeToPNG());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vector4 GetChangeStamp()
|
||||||
|
{
|
||||||
|
return Vector4.one;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tuple<Vector3[], int[]> BuildMesh()
|
||||||
|
{
|
||||||
|
return Tuple.Create(new Vector3[]{}, new int[]{});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DebugDraw(Transform transform)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4572cd1b5f036db4483a214346587960
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -34,7 +34,8 @@ public abstract class MjShapeComponent : MjComponent {
|
|||||||
Cylinder,
|
Cylinder,
|
||||||
Box,
|
Box,
|
||||||
Plane,
|
Plane,
|
||||||
Mesh
|
Mesh,
|
||||||
|
HField
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShapeTypes ShapeType;
|
public ShapeTypes ShapeType;
|
||||||
@@ -46,6 +47,7 @@ public abstract class MjShapeComponent : MjComponent {
|
|||||||
public MjEllipsoidShape Ellipsoid = new MjEllipsoidShape();
|
public MjEllipsoidShape Ellipsoid = new MjEllipsoidShape();
|
||||||
public MjPlaneShape Plane = new MjPlaneShape();
|
public MjPlaneShape Plane = new MjPlaneShape();
|
||||||
public MjMeshShape Mesh = new MjMeshShape();
|
public MjMeshShape Mesh = new MjMeshShape();
|
||||||
|
public MjHeightFieldShape HField = new MjHeightFieldShape();
|
||||||
|
|
||||||
public IMjShape Shape {
|
public IMjShape Shape {
|
||||||
get {
|
get {
|
||||||
@@ -58,6 +60,7 @@ public abstract class MjShapeComponent : MjComponent {
|
|||||||
case ShapeTypes.Box: return Box;
|
case ShapeTypes.Box: return Box;
|
||||||
case ShapeTypes.Plane: return Plane;
|
case ShapeTypes.Plane: return Plane;
|
||||||
case ShapeTypes.Mesh: return Mesh;
|
case ShapeTypes.Mesh: return Mesh;
|
||||||
|
case ShapeTypes.HField: return HField;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class MjcfGenerationContext {
|
|||||||
private int _nuserSensor;
|
private int _nuserSensor;
|
||||||
private int _numGeneratedNames = 0;
|
private int _numGeneratedNames = 0;
|
||||||
private Dictionary<Mesh, string> _meshAssets = new Dictionary<Mesh, string>();
|
private Dictionary<Mesh, string> _meshAssets = new Dictionary<Mesh, string>();
|
||||||
|
private Dictionary<MjHeightFieldShape, string> _hFieldAssets = new Dictionary<MjHeightFieldShape, string>();
|
||||||
|
|
||||||
public void GenerateMjcf(XmlElement mjcf) {
|
public void GenerateMjcf(XmlElement mjcf) {
|
||||||
GenerateConfigurationMjcf(mjcf);
|
GenerateConfigurationMjcf(mjcf);
|
||||||
@@ -51,6 +52,17 @@ public class MjcfGenerationContext {
|
|||||||
return _meshAssets[mesh];
|
return _meshAssets[mesh];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds a mesh to the list of assets that will be added to the generated MJCF.
|
||||||
|
// Returns the unique name that identifies this new asset.
|
||||||
|
public string AddHeightFieldAsset(MjHeightFieldShape hField) {
|
||||||
|
if (!_hFieldAssets.ContainsKey(hField)) {
|
||||||
|
var uniqueName = $"hfield_{_numGeneratedNames}";
|
||||||
|
_numGeneratedNames++;
|
||||||
|
_hFieldAssets.Add(hField, uniqueName);
|
||||||
|
}
|
||||||
|
return _hFieldAssets[hField];
|
||||||
|
}
|
||||||
|
|
||||||
public string GenerateName(Component comp) {
|
public string GenerateName(Component comp) {
|
||||||
var settings = MjGlobalSettings.Instance;
|
var settings = MjGlobalSettings.Instance;
|
||||||
if (settings?.UseRawGameObjectNames ?? false) {
|
if (settings?.UseRawGameObjectNames ?? false) {
|
||||||
@@ -81,10 +93,15 @@ public class MjcfGenerationContext {
|
|||||||
private void GenerateAssetsMjcf(XmlElement mjcf) {
|
private void GenerateAssetsMjcf(XmlElement mjcf) {
|
||||||
var doc = mjcf.OwnerDocument;
|
var doc = mjcf.OwnerDocument;
|
||||||
var assetMjcf = (XmlElement)mjcf.AppendChild(doc.CreateElement("asset"));
|
var assetMjcf = (XmlElement)mjcf.AppendChild(doc.CreateElement("asset"));
|
||||||
foreach (var asset in _meshAssets) {
|
foreach (var meshAsset in _meshAssets) {
|
||||||
var meshMjcf = (XmlElement)assetMjcf.AppendChild(doc.CreateElement("mesh"));
|
var meshMjcf = (XmlElement)assetMjcf.AppendChild(doc.CreateElement("mesh"));
|
||||||
meshMjcf.SetAttribute("name", asset.Value);
|
meshMjcf.SetAttribute("name", meshAsset.Value);
|
||||||
GenerateMeshMjcf(asset.Key, meshMjcf);
|
GenerateMeshMjcf(meshAsset.Key, meshMjcf);
|
||||||
|
}
|
||||||
|
foreach (var hFieldAsset in _hFieldAssets) {
|
||||||
|
var hFieldMjcf = (XmlElement)assetMjcf.AppendChild(doc.CreateElement("hfield"));
|
||||||
|
hFieldMjcf.SetAttribute("name", hFieldAsset.Value);
|
||||||
|
GenerateHeightFieldMjcf(hFieldAsset.Key, hFieldMjcf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,5 +114,15 @@ public class MjcfGenerationContext {
|
|||||||
}
|
}
|
||||||
mjcf.SetAttribute("vertex", vertexPositionsStr.ToString());
|
mjcf.SetAttribute("vertex", vertexPositionsStr.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void GenerateHeightFieldMjcf(MjHeightFieldShape hFieldComponent, XmlElement mjcf) {
|
||||||
|
mjcf.SetAttribute("nrow", "0");
|
||||||
|
mjcf.SetAttribute("ncol", "0");
|
||||||
|
mjcf.SetAttribute("content_type", "image/png");
|
||||||
|
mjcf.SetAttribute("file", hFieldComponent.FullHeightMapPath);
|
||||||
|
|
||||||
|
mjcf.SetAttribute("size", MjEngineTool.MakeLocaleInvariant($"{hFieldComponent.HeightMapScale.x*hFieldComponent.HeightMapHeight/2} {hFieldComponent.HeightMapScale.z * hFieldComponent.HeightMapWidth/2} {hFieldComponent.HeightMapScale.y*2} {hFieldComponent.terrain.transform.position.y}"));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user