// import renderable objects private unsafe void ImportObjects(int nobject) { string cwd = Path.GetDirectoryName(modelFile); // make primitives PrimitiveType[] ptypes = { PrimitiveType.Plane, PrimitiveType.Sphere, PrimitiveType.Cylinder, PrimitiveType.Cube }; GameObject[] primitives = new GameObject[4]; for (int i = 0; i < 4; i++) { primitives[i] = GameObject.CreatePrimitive(ptypes[i]); } // allocate array objects = new GameObject[nobject]; // process objects for (int i = 0; i < nobject; i++) { // get object name StringBuilder name = new StringBuilder(100); MJP.GetObjectName(i, name, 100); // create new GameObject, place under root objects[i] = new GameObject(name.ToString()); MeshFilter filt = objects[i].AddComponent <MeshFilter>(); MeshRenderer rend = objects[i].AddComponent <MeshRenderer>(); InstancedColor colors = objects[i].AddComponent <InstancedColor>(); objects[i].transform.parent = root.transform; // get MuJoCo object descriptor MJP.TObject obj; MJP.GetObject(i, &obj); // For Furniture Assembly Environment: do not visualize site if (obj.category == (int)MJP.TCategory.SITE && !objects[i].name.Contains("conn")) { objects[i].layer = 9; } if (objects[i].name.StartsWith("noviz", StringComparison.Ordinal)) { objects[i].layer = 10; } if (objects[i].name.StartsWith("floor", StringComparison.Ordinal)) { objects[i].layer = 10; } // set mesh switch ((MJP.TGeom)obj.geomtype) { case MJP.TGeom.PLANE: filt.sharedMesh = primitives[0].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.SPHERE: filt.sharedMesh = primitives[1].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.CYLINDER: filt.sharedMesh = primitives[2].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.BOX: filt.sharedMesh = primitives[3].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.HFIELD: int nrow = obj.hfield_nrow; int ncol = obj.hfield_ncol; int r, c; // allocate Vector3[] hfvertices = new Vector3[nrow * ncol + 4 * nrow + 4 * ncol]; Vector2[] hfuv = new Vector2[nrow * ncol + 4 * nrow + 4 * ncol]; int[] hffaces0 = new int[3 * 2 * (nrow - 1) * (ncol - 1)]; int[] hffaces1 = new int[3 * (4 * (nrow - 1) + 4 * (ncol - 1))]; // vertices and uv: surface for (r = 0; r < nrow; r++) { for (c = 0; c < ncol; c++) { int k = r * ncol + c; float wc = c / (float)(ncol - 1); float wr = r / (float)(nrow - 1); hfvertices[k].Set(-(wc - 0.5f), obj.hfield_data[k], -(wr - 0.5f)); hfuv[k].Set(wc, wr); } } // vertices and uv: front and back for (r = 0; r < nrow; r += (nrow - 1)) { for (c = 0; c < ncol; c++) { int k = nrow * ncol + 2 * ((r > 0?ncol:0) + c); float wc = c / (float)(ncol - 1); float wr = r / (float)(nrow - 1); hfvertices[k].Set(-(wc - 0.5f), -0.5f, -(wr - 0.5f)); hfuv[k].Set(wc, 0); hfvertices[k + 1].Set(-(wc - 0.5f), obj.hfield_data[r * ncol + c], -(wr - 0.5f)); hfuv[k + 1].Set(wc, 1); } } // vertices and uv: left and right for (c = 0; c < ncol; c += (ncol - 1)) { for (r = 0; r < nrow; r++) { int k = nrow * ncol + 4 * ncol + 2 * ((c > 0?nrow:0) + r); float wc = c / (float)(ncol - 1); float wr = r / (float)(nrow - 1); hfvertices[k].Set(-(wc - 0.5f), -0.5f, -(wr - 0.5f)); hfuv[k].Set(wr, 0); hfvertices[k + 1].Set(-(wc - 0.5f), obj.hfield_data[r * ncol + c], -(wr - 0.5f)); hfuv[k + 1].Set(wr, 1); } } // faces: surface for (r = 0; r < nrow - 1; r++) { for (c = 0; c < ncol - 1; c++) { int f = r * (ncol - 1) + c; int k = r * ncol + c; // first face in rectangle hffaces0[3 * 2 * f] = k; hffaces0[3 * 2 * f + 2] = k + 1; hffaces0[3 * 2 * f + 1] = k + ncol + 1; // second face in rectangle hffaces0[3 * 2 * f + 3] = k; hffaces0[3 * 2 * f + 5] = k + ncol + 1; hffaces0[3 * 2 * f + 4] = k + ncol; } } // faces: front and back for (r = 0; r < 2; r++) { for (c = 0; c < ncol - 1; c++) { int f = ((r > 0?(ncol - 1):0) + c); int k = nrow * ncol + 2 * ((r > 0?ncol:0) + c); // first face in rectangle hffaces1[3 * 2 * f] = k; hffaces1[3 * 2 * f + 2] = k + (r > 0 ? 1 : 3); hffaces1[3 * 2 * f + 1] = k + (r > 0 ? 3 : 1); // second face in rectangle hffaces1[3 * 2 * f + 3] = k; hffaces1[3 * 2 * f + 5] = k + (r > 0 ? 3 : 2); hffaces1[3 * 2 * f + 4] = k + (r > 0 ? 2 : 3); } } // faces: left and right for (c = 0; c < 2; c++) { for (r = 0; r < nrow - 1; r++) { int f = 2 * (ncol - 1) + ((c > 0?(nrow - 1):0) + r); int k = nrow * ncol + 4 * ncol + 2 * ((c > 0?nrow:0) + r); // first face in rectangle hffaces1[3 * 2 * f] = k; hffaces1[3 * 2 * f + 2] = k + (c > 0 ? 3 : 1); hffaces1[3 * 2 * f + 1] = k + (c > 0 ? 1 : 3); // second face in rectangle hffaces1[3 * 2 * f + 3] = k; hffaces1[3 * 2 * f + 5] = k + (c > 0 ? 2 : 3); hffaces1[3 * 2 * f + 4] = k + (c > 0 ? 3 : 2); } } Debug.Log(ncol); Debug.Log(nrow); Debug.Log(Mathf.Min(hffaces1)); Debug.Log(Mathf.Max(hffaces1)); // create mesh with automatic normals and tangents filt.sharedMesh = new Mesh(); filt.sharedMesh.vertices = hfvertices; filt.sharedMesh.uv = hfuv; filt.sharedMesh.subMeshCount = 2; filt.sharedMesh.SetTriangles(hffaces0, 0); filt.sharedMesh.SetTriangles(hffaces1, 1); filt.sharedMesh.RecalculateNormals(); filt.sharedMesh.RecalculateTangents(); // set name StringBuilder hname = new StringBuilder(100); MJP.GetElementName(MJP.TElement.HFIELD, obj.dataid, hname, 100); filt.sharedMesh.name = hname.ToString(); break; case MJP.TGeom.CAPSULE: case MJP.TGeom.MESH: // reuse shared mesh from earlier object if (obj.mesh_shared >= 0) { filt.sharedMesh = objects[obj.mesh_shared].GetComponent <MeshFilter>().sharedMesh; } // create new mesh else { string meshName; // set name if ((MJP.TGeom)obj.geomtype == MJP.TGeom.CAPSULE) { meshName = "Capsule mesh"; } else { StringBuilder mname = new StringBuilder(100); MJP.GetElementName(MJP.TElement.MESH, obj.dataid, mname, 100); meshName = mname.ToString(); } { // copy vertices, normals, uv Vector3[] vertices = new Vector3[obj.mesh_nvertex]; Vector3[] normals = new Vector3[obj.mesh_nvertex]; Vector2[] uv = new Vector2[obj.mesh_nvertex]; for (int k = 0; k < obj.mesh_nvertex; k++) { vertices[k].Set(-obj.mesh_position[3 * k], obj.mesh_position[3 * k + 2], -obj.mesh_position[3 * k + 1]); normals[k].Set(-obj.mesh_normal[3 * k], obj.mesh_normal[3 * k + 2], -obj.mesh_normal[3 * k + 1]); uv[k].Set(obj.mesh_texcoord[2 * k], obj.mesh_texcoord[2 * k + 1]); } // copy faces int[] faces = new int[3 * obj.mesh_nface]; for (int k = 0; k < obj.mesh_nface; k++) { faces[3 * k] = obj.mesh_face[3 * k]; faces[3 * k + 1] = obj.mesh_face[3 * k + 2]; faces[3 * k + 2] = obj.mesh_face[3 * k + 1]; } // number of verices can be modified by uncompressed mesh int nvert = obj.mesh_nvertex; // replace with uncompressed mesh when UV needs to be recomputed // ( recomputeUV && (MJP.TGeom)obj.geomtype==MJP.TGeom.MESH ) { // make temporary mesh Mesh temp = new Mesh(); temp.vertices = vertices; temp.normals = normals; temp.triangles = faces; // generate uncompressed UV unwrapping /* Vector2[] UV = Unwrapping.GeneratePerTriangleUV(temp); * int N = UV.GetLength(0)/3; * if( N!=obj.mesh_nface ) * throw new System.Exception("Unexpected number of faces"); * nvert = 3*N;*/ int N = obj.mesh_nface; nvert = 3 * N; // create corresponding uncompressed vertices, normals, faces Vector3[] Vertex = new Vector3[3 * N]; Vector3[] Normal = new Vector3[3 * N]; int[] Face = new int[3 * N]; for (int k = 0; k < N; k++) { Vertex[3 * k] = vertices[faces[3 * k]]; Vertex[3 * k + 1] = vertices[faces[3 * k + 1]]; Vertex[3 * k + 2] = vertices[faces[3 * k + 2]]; Normal[3 * k] = normals[faces[3 * k]]; Normal[3 * k + 1] = normals[faces[3 * k + 1]]; Normal[3 * k + 2] = normals[faces[3 * k + 2]]; Face[3 * k] = 3 * k; Face[3 * k + 1] = 3 * k + 1; Face[3 * k + 2] = 3 * k + 2; } // create uncompressed mesh filt.sharedMesh = new Mesh(); filt.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; filt.sharedMesh.vertices = Vertex; filt.sharedMesh.normals = Normal; filt.sharedMesh.triangles = Face; filt.sharedMesh.name = meshName; // filt.sharedMesh.uv = UV; } // otherwise create mesh directly /* else * { * filt.sharedMesh = new Mesh(); * filt.sharedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; * filt.sharedMesh.vertices = vertices; * filt.sharedMesh.normals = normals; * filt.sharedMesh.triangles = faces; * filt.sharedMesh.uv = uv; * filt.sharedMesh.name = meshName; * filt.sharedMesh.RecalculateNormals(30); * }*/ // optionally recompute normals for meshes /* if (recomputeNormal && (MJP.TGeom)obj.geomtype == MJP.TGeom.MESH) * filt.sharedMesh.RecalculateNormals(60);*/ filt.sharedMesh.RecalculateNormals(25); // always calculate tangents (MuJoCo does not support tangents) filt.sharedMesh.RecalculateTangents(); } // print error if number of vertices or faces is over 65535 /* if( obj.mesh_nface>65535 || nvert>65535 ) * Debug.LogError("MESH TOO BIG: " + filt.sharedMesh.name + * ", vertices " + nvert + ", faces " + obj.mesh_nface);*/ } break; } //TODO: Set segmentation color with Material.SetColor("_SegColor") // existing material byte segmentation = 0; // Try to get segmentation id. If nothing is found, the object will remain background (0) if (meshSegmentMap.TryGetValue(name.ToString(), out segmentation) == false) { meshSegmentMap.TryGetValue(filt.sharedMesh.name, out segmentation); } Material base_material = null; if (materials.TryGetValue(obj.material, out base_material)) { rend.sharedMaterial = base_material; colors.Diffuse = new Color(obj.color[0], obj.color[1], obj.color[2], obj.color[3]); colors.Segmentation = new Color32(segmentation, segmentation, segmentation, segmentation); } else // Missing material (shouldn't be possible?) { Debug.Log("Couldn't find a Material for id:" + obj.material); } // get MuJoCo object transform and set in Unity MJP.TTransform transform; int visible; int selected; MJP.GetObjectState(i, &transform, &visible, &selected); SetTransform(objects[i], transform); } // delete primitives for (int i = 0; i < 4; i++) { Destroy(primitives[i]); } }
// import renderable objects private unsafe void ImportObjects(int nobject) { // make primitives PrimitiveType[] ptypes = { PrimitiveType.Plane, PrimitiveType.Sphere, PrimitiveType.Cylinder, PrimitiveType.Cube }; GameObject[] primitives = new GameObject[4]; for (int i = 0; i < 4; i++) { primitives[i] = GameObject.CreatePrimitive(ptypes[i]); } // allocate array objects = new GameObject[nobject]; // process objects for (int i = 0; i < nobject; i++) { // get object name StringBuilder name = new StringBuilder(100); MJP.GetObjectName(i, name, 100); // create new GameObject, place under root objects[i] = new GameObject(name.ToString()); objects[i].AddComponent <MeshFilter>(); objects[i].AddComponent <MeshRenderer>(); objects[i].transform.parent = root.transform; // get components MeshFilter filt = objects[i].GetComponent <MeshFilter>(); MeshRenderer rend = objects[i].GetComponent <MeshRenderer>(); // get MuJoCo object descriptor MJP.TObject obj; MJP.GetObject(i, &obj); // set mesh switch ((MJP.TGeom)obj.geomtype) { case MJP.TGeom.PLANE: filt.sharedMesh = primitives[0].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.SPHERE: filt.sharedMesh = primitives[1].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.CYLINDER: filt.sharedMesh = primitives[2].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.BOX: filt.sharedMesh = primitives[3].GetComponent <MeshFilter>().sharedMesh; break; case MJP.TGeom.HFIELD: int nrow = obj.hfield_nrow; int ncol = obj.hfield_ncol; int r, c; // allocate Vector3[] hfvertices = new Vector3[nrow * ncol + 4 * nrow + 4 * ncol]; Vector2[] hfuv = new Vector2[nrow * ncol + 4 * nrow + 4 * ncol]; int[] hffaces0 = new int[3 * 2 * (nrow - 1) * (ncol - 1)]; int[] hffaces1 = new int[3 * (4 * (nrow - 1) + 4 * (ncol - 1))]; // vertices and uv: surface for (r = 0; r < nrow; r++) { for (c = 0; c < ncol; c++) { int k = r * ncol + c; float wc = c / (float)(ncol - 1); float wr = r / (float)(nrow - 1); hfvertices[k].Set(-(wc - 0.5f), obj.hfield_data[k], -(wr - 0.5f)); hfuv[k].Set(wc, wr); } } // vertices and uv: front and back for (r = 0; r < nrow; r += (nrow - 1)) { for (c = 0; c < ncol; c++) { int k = nrow * ncol + 2 * ((r > 0?ncol:0) + c); float wc = c / (float)(ncol - 1); float wr = r / (float)(nrow - 1); hfvertices[k].Set(-(wc - 0.5f), -0.5f, -(wr - 0.5f)); hfuv[k].Set(wc, 0); hfvertices[k + 1].Set(-(wc - 0.5f), obj.hfield_data[r * ncol + c], -(wr - 0.5f)); hfuv[k + 1].Set(wc, 1); } } // vertices and uv: left and right for (c = 0; c < ncol; c += (ncol - 1)) { for (r = 0; r < nrow; r++) { int k = nrow * ncol + 4 * ncol + 2 * ((c > 0?nrow:0) + r); float wc = c / (float)(ncol - 1); float wr = r / (float)(nrow - 1); hfvertices[k].Set(-(wc - 0.5f), -0.5f, -(wr - 0.5f)); hfuv[k].Set(wr, 0); hfvertices[k + 1].Set(-(wc - 0.5f), obj.hfield_data[r * ncol + c], -(wr - 0.5f)); hfuv[k + 1].Set(wr, 1); } } // faces: surface for (r = 0; r < nrow - 1; r++) { for (c = 0; c < ncol - 1; c++) { int f = r * (ncol - 1) + c; int k = r * ncol + c; // first face in rectangle hffaces0[3 * 2 * f] = k; hffaces0[3 * 2 * f + 2] = k + 1; hffaces0[3 * 2 * f + 1] = k + ncol + 1; // second face in rectangle hffaces0[3 * 2 * f + 3] = k; hffaces0[3 * 2 * f + 5] = k + ncol + 1; hffaces0[3 * 2 * f + 4] = k + ncol; } } // faces: front and back for (r = 0; r < 2; r++) { for (c = 0; c < ncol - 1; c++) { int f = ((r > 0?(ncol - 1):0) + c); int k = nrow * ncol + 2 * ((r > 0?ncol:0) + c); // first face in rectangle hffaces1[3 * 2 * f] = k; hffaces1[3 * 2 * f + 2] = k + (r > 0 ? 1 : 3); hffaces1[3 * 2 * f + 1] = k + (r > 0 ? 3 : 1); // second face in rectangle hffaces1[3 * 2 * f + 3] = k; hffaces1[3 * 2 * f + 5] = k + (r > 0 ? 3 : 2); hffaces1[3 * 2 * f + 4] = k + (r > 0 ? 2 : 3); } } // faces: left and right for (c = 0; c < 2; c++) { for (r = 0; r < nrow - 1; r++) { int f = 2 * (ncol - 1) + ((c > 0?(nrow - 1):0) + r); int k = nrow * ncol + 4 * ncol + 2 * ((c > 0?nrow:0) + r); // first face in rectangle hffaces1[3 * 2 * f] = k; hffaces1[3 * 2 * f + 2] = k + (c > 0 ? 3 : 1); hffaces1[3 * 2 * f + 1] = k + (c > 0 ? 1 : 3); // second face in rectangle hffaces1[3 * 2 * f + 3] = k; hffaces1[3 * 2 * f + 5] = k + (c > 0 ? 2 : 3); hffaces1[3 * 2 * f + 4] = k + (c > 0 ? 3 : 2); } } Debug.Log(ncol); Debug.Log(nrow); Debug.Log(Mathf.Min(hffaces1)); Debug.Log(Mathf.Max(hffaces1)); // create mesh with automatic normals and tangents filt.sharedMesh = new Mesh(); filt.sharedMesh.vertices = hfvertices; filt.sharedMesh.uv = hfuv; filt.sharedMesh.subMeshCount = 2; filt.sharedMesh.SetTriangles(hffaces0, 0); filt.sharedMesh.SetTriangles(hffaces1, 1); filt.sharedMesh.RecalculateNormals(); filt.sharedMesh.RecalculateTangents(); // set name StringBuilder hname = new StringBuilder(100); MJP.GetElementName(MJP.TElement.HFIELD, obj.dataid, hname, 100); filt.sharedMesh.name = hname.ToString(); break; case MJP.TGeom.CAPSULE: case MJP.TGeom.MESH: // reuse shared mesh from earlier object if (obj.mesh_shared >= 0) { filt.sharedMesh = objects[obj.mesh_shared].GetComponent <MeshFilter>().sharedMesh; } // create new mesh else { // copy vertices, normals, uv Vector3[] vertices = new Vector3[obj.mesh_nvertex]; Vector3[] normals = new Vector3[obj.mesh_nvertex]; Vector2[] uv = new Vector2[obj.mesh_nvertex]; for (int k = 0; k < obj.mesh_nvertex; k++) { vertices[k].Set(-obj.mesh_position[3 * k], obj.mesh_position[3 * k + 2], -obj.mesh_position[3 * k + 1]); normals[k].Set(-obj.mesh_normal[3 * k], obj.mesh_normal[3 * k + 2], -obj.mesh_normal[3 * k + 1]); uv[k].Set(obj.mesh_texcoord[2 * k], obj.mesh_texcoord[2 * k + 1]); } // copy faces int[] faces = new int[3 * obj.mesh_nface]; for (int k = 0; k < obj.mesh_nface; k++) { faces[3 * k] = obj.mesh_face[3 * k]; faces[3 * k + 1] = obj.mesh_face[3 * k + 2]; faces[3 * k + 2] = obj.mesh_face[3 * k + 1]; } // number of verices can be modified by uncompressed mesh int nvert = obj.mesh_nvertex; // replace with uncompressed mesh when UV needs to be recomputed if (recomputeUV && (MJP.TGeom)obj.geomtype == MJP.TGeom.MESH) { // make temporary mesh Mesh temp = new Mesh(); temp.vertices = vertices; temp.normals = normals; temp.triangles = faces; // generate uncompressed UV unwrapping Vector2[] UV = Unwrapping.GeneratePerTriangleUV(temp); int N = UV.GetLength(0) / 3; if (N != obj.mesh_nface) { throw new System.Exception("Unexpected number of faces"); } nvert = 3 * N; // create corresponding uncompressed vertices, normals, faces Vector3[] Vertex = new Vector3[3 * N]; Vector3[] Normal = new Vector3[3 * N]; int[] Face = new int[3 * N]; for (int k = 0; k < N; k++) { Vertex[3 * k] = vertices[faces[3 * k]]; Vertex[3 * k + 1] = vertices[faces[3 * k + 1]]; Vertex[3 * k + 2] = vertices[faces[3 * k + 2]]; Normal[3 * k] = normals[faces[3 * k]]; Normal[3 * k + 1] = normals[faces[3 * k + 1]]; Normal[3 * k + 2] = normals[faces[3 * k + 2]]; Face[3 * k] = 3 * k; Face[3 * k + 1] = 3 * k + 1; Face[3 * k + 2] = 3 * k + 2; } // create uncompressed mesh filt.sharedMesh = new Mesh(); filt.sharedMesh.vertices = Vertex; filt.sharedMesh.normals = Normal; filt.sharedMesh.triangles = Face; filt.sharedMesh.uv = UV; } // otherwise create mesh directly else { filt.sharedMesh = new Mesh(); filt.sharedMesh.vertices = vertices; filt.sharedMesh.normals = normals; filt.sharedMesh.triangles = faces; filt.sharedMesh.uv = uv; } // optionally recompute normals for meshes if (recomputeNormal && (MJP.TGeom)obj.geomtype == MJP.TGeom.MESH) { filt.sharedMesh.RecalculateNormals(); } // always calculate tangents (MuJoCo does not support tangents) filt.sharedMesh.RecalculateTangents(); // set name if ((MJP.TGeom)obj.geomtype == MJP.TGeom.CAPSULE) { filt.sharedMesh.name = "Capsule mesh"; } else { StringBuilder mname = new StringBuilder(100); MJP.GetElementName(MJP.TElement.MESH, obj.dataid, mname, 100); filt.sharedMesh.name = mname.ToString(); } // print error if number of vertices or faces is over 65535 if (obj.mesh_nface > 65535 || nvert > 65535) { Debug.LogError("MESH TOO BIG: " + filt.sharedMesh.name + ", vertices " + nvert + ", faces " + obj.mesh_nface); } } break; } // existing material if (obj.material >= 0) { // not modified if (obj.color[0] == 0.5f && obj.color[1] == 0.5f && obj.color[2] == 0.5f && obj.color[3] == 1) { rend.sharedMaterial = materials[obj.material]; } // color override else { rend.sharedMaterial = new Material(materials[obj.material]); AdjustMaterial(rend.sharedMaterial, obj.color[0], obj.color[1], obj.color[2], obj.color[3]); } } // new material else { rend.sharedMaterial = new Material(Shader.Find("Standard")); AdjustMaterial(rend.sharedMaterial, obj.color[0], obj.color[1], obj.color[2], obj.color[3]); } // get MuJoCo object transform and set in Unity MJP.TTransform transform; int visible; int selected; MJP.GetObjectState(i, &transform, &visible, &selected); SetTransform(objects[i], transform); } // delete primitives for (int i = 0; i < 4; i++) { DestroyImmediate(primitives[i]); } AssetDatabase.Refresh(); }