首页 > 解决方案 > MRTK 2.4 - 在运行时保存空间网格

问题描述

现在我正在尝试将我的 hololens 上的空间网格保存为 obj 文件。我的问题是,当尝试在 Blender(或 3D 查看器)中打开 obj 文件时,我收到一条错误消息IndexError: List index out of range。所以我想我给我的三角形提供了错误的索引(在最后一部分MeshToString()),但我不知道如何以正确的方式去做。

什么有效:例如,如果我只从第一个网格过滤器中取出一个网格并切掉它+ lastFaceIndexMeshToString()它就可以正常工作。但这只是我房间的一部分。我想要房间里的整个网格,所以我必须遍历所有网格过滤器并获取我的网格,然后将它们写入一个 obj 文件,但我不知道如何为三角形提供正确的索引。

我还注意到,如果我通过设备门户下载网格,它大约有 4.8MB,但我通过统一应用程序下载的网格只有 1.4MB。

我的设置:
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();
}

解决方案:
场景理解示例项目对我不起作用,所以我只是通过统一的nuget manager获得了sdk。我使用了来自SceneUnderstandingManager的代码(它是示例项目的一部分),Hernando-MSFT 在下面的回答中引用了该代码。

标签: unity3dspatialmeshhololensmrtk

解决方案


嗨,我知道这有点老了,Perazim 已经解决了他的问题,但我还需要将 hololens 2 Mesh 转换为 obj,所以......也许它将来对其他人有用:),这就是为什么我我提出了我的解决方案。

不幸的是,我没有设法将代码与场景理解一起使用,正如 Hernando 所提出的那样(免责声明这是我第一次尝试对 Hololens 进行编程,所以......我很确定这是我的错)。当我尝试打电话时

SceneBuffer serializedScene = SceneObserver.ComputeSerializedAsync(querySettings, 10.0f).GetAwaiter().GetResult();

我遇到了一个错误,实际上我的应用程序不再工作了!

我所做和工作的是“手动”创建 obj,对我有用的功能如下:

    private void exportScene(String targetFileName)
    {
        var spatialAwarenessSystem = CoreServices.SpatialAwarenessSystem;
        spatialAwarenessSystem.SuspendObservers();

        var observer = CoreServices.GetSpatialAwarenessSystemDataProvider<IMixedRealitySpatialAwarenessMeshObserver>();
        System.Text.StringBuilder objFileContent = new System.Text.StringBuilder(4096);
        objFileContent.Append("# Automatic export of Hololens 2 scanned mesh scan. \r\n\r\n");

        // Collects the spread objects
        foreach (SpatialAwarenessMeshObject meshObject in observer.Meshes.Values)
        {
            meshes.Add(meshObject.Filter);
        }

        int adjust = 1;
        MeshFilter[] mf = meshes.ToArray();
        int countVertex = 0;

        // for each object gets the vertexes normals and faces
        for (int i = 0; i < mf.Length; i++)
        {
            Mesh m = mf[i].sharedMesh;
            objFileContent.Append("o Object." + (i + 1) + "\r\n");
            
            // inverts x as the coordinates are different from the ones of regular obj
            foreach (Vector3 v in m.vertices)
            {
                float x = -v.x;
                float z = v.z;
                objFileContent.Append("v " + x.ToString("0.000000", CultureInfo.InvariantCulture) + " " + v.y.ToString("0.000000", CultureInfo.InvariantCulture) + " " + z.ToString("0.000000", CultureInfo.InvariantCulture) + "\r\n");
            }

            objFileContent.Append("\r\n\r\n");

            foreach (Vector3 n in m.normals)
            {
                float x = n.x;
                float z = n.z;
                objFileContent.Append("vn " + x.ToString("0.000000", CultureInfo.InvariantCulture) + " " + n.y.ToString("0.000000", CultureInfo.InvariantCulture) + " " + z.ToString("0.000000", CultureInfo.InvariantCulture) + "\r\n");
            }
            objFileContent.Append("\r\n\r\n");

            // the count of the faces starts with 1 and is cumulative for 
            // all objects on the scene this is why it is add 
            // adjust + countVertex as the id of the vertexes
            for (int ti = 0; ti < m.triangles.Length; ti += 3)
            {
                objFileContent.Append("f " + (m.triangles[ti] + adjust + countVertex ) + "//" + (m.triangles[ti] + adjust + countVertex) + " " + (m.triangles[ti + 1] + adjust + countVertex) + "//" + (m.triangles[ti + 1] + adjust + countVertex) + " " + (m.triangles[ti + 2] + adjust + countVertex) + "//" + (m.triangles[ti + 2] + adjust + countVertex) + "\r\n");
            }
            objFileContent.Append("\r\n\r\n");
            countVertex += m.vertexCount;
      }
       // string objPath = Path.Combine(sceneFolderPath, sceneName + ".obj"); 
        using (StreamWriter sw = File.AppendText(targetFileName))
        {
            sw.WriteLine(objFileContent.ToString());
        }
    }

推荐阅读