Search code examples
unity-game-enginespatialmeshhololensmrtk

MRTK 2.4 - Save Spatial Mesh on Runtime


Right now I'm trying to save the spatial mesh on my hololens as an obj-file. My problem is that, when trying to open the obj-file e.g. in Blender (or 3D Viewer) I get an error that says IndexError: List index out of range. So I guess I'm giving my triangles the wrong index (in the last part of MeshToString()), but I don't know how to do it the right way.

What works: For example if I take only one mesh out of the first meshfilter and cut out + lastFaceIndex in the MeshToString() it works fine. But thats only a part of my room. And I want the whole mesh from my room, so I have to go through all the mesh filters and get my mesh and then write them into an obj-file, but I dont know how to give the triangles the right index.

Also I noticed that if I download the mesh via device portal it has ~4.8MB, but the mesh I'm downloading via my unity application has only 1.4MB.

My Setup:
Unity 2019.3.14 MRTK 2.4 HoloLens 2nd Gen

GetSpatialMesh():

private void GetSpatialMesh()
{
    if (_observer == null)
        return;

    List<Mesh> meshes = new List<Mesh>();
    // Loop through all known Meshes
    foreach (SpatialAwarenessMeshObject meshObject in _observer.Meshes.Values)
        meshes.Add(meshObject.Filter.mesh);
    WriteMeshToFile("MyMesh.obj", meshes);
}

WriteMeshToFile():

public static void WriteMeshToFile(string fileName, IEnumerable<Mesh> meshes)
{
    string path = Path.Combine(Application.persistentDataPath, fileName);
    using (var file = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write))
    {
        faceCount = 0;
        using (var writer = new StreamWriter(file, Encoding.UTF8))
        {
            int o = 0;
            foreach (Mesh mesh in meshes)
            {
                o++;
                writer.WriteLine("o Object." + o);
                writer.Write(MeshToString(mesh, faceCount));
                writer.WriteLine("");
            }
        }
    }
}

MeshToString():

public static string MeshToString(Mesh m, int lastFaceIndex = 0)
{
    StringBuilder sb = new StringBuilder();

    foreach (Vector3 v in m.vertices)
    {
        sb.Append(string.Format("v {0} {1} {2}\n", v.x, v.y, v.z));
    }
    sb.Append("\n");
    foreach (Vector3 v in m.normals)
    {
        sb.Append(string.Format("vn {0} {1} {2}\n", v.x, v.y, v.z));
    }
    sb.Append("\n");
    foreach (Vector3 v in m.uv)
    {
        sb.Append(string.Format("vt {0} {1}\n", v.x, v.y));
    }
    for (int material = 0; material < m.subMeshCount; material++)
    {
        int[] triangles = m.GetTriangles(material);
        for (int i = 0; i < triangles.Length; i += 3)
        {
            faceCount += 3;
            sb.Append(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n",
                triangles[i] + 1 + lastFaceIndex, triangles[i + 1] + 1 + lastFaceIndex, triangles[i + 2] + 1 + lastFaceIndex));
        }
    }
    return sb.ToString();
}

Solution:
The scene understanding sample project didnt work for me, so I just got the sdk via nuget manager in unity. I used the code from SceneUnderstandingManager(which is part of the sample project), referenced by Hernando-MSFT in his answer below.


Solution

  • I noticed that the code you posted is based on Legacy HoloToolkit but all support will now be with the newer Scene understanding SDK. Since you are using HoloLens2, we recommend you using Scene understanding SDK to query a static version of the spatial mapping data and save a serialized scene bytes to disk. After scanning the room, invoke SceneObserver.ComputeSerializedAsync to serialize the scene as a byte array.

    Microsoft.MixedReality.SceneUnderstanding.Samples is a Unity-based sample application that showcases Scene Understanding on HoloLens 2. And it shows up how to save any scene you've captured by saving the output of ComputeSerializedAsync to file: Line1154

    Besides, the SaveObjsToDiskAsync function show how to save the Unity Objects from Scene Understanding as Obj files: Line1206