/// <summary> /// Converts mesh content to model content with a <see cref="TriangleMeshShape"/>. /// </summary> /// <param name="input">The root node content.</param> /// <param name="context">Context for the specified processor.</param> /// <returns>The <see cref="Shape"/>.</returns> public override DRModelNodeContent Process(NodeContent input, ContentProcessorContext context) { // ----- Process Model var model = base.Process(input, context); // ----- Extract Triangles var triangleMesh = new TriangleMesh(); // The input node is usually a tree of nodes. We need to collect all MeshContent nodes // in the tree. The DigitalRune Helper library provides a TreeHelper that can be used // to traverse trees using LINQ. // The following returns an IEnumerable that returns all nodes of the tree. var nodes = TreeHelper.GetSubtree(input, n => n.Children); // We only need nodes of type MeshContent. var meshes = nodes.OfType<MeshContent>(); foreach (var mesh in meshes) { // Apply any transformations to vertices. Matrix transform = mesh.AbsoluteTransform; for (int i = 0; i < mesh.Positions.Count; i++) mesh.Positions[i] = Vector3.Transform(mesh.Positions[i], transform); // Extract triangles from submeshes. foreach (var geometry in mesh.Geometry) { int numberOfTriangles = geometry.Indices.Count / 3; for (int i = 0; i < numberOfTriangles; i++) { int index0 = geometry.Indices[3 * i + 0]; int index1 = geometry.Indices[3 * i + 2]; // Note: DigitalRune Geometry uses a different winding int index2 = geometry.Indices[3 * i + 1]; // order. Therefore, the indices need to be swapped. Vector3F vertex0 = (Vector3F)geometry.Vertices.Positions[index0]; Vector3F vertex1 = (Vector3F)geometry.Vertices.Positions[index1]; Vector3F vertex2 = (Vector3F)geometry.Vertices.Positions[index2]; triangleMesh.Add(new Triangle(vertex0, vertex1, vertex2), false, Numeric.EpsilonF, true); } } } // Remove duplicate vertices. triangleMesh.WeldVertices(); // ----- Create TriangleMeshShape // Create a TriangleMeshShape that can be used for collision detection. // Note: // - Contact-welding is enabled to improve the results of the collision detection. // - A CompressedAabbTree is used for spatial partitioning to speed up collision detection. var triangleMeshShape = new TriangleMeshShape(triangleMesh, true, new CompressedAabbTree()); // Export the ModelNode together with the TriangleMeshShape. model.UserData = triangleMeshShape; return model; }
/// <overloads> /// <summary> /// Converts the given <see cref="ITriangleMesh"/> to a <see cref="DcelMesh"/>. /// </summary> /// </overloads> /// /// <summary> /// Converts the given <see cref="ITriangleMesh"/> to a <see cref="DcelMesh"/>. /// </summary> /// <param name="mesh">The triangle mesh.</param> /// <returns> /// The <see cref="DcelMesh"/>. /// </returns> /// <remarks> /// Currently, creating <see cref="DcelMesh"/>es is not supported if the triangle mesh consists /// of unconnected sub-meshes or unconnected triangles. All parts of the triangle mesh must be /// connected via an edge. (But it is not required that the mesh is closed.) /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="mesh"/> has no vertices or vertex indices. /// </exception> /// <exception cref="NotSupportedException"> /// <paramref name="mesh"/> consists of several unconnected components or sub-meshes. /// </exception> public static DcelMesh FromTriangleMesh(ITriangleMesh mesh) { if (mesh == null) throw new ArgumentNullException("mesh"); // The simple way: Create a TriangleMesh first. var triangleMesh = new TriangleMesh(); triangleMesh.Add(mesh, false); triangleMesh.WeldVertices(); return FromTriangleMesh(triangleMesh); }
/// <overloads> /// <summary> /// Converts the given <see cref="ITriangleMesh"/> to a <see cref="DcelMesh"/>. /// </summary> /// </overloads> /// /// <summary> /// Converts the given <see cref="ITriangleMesh"/> to a <see cref="DcelMesh"/>. /// </summary> /// <param name="mesh">The triangle mesh.</param> /// <returns> /// The <see cref="DcelMesh"/>. /// </returns> /// <remarks> /// Currently, creating <see cref="DcelMesh"/>es is not supported if the triangle mesh consists /// of unconnected sub-meshes or unconnected triangles. All parts of the triangle mesh must be /// connected via an edge. (But it is not required that the mesh is closed.) /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="mesh"/> has no vertices or vertex indices. /// </exception> /// <exception cref="NotSupportedException"> /// <paramref name="mesh"/> consists of several unconnected components or sub-meshes. /// </exception> public static DcelMesh FromTriangleMesh(ITriangleMesh mesh) { if (mesh == null) { throw new ArgumentNullException("mesh"); } // The simple way: Create a TriangleMesh first. var triangleMesh = new TriangleMesh(); triangleMesh.Add(mesh, false); triangleMesh.WeldVertices(); return(FromTriangleMesh(triangleMesh)); }
public static Submesh CreateSubmesh(GraphicsDevice graphicsDevice, ITriangleMesh mesh, float angleLimit) { var tm = mesh as TriangleMesh; if (tm == null) { tm = new TriangleMesh(); tm.Add(mesh); tm.WeldVertices(); } return CreateSubmesh(graphicsDevice, tm, angleLimit); }
/// <summary> /// Called when a mesh should be generated for the shape. /// </summary> /// <param name="absoluteDistanceThreshold">The absolute distance threshold.</param> /// <param name="iterationLimit">The iteration limit.</param> /// <returns>The triangle mesh for this shape.</returns> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { // Estimate required segment angle for given accuracy. // (Easy to derive from simple drawing of a circle segment with a triangle used to represent // the segment.) float alpha = (float)Math.Acos((Radius - absoluteDistanceThreshold) / Radius) * 2; int numberOfSegments = (int)Math.Ceiling(ConstantsF.PiOver2 / alpha) * 4; // Apply the iteration limit - in case absoluteDistanceThreshold is 0. // Lets say each iteration doubles the number of segments. This is an arbitrary interpretation // of the "iteration limit". numberOfSegments = Math.Min(numberOfSegments, 2 << iterationLimit); alpha = ConstantsF.TwoPi / numberOfSegments; TriangleMesh mesh = new TriangleMesh(); // The world space vertices are created by rotating "radius vectors" with this rotations. QuaternionF rotationY = QuaternionF.CreateRotationY(alpha); QuaternionF rotationZ = QuaternionF.CreateRotationZ(alpha); // We use two nested loops: In each loop a "radius vector" is rotated further to get a // new vertex. Vector3F rLow = Vector3F.UnitX * Radius; // Radius vector for the lower vertex. for (int i = 1; i <= numberOfSegments / 4; i++) { Vector3F rHigh = rotationZ.Rotate(rLow); // Radius vector for the higher vertex. // In the inner loop we create lines and triangles between 4 vertices, which are created // with the radius vectors rLow0, rLow1, rHigh0, rHigh1. Vector3F rLow0 = rLow; Vector3F rHigh0 = rHigh; for (int j = 1; j <= numberOfSegments; j++) { Vector3F rLow1 = rotationY.Rotate(rLow0); Vector3F rHigh1 = rotationY.Rotate(rHigh0); // Two top hemisphere triangles mesh.Add(new Triangle { Vertex0 = new Vector3F(0, Height / 2 - Radius, 0) + rLow0, Vertex1 = new Vector3F(0, Height / 2 - Radius, 0) + rLow1, Vertex2 = new Vector3F(0, Height / 2 - Radius, 0) + rHigh0, }, false); if (i < numberOfSegments / 4) // At the "northpole" only a triangle is needed. No quad. { mesh.Add(new Triangle { Vertex0 = new Vector3F(0, Height / 2 - Radius, 0) + rLow1, Vertex1 = new Vector3F(0, Height / 2 - Radius, 0) + rHigh1, Vertex2 = new Vector3F(0, Height / 2 - Radius, 0) + rHigh0, }, false); } // Two bottom hemisphere triangles mesh.Add(new Triangle { Vertex0 = new Vector3F(0, -Height / 2 + Radius, 0) - rLow0, Vertex1 = new Vector3F(0, -Height / 2 + Radius, 0) - rHigh0, Vertex2 = new Vector3F(0, -Height / 2 + Radius, 0) - rLow1, }, false); if (i < numberOfSegments / 4) // At the "southpole" only a triangle is needed. No quad. { mesh.Add(new Triangle { Vertex0 = new Vector3F(0, -Height / 2 + Radius, 0) - rLow1, Vertex1 = new Vector3F(0, -Height / 2 + Radius, 0) - rHigh0, Vertex2 = new Vector3F(0, -Height / 2 + Radius, 0) - rHigh1, }, false); } // Two side triangles if (i == 1) { mesh.Add(new Triangle { Vertex0 = new Vector3F(0, -Height / 2 + Radius, 0) + rLow0, Vertex1 = new Vector3F(0, -Height / 2 + Radius, 0) + rLow1, Vertex2 = new Vector3F(0, Height / 2 - Radius, 0) + rLow0, }, false); mesh.Add(new Triangle { Vertex0 = new Vector3F(0, -Height / 2 + Radius, 0) + rLow1, Vertex1 = new Vector3F(0, Height / 2 - Radius, 0) + rLow1, Vertex2 = new Vector3F(0, Height / 2 - Radius, 0) + rLow0, }, false); } rLow0 = rLow1; rHigh0 = rHigh1; } rLow = rHigh; } mesh.WeldVertices(); return mesh; }
/// <summary> /// Called when a mesh should be generated for the shape. /// </summary> /// <param name="absoluteDistanceThreshold">The absolute distance threshold.</param> /// <param name="iterationLimit">The iteration limit.</param> /// <returns>The triangle mesh for this shape.</returns> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { // Estimate required segment angle for given accuracy. // (Easy to derive from simple drawing of a circle segment with a triangle used to represent // the segment.) float alpha = (float)Math.Acos((Radius - absoluteDistanceThreshold) / Radius) * 2; int numberOfSegments = (int)Math.Ceiling(ConstantsF.TwoPi / alpha); // Apply the iteration limit - in case absoluteDistanceThreshold is 0. // Lets say each iteration doubles the number of segments. This is an arbitrary interpretation // of the "iteration limit". numberOfSegments = Math.Min(numberOfSegments, 2 << iterationLimit); alpha = ConstantsF.TwoPi / numberOfSegments; Vector3F r0 = new Vector3F(Radius, 0, 0); QuaternionF rotation = QuaternionF.CreateRotationY(alpha); TriangleMesh mesh = new TriangleMesh(); for (int i = 1; i <= numberOfSegments; i++) { Vector3F r1 = rotation.Rotate(r0); // Bottom triangle mesh.Add(new Triangle { Vertex0 = new Vector3F(0, -Height / 2, 0), Vertex1 = new Vector3F(0, -Height / 2, 0) + r1, Vertex2 = new Vector3F(0, -Height / 2, 0) + r0, }, false); // Top triangle mesh.Add(new Triangle { Vertex0 = new Vector3F(0, +Height / 2, 0), Vertex1 = new Vector3F(0, +Height / 2, 0) + r0, Vertex2 = new Vector3F(0, +Height / 2, 0) + r1, }, false); // Two side triangles mesh.Add(new Triangle { Vertex0 = new Vector3F(0, -Height / 2, 0) + r0, Vertex1 = new Vector3F(0, -Height / 2, 0) + r1, Vertex2 = new Vector3F(0, +Height / 2, 0) + r0, }, false); mesh.Add(new Triangle { Vertex0 = new Vector3F(0, -Height / 2, 0) + r1, Vertex1 = new Vector3F(0, +Height / 2, 0) + r1, Vertex2 = new Vector3F(0, +Height / 2, 0) + r0, }, false); r0 = r1; } mesh.WeldVertices(); return mesh; }
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 Vector3F[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] = Vector3F.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. }
/// <summary> /// Called when a mesh should be generated for the shape. /// </summary> /// <param name="absoluteDistanceThreshold">The absolute distance threshold.</param> /// <param name="iterationLimit">The iteration limit.</param> /// <returns>The triangle mesh for this shape.</returns> /// <remarks> /// A deep copy of the <see cref="Mesh"/> is returned. /// </remarks> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { var triangleMesh = Mesh as TriangleMesh; if (triangleMesh != null) return triangleMesh.Clone(); // Return a copy of the mesh. TriangleMesh mesh = new TriangleMesh(); int numberOfTriangles = _mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) { Triangle triangle = _mesh.GetTriangle(i); mesh.Add(triangle, false); } mesh.WeldVertices(); return mesh; }
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 Vector3F[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] = Vector3F.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. }