public static DcelMesh FromTriangleMesh(TriangleMesh mesh)
        {
            // TODO: To optimize, check tricks of TriangleMeshShape.ComputeNeighbors.

            if (mesh == null)
            {
                throw new ArgumentNullException("mesh");
            }
            if (mesh.Vertices == null || mesh.Indices == null)
            {
                throw new ArgumentException("Input mesh has no vertices or vertex indices.");
            }

            // Create vertices.
            int numberOfVertices = mesh.Vertices.Count;
            var vertices         = new List <DcelVertex>(numberOfVertices);

            foreach (var position in mesh.Vertices)
            {
                vertices.Add(new DcelVertex(position, null));
            }

            // Weld similar vertices.
            for (int i = 0; i < numberOfVertices; i++)
            {
                for (int j = i + 1; j < numberOfVertices; j++)
                {
                    if (Vector3.AreNumericallyEqual(vertices[i].Position, vertices[j].Position))
                    {
                        vertices[i] = vertices[j];
                    }
                }
            }

            // Create edges and faces for each triangle.
            // We need at least 3 edges for each triangle. We might need more edges if we have to
            // connect unconnected islands of triangles.
            var edges = new List <DcelEdge>(mesh.NumberOfTriangles * 3 * 2);
            var faces = new List <DcelFace>(mesh.NumberOfTriangles);

            for (int i = 0; i < mesh.NumberOfTriangles; i++)
            {
                // Get triangle indices.
                var index0 = mesh.Indices[i * 3 + 0];
                var index1 = mesh.Indices[i * 3 + 1];
                var index2 = mesh.Indices[i * 3 + 2];

                // Get DCEL vertices.
                var vertex0 = vertices[index0];
                var vertex1 = vertices[index1];
                var vertex2 = vertices[index2];

                // Create 3 edges.
                var edge0 = new DcelEdge();
                var edge1 = new DcelEdge();
                var edge2 = new DcelEdge();

                // Create 1 face.
                var face = new DcelFace();

                // Fill out face info.
                face.Boundary = edge0;

                // Fill out vertex info.
                vertex0.Edge = edge0;
                vertex1.Edge = edge1;
                vertex2.Edge = edge2;

                // Fill out edge info.
                // Twin links are created later.
                edge0.Face     = face;
                edge0.Origin   = vertex0;
                edge0.Next     = edge1;
                edge0.Previous = edge2;

                edge1.Face     = face;
                edge1.Origin   = vertex1;
                edge1.Next     = edge2;
                edge1.Previous = edge0;

                edge2.Face     = face;
                edge2.Origin   = vertex2;
                edge2.Next     = edge0;
                edge2.Previous = edge1;

                // Add to lists.
                edges.Add(edge0);
                edges.Add(edge1);
                edges.Add(edge2);
                faces.Add(face);
            }

            // Connect triangles that share an edge.
            for (int i = 0; i < faces.Count; i++)
            {
                // Get face and its 3 edges.
                var faceI  = faces[i];
                var edgeI0 = faceI.Boundary;
                var edgeI1 = edgeI0.Next;
                var edgeI2 = edgeI1.Next;
                Debug.Assert(edgeI2.Next == edgeI0);

                for (int j = i + 1; j < faces.Count; j++)
                {
                    // Get face and its 3 edges.
                    var faceJ  = faces[j];
                    var edgeJ0 = faceJ.Boundary;
                    var edgeJ1 = edgeJ0.Next;
                    var edgeJ2 = edgeJ1.Next;
                    Debug.Assert(edgeJ2.Next == edgeJ0);

                    TryLink(edgeI0, edgeJ0);
                    TryLink(edgeI0, edgeJ1);
                    TryLink(edgeI0, edgeJ2);

                    TryLink(edgeI1, edgeJ0);
                    TryLink(edgeI1, edgeJ1);
                    TryLink(edgeI1, edgeJ2);

                    TryLink(edgeI2, edgeJ0);
                    TryLink(edgeI2, edgeJ1);
                    TryLink(edgeI2, edgeJ2);
                }
            }

            // If the mesh is not closed, we have to add twin edges at the boundaries
            foreach (var edge in edges.ToArray())
            {
                if (edge.Twin == null)
                {
                    var twin = new DcelEdge();
                    twin.Origin = edge.Next.Origin;
                    twin.Twin   = edge;
                    edge.Twin   = twin;
                    edges.Add(twin);
                }
            }

            // Yet, twin.Next/Previous were not set.
            foreach (var edge in edges)
            {
                if (edge.Previous == null)
                {
                    // The previous edge has not been set.
                    // Search the edges around the origin until we find the previous unconnected edge.
                    var origin     = edge.Origin;
                    var originEdge = edge.Twin.Next; // Another edge with the same origin.
                    while (originEdge.Twin.Next != null)
                    {
                        Debug.Assert(originEdge.Origin == origin);
                        originEdge = originEdge.Twin.Next;
                    }

                    var previous = originEdge.Twin;
                    previous.Next = edge;
                    edge.Previous = previous;
                }
            }

            // Check if we have one connected mesh.
            if (vertices.Count > 0)
            {
                const int Tag = 1;
                TagLinkedComponents(vertices[0], Tag);

                // Check if all components were reached.
                if (vertices.Any(v => v.Tag != Tag) ||
                    edges.Any(e => e.Tag != Tag) ||
                    faces.Any(f => f.Tag != Tag))
                {
                    throw new NotSupportedException("The triangle mesh consists of several unconnected components or sub-meshes.");
                }
            }

            var dcelMesh = new DcelMesh {
                Vertex = vertices.FirstOrDefault()
            };

            dcelMesh.ResetTags();

            return(dcelMesh);
        }
Пример #2
0
        private void CreateDualGraph()
        {
            var triangles = new List <CDTriangle>();

            // Convert to TriangleMesh.
            var triangleMesh = _mesh as TriangleMesh;

            if (triangleMesh == null)
            {
                triangleMesh = new TriangleMesh();
                triangleMesh.Add(_mesh, false);
                triangleMesh.WeldVertices();
            }

            // Initialize vertex normals.
            var normals        = new Vector3[triangleMesh.Vertices.Count]; // Vertex normals.
            var neighborCounts = new int[triangleMesh.Vertices.Count];     // Numbers of triangles that touch each vertex.

            for (int i = 0; i < triangleMesh.Vertices.Count; i++)
            {
                normals[i]        = Vector3.Zero;
                neighborCounts[i] = 0;
            }

            // Go through all triangles. Add the normal to normals and increase the neighborCounts
            for (int i = 0; i < triangleMesh.NumberOfTriangles; i++)
            {
                Triangle triangle = triangleMesh.GetTriangle(i);
                var      normal   = triangle.Normal;

                for (int j = 0; j < 3; j++)
                {
                    var vertexIndex = triangleMesh.Indices[(i * 3) + j];
                    normals[vertexIndex]        = normals[vertexIndex] + normal;
                    neighborCounts[vertexIndex] = neighborCounts[vertexIndex] + 1;
                }
            }

            // Create triangles.
            for (int i = 0; i < triangleMesh.NumberOfTriangles; i++)
            {
                Triangle triangle   = triangleMesh.GetTriangle(i);
                var      cdTriangle = new CDTriangle
                {
                    Id       = i,
                    Vertices = new[] { triangle.Vertex0, triangle.Vertex1, triangle.Vertex2 },
                    Normal   = triangle.Normal, // TODO: Special care for degenerate triangles needed?
                };

                for (int j = 0; j < 3; j++)
                {
                    var vertexIndex   = triangleMesh.Indices[(i * 3) + j];
                    var normalSum     = normals[vertexIndex];
                    var neighborCount = neighborCounts[vertexIndex];
                    if (neighborCount > 0)
                    {
                        var normal = normalSum / neighborCount;
                        normal.TryNormalize();
                        cdTriangle.VertexNormals[j] = normal;
                    }
                }

                triangles.Add(cdTriangle);
            }

            // Create an island for each triangle.
            _islands = new List <CDIsland>(triangles.Count);
            for (int i = 0; i < triangles.Count; i++)
            {
                var triangle = triangles[i];

                var island = new CDIsland();
                island.Id        = i;
                island.Triangles = new[] { triangle };
                island.Vertices  = triangle.Vertices;

                island.Aabb = new Aabb(triangle.Vertices[0], triangle.Vertices[0]);
                island.Aabb.Grow(triangle.Vertices[1]);
                island.Aabb.Grow(triangle.Vertices[2]);

                triangle.Island = island;

                _islands.Add(island);
            }

            // Find connectivity (= add neighbor links).
            for (int i = 0; i < triangles.Count; i++)
            {
                var a = triangles[i];
                for (int j = i + 1; j < triangles.Count; j++)
                {
                    var b = triangles[j];
                    CDTriangle.FindNeighbors(a, b);
                }
            }

            // Create links.
            _links = new List <CDIslandLink>();
            for (int i = 0; i < _islands.Count; i++)
            {
                var island   = _islands[i];
                var triangle = island.Triangles[0];

                // Go through all neighbors.
                // If there is a neighbor, create a link.
                // To avoid two links per triangle, we create the link only if the id of this triangle
                // is less than the other island id.
                for (int j = 0; j < 3; j++)
                {
                    CDTriangle neighborTriangle = triangle.Neighbors[j];
                    if (neighborTriangle != null && neighborTriangle.Island.Id > i)
                    {
                        var link = new CDIslandLink(island, neighborTriangle.Island, AllowedConcavity, SmallIslandBoost, IntermediateVertexLimit, SampleTriangleVertices, SampleTriangleCenters);
                        _links.Add(link);
                    }
                }
            }

            // Now, we have a lot of islands with 1 triangle each.
        }
Пример #3
0
        public static TriangleMesh FromModel(Model model)
        {
            // Similar code can be found on http://www.enchantedage.com/node/30 (by Jon Watte).
            // But this code was developed independently.

            if (model == null)
            {
                throw new ArgumentNullException("model");
            }

            var triangleMesh = new TriangleMesh();

            foreach (var modelMesh in model.Meshes)
            {
                // Get bone transformation.
                Matrix transform = GetAbsoluteTransform(modelMesh.ParentBone);

                foreach (var modelMeshPart in modelMesh.MeshParts)
                {
                    // Get vertex element info.
                    var vertexDeclaration = modelMeshPart.VertexBuffer.VertexDeclaration;
                    var vertexElements    = vertexDeclaration.GetVertexElements();

                    // Get the vertex positions.
                    var positionElement = vertexElements.First(e => e.VertexElementUsage == VertexElementUsage.Position);
                    if (positionElement.VertexElementFormat != VertexElementFormat.Vector3)
                    {
                        throw new NotSupportedException("For vertex positions only VertexElementFormat.Vector3 is supported.");
                    }

                    var positions = new Vector3[modelMeshPart.NumVertices];
                    modelMeshPart.VertexBuffer.GetData(
                        modelMeshPart.VertexOffset * vertexDeclaration.VertexStride + positionElement.Offset,
                        positions,
                        0,
                        modelMeshPart.NumVertices,
                        vertexDeclaration.VertexStride);

                    // Apply bone transformation.
                    for (int i = 0; i < positions.Length; i++)
                    {
                        positions[i] = Vector3.Transform(positions[i], transform);
                    }

                    // Remember the number of vertices already in the mesh.
                    int vertexCount = triangleMesh.Vertices.Count;

                    // Add the vertices of the current modelMeshPart.
                    foreach (Vector3 p in positions)
                    {
                        triangleMesh.Vertices.Add((Vector3)p);
                    }

                    // Get indices.
                    var indexElementSize = (modelMeshPart.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) ? 2 : 4;
                    if (indexElementSize == 2)
                    {
                        ushort[] indices = new ushort[modelMeshPart.PrimitiveCount * 3];
                        modelMeshPart.IndexBuffer.GetData(
                            modelMeshPart.StartIndex * 2,
                            indices,
                            0,
                            modelMeshPart.PrimitiveCount * 3);

                        // Add indices to triangle mesh.
                        for (int i = 0; i < modelMeshPart.PrimitiveCount; i++)
                        {
                            // The three indices of the next triangle.
                            // We add 'vertexCount' because the triangleMesh already contains other mesh parts.
                            int i0 = indices[i * 3 + 0] + vertexCount;
                            int i1 = indices[i * 3 + 1] + vertexCount;
                            int i2 = indices[i * 3 + 2] + vertexCount;

                            triangleMesh.Indices.Add(i0);
                            triangleMesh.Indices.Add(i2); // DigitalRune Geometry uses other winding order!
                            triangleMesh.Indices.Add(i1);
                        }
                    }
                    else
                    {
                        Debug.Assert(indexElementSize == 4);

                        int[] indices = new int[modelMeshPart.PrimitiveCount * 3];
                        modelMeshPart.IndexBuffer.GetData(
                            modelMeshPart.StartIndex * 4,
                            indices,
                            0,
                            modelMeshPart.PrimitiveCount * 3);

                        // Add indices to triangle mesh.
                        for (int i = 0; i < modelMeshPart.PrimitiveCount; i++)
                        {
                            // The three indices of the next triangle.
                            // We add 'vertexCount' because the triangleMesh already contains other mesh parts.
                            int i0 = indices[i * 3 + 0] + vertexCount;
                            int i1 = indices[i * 3 + 1] + vertexCount;
                            int i2 = indices[i * 3 + 2] + vertexCount;

                            triangleMesh.Indices.Add(i0);
                            triangleMesh.Indices.Add(i2); // DigitalRune Geometry uses other winding order!
                            triangleMesh.Indices.Add(i1);
                        }
                    }
                }
            }

            return(triangleMesh);
        }