【问题标题】:MRTK 2.4 - Save Spatial Mesh on RuntimeMRTK 2.4 - 在运行时保存空间网格
【发布时间】:2020-12-01 04:25:10
【问题描述】:

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

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

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

我的设置:
团结 2019.3.14 机读旅行证件 2.4 HoloLens 第二代

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();
}

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

【问题讨论】:

  • Hololens 上的persistentDataPath() 保存到什么位置?
  • Windows 设备门户 → 文件资源管理器 → 本地应用程序数据 → YourApp → LocalState。我花了大约 2 秒钟的时间来谷歌:appzinside.com/2018/08/06/…

标签: unity3d spatial mesh hololens mrtk


【解决方案1】:

嗨,我知道这有点老了,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());
        }
    }

【讨论】:

    【解决方案2】:

    我注意到您发布的代码基于Legacy HoloToolkit,但现在所有支持都将使用较新的Scene understanding SDK。由于您使用的是 HoloLens2,我们建议您使用场景理解 SDK 来查询静态版本的空间映射数据并将序列化的场景字节保存到磁盘。 扫描房间后,调用SceneObserver.ComputeSerializedAsync 将场景序列化为字节数组。

    Microsoft.MixedReality.SceneUnderstanding.Samples 是一个基于 Unity 的示例应用程序,展示了 HoloLens 2 上的场景理解。它显示了如何通过将 ComputeSerializedAsync 的输出保存到文件中来保存您捕获的任何场景:Line1154

    此外,SaveObjsToDiskAsync 函数显示了如何将场景理解中的 Unity 对象保存为 Obj 文件:Line1206

    【讨论】:

    • 我正在尝试运行this,但我只得到这种errors。奇怪的是,他们说这是由独立到 uwp 和返回之间切换引起的。但是我没有切换回来,我只是切换到了uwp。修复该错误的方法也不起作用。 'CDiaz-MS' 写道 '这个问题可以通过在切换回 Standalone 后删除 Dependencies 文件夹来解决'。我删除了“Assets\Packages”下的所有内容,但没有解决问题。
    • 该示例针对 Unity 2019.2.21f1,我验证它运行良好。而且我的 PC 上没有安装相同的 Unity 版本,更高版本 2019.4.3f1 可以正确处理升级工作。只是给你一个参考来缩小这个 Unity 问题
    • 好吧,我想我太愚蠢了。尝试了 2019.4 版,它仍然给我同样的错误。这里是screenshot。所以我只会使用sdk
    • 嘿,我在另一台计算机上尝试了示例项目,它运行良好。不知道为什么它不能在这个上工作。
    • @Hernando - MSFT 我有一个关于 SU sdk 和 MRTK 的空间网格的一般性问题。现在我正在从空间网格中提取部分,由 mrtk 显示。它对我来说很好:)!我还尝试从 SU sdk (RenderWorldMesh = true; StartDisplay()) 显示的网格中提取一些部分,但不能与网格发生碰撞,因为它上面没有碰撞器。我明白了,如果我想保存整个空间网格,我应该使用SU sdk,但是从空间网格中提取一些部分呢?
    猜你喜欢
    • 1970-01-01
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-03
    • 2022-08-23
    相关资源
    最近更新 更多