从 SharpDX 工具包模型中提取顶点和三角形数据

Posted

技术标签:

【中文标题】从 SharpDX 工具包模型中提取顶点和三角形数据【英文标题】:Extracting vertex and triangle data from SharpDX toolkit model 【发布时间】:2014-11-23 16:24:43 【问题描述】:

我正在使用 SharpDX 2.6 加载的模型创建凸包和三角形网格。

我找到了一些用于从模型中提取顶点和索引的示例代码,但它们基于 XNA 和(似乎是)DirectX 9 - 我的程序使用 SharpDX 和 DirectX 11(和 SharpDX 工具包,所以很多东西都和XNA类似)。

我发现提取顶点和索引(作为三角形)的代码:

public void ExtractData(List<JVector> vertices, List<JOctree.TriangleVertexIndices> indices, Model model)
    
        Matrix[] bones_ = new Matrix[model.Bones.Count];
        model.CopyAbsoluteBoneTransformsTo(bones_);
        foreach (ModelMesh mm in model.Meshes)
        
            Matrix xform = bones_[mm.ParentBone.Index];
            foreach (ModelMeshPart mmp in mm.MeshParts)
            
                int offset = vertices.Count;
                Vector3[] a = new Vector3[mmp.NumVertices];
                mm.VertexBuffer.GetData<Vector3>(mmp.StreamOffset + mmp.BaseVertex * mmp.VertexStride,
                    a, 0, mmp.NumVertices, mmp.VertexStride);
                for (int i = 0; i != a.Length; ++i)
                    Vector3.Transform(ref a[i], ref xform, out a[i]);

                for (int i = 0; i < a.Length; i++) vertices.Add(new JVector(a[i].X, a[i].Y, a[i].Z));

                if (mm.IndexBuffer.IndexElementSize != IndexElementSize.SixteenBits)
                    throw new Exception(
                        String.Format("Model uses 32-bit indices, which are not supported."));
                short[] s = new short[mmp.PrimitiveCount * 3];
                mm.IndexBuffer.GetData<short>(mmp.StartIndex * 2, s, 0, mmp.PrimitiveCount * 3);
                JOctree.TriangleVertexIndices[] tvi = new JOctree.TriangleVertexIndices[mmp.PrimitiveCount];
                for (int i = 0; i != tvi.Length; ++i)
                
                    tvi[i].I0 = s[i * 3 + 2] + offset;
                    tvi[i].I1 = s[i * 3 + 1] + offset;
                    tvi[i].I2 = s[i * 3 + 0] + offset;
                
                indices.AddRange(tvi);
            
        
    

这是我能够将其重做的内容(对于带有 SharpDX 工具包的 DirectX 11): 我已经将它概括为简单地从模型构建一个八叉树,但管道的主要部分仍然是相同的。

    public static Octree BuildOctree(Model model) 

        List<JVector> vertices = new List<JVector>();
        List<TriangleVertexIndices> indices = new List<TriangleVertexIndices>();           
        Matrix[] bones = new Matrix[model.Bones.Count];
        model.CopyAbsoluteBoneTransformsTo(bones);   

        foreach (ModelMesh modelMesh in model.Meshes)
                       
            JMatrix boneTransform = PhysicsSystem.toJMatrix(bones[modelMesh.ParentBone.Index]);
            foreach (ModelMeshPart meshPart in modelMesh.MeshParts)
            
                int offset = vertices.Count;              
                var meshVertices = meshPart.VertexBuffer.Resource.Buffer.GetData<JVector>();
                for (int i = 0; i < meshVertices.Length; ++i)
                
                    JVector.Transform(ref meshVertices[i], ref boneTransform, out meshVertices[i]);
                
                vertices.AddRange(meshVertices);    // append transformed vertices

                var indexElements = meshPart.IndexBuffer.Resource.GetData<short>(); // this is dangerous if the model uses larger integers

                // Each TriangleVertexIndices holds the indices that constitute a triangle primitive
                TriangleVertexIndices[] tvi = new TriangleVertexIndices[indexElements.Length];
                for (int i = 0; i <= tvi.Length - 2; i += 3) 
                    tvi[i].I0 = indexElements[i + 0] + offset;
                    tvi[i].I1 = indexElements[i + 1] + offset;
                    tvi[i].I2 = indexElements[i + 2] + offset;
                
                indices.AddRange(tvi);  // append triangles           
            
        
        Octree ot = new Octree(vertices, indices);
        //ot.BuildOctree(); // (already happens in Octree constructor)
        return ot;
    

我正在使用此代码的变体(没有三角形计算,只有顶点)以类似的方式创建凸包形状。

我的问题是: 有什么办法可以改进这段代码吗?

这段代码是否正确/是否有您立即认为可能错误的地方?

当我使用这种方法从盒子模型中提取顶点时(与抖动教程中使用的相同),我得到的表示不是一个盒子,它的形状网格细节分布不均匀,这会减慢碰撞速度远不止使用内置的 BoxShape 基元(使用 BoxShape 时为 60fps,使用与静态 TerrainShape 碰撞的单个框时约为 20-30)。为什么会这样?

我觉得,虽然我对代码的重写产生了一些输出,但感觉根本不是最优的。

【问题讨论】:

【参考方案1】:

很遗憾,您无法轻松提取数据。顶点缓冲区中的元素可能因模型顶点结构而异(参见compiler code,您可以有Vector4Vector3Vector2 甚至16 位)。

要正确解码它,您需要在运行时访问布局并用它解码顶点缓冲区。

【讨论】:

感谢 xoofx 的回答,如果您的任务是构建一个顶点列表以构建 Jitter Physics 的 ConvexHullShape(),您将如何去做? (我问这个主要是为了从实际开发 SharpDX 的人那里获得洞察力)下面链接了一个使用 XNA 的 Jitter 示例:code.google.com/p/jitterphysics/source/browse/trunk/JitterDemo/…您是否同意他们这样做的方式以及如何最好地将其转换为 SharpDX 解决方案?谢谢 没有时间写一个完整的解决方案,但基本上是这个想法。与 XNA 版本的不同之处在于 XNA GetData 能够获取部分顶点流,而使用 SharpDX 您必须计算到正确顶点元素的偏移量并在原始缓冲区上迭代以对其进行解码。代码写起来有点费力。 当我写我的解决方案时,偏移部分让我很困惑(我是 3D 图形的新手,请原谅)。所以我想迭代模型中每个网格的顶点缓冲区,将适用的骨骼变换(对我来说是新的)应用到每个顶点,然后将该缓冲区的顶点附加到顶点的有序列表中 - 本质上是将许多网格压缩到一个顶点列表中?抵消?这就是创建凸包所需的全部内容,因为可以从没有任何顶点索引信息的点云中构造一个凸包,但要创建一个有代表性的网格,我需要索引/TriangleVertexElements。 从顶点缓冲区获取的数据可以包含其他元素,例如 Vector2 UV、Vector3 法线...等。所以每个顶点不仅仅是一个位置,但对于每个顶点,前 3 个浮点数是位置。因此,对于以字节为单位的顶点大小的顶点步幅(我不确定您是否直接拥有它而不从 VertexElements 重新计算大小),您需要通过 VertexStride 字节迭代顶点缓冲区数据,并采取来自它的第一个 Vector3。对于一些不安全的代码,应该是直截了当的。

以上是关于从 SharpDX 工具包模型中提取顶点和三角形数据的主要内容,如果未能解决你的问题,请参考以下文章

使用 XNA 4.0 从 fbx 模型中提取顶点

从“三角形汤”中找到唯一的顶点

来自自定义数据的模型 I/O 网格

Three.js教程:顶点索引复用顶点数据

如何访问碰撞模型数据?

使用 SharpDx 或 IMSourceReader 从 mp4 文件中读取第二个音轨流