/// <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; }
public void ComputeCollision() { CollisionObject a = new CollisionObject(); TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(0, 0, 1), new Vector3F(1, 0, 0)), false); mesh.Add(new Triangle(new Vector3F(1, 0, 0), new Vector3F(0, 0, 1), new Vector3F(1, 0, 1)), false); TriangleMeshShape meshShape = new TriangleMeshShape(); meshShape.Mesh = mesh; a.GeometricObject = new GeometricObject(meshShape, Pose.Identity); CollisionObject b = new CollisionObject(new GeometricObject { Shape = new SphereShape(1), Pose = new Pose(new Vector3F(0, 0.9f, 0)), }); ContactSet set; TriangleMeshAlgorithm algo = new TriangleMeshAlgorithm(new CollisionDetection()); set = algo.GetClosestPoints(a, b); Assert.AreEqual(true, algo.HaveContact(a, b)); Assert.AreEqual(true, algo.HaveContact(b, a)); Assert.AreEqual(1, set.Count); Assert.IsTrue(Numeric.AreEqual(0.1f, set[0].PenetrationDepth, 0.001f)); //Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 0, 0), set[0].PositionALocal, 0.01f)); // MPR will not return the perfect contact point. Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 1, 0), set[0].Normal, 0.1f)); ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(0.1f, 0.9f, 0.1f)); algo.UpdateContacts(set, 0); Assert.AreEqual(true, algo.HaveContact(a, b)); Assert.AreEqual(true, algo.HaveContact(b, a)); Assert.AreEqual(1, set.Count); Assert.IsTrue(Numeric.AreEqual(0.1f, set[0].PenetrationDepth, 0.001f)); Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0.1f, 0, 0.1f), set[0].PositionALocal, 0.01f)); Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 1, 0), set[0].Normal, 0.1f)); // The same with a swapped set. set = set.Swapped; algo.UpdateContacts(set, 0); Assert.AreEqual(1, set.Count); Assert.IsTrue(Numeric.AreEqual(0.1f, set[0].PenetrationDepth, 0.001f)); Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0.1f, 0, 0.1f), set[0].PositionBLocal, 0.01f)); Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, -1, 0), set[0].Normal, 0.1f)); // Separation. ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(0.2f, 1.2f, 0.3f)); set = set.Swapped; Assert.AreEqual(false, algo.HaveContact(a, b)); Assert.AreEqual(false, algo.HaveContact(b, a)); algo.UpdateClosestPoints(set, 0); Assert.IsTrue(Numeric.AreEqual(-0.2f, set[0].PenetrationDepth, 0.001f)); Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0.2f, 0, 0.3f), set[0].PositionALocal, 0.01f)); Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 1, 0), set[0].Normal, 0.1f)); algo.UpdateContacts(set, 0); Assert.AreEqual(0, set.Count); }
/// <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, TriangleMesh mesh, float angleLimit) { VertexBuffer vertexBuffer; PrimitiveType primitiveType; int primitiveCount; CreateVertexBuffer(graphicsDevice, mesh, angleLimit, out vertexBuffer, out primitiveType, out primitiveCount); if (vertexBuffer == null) return null; return new Submesh { PrimitiveType = PrimitiveType.TriangleList, PrimitiveCount = primitiveCount, VertexBuffer = vertexBuffer, VertexCount = vertexBuffer.VertexCount }; }
private void CreateRigidBody() { var triangleMesh = new TriangleMesh(); foreach (var meshNode in _modelNode.GetSubtree().OfType<MeshNode>()) { // Extract the triangle mesh from the DigitalRune Graphics Mesh instance. var subTriangleMesh = new TriangleMesh(); foreach (var submesh in meshNode.Mesh.Submeshes) { submesh.ToTriangleMesh(subTriangleMesh); } // Apply the transformation of the mesh node. subTriangleMesh.Transform(meshNode.PoseWorld * Matrix44F.CreateScale(meshNode.ScaleWorld)); // Combine into final triangle mesh. triangleMesh.Add(subTriangleMesh); } // Create a collision shape that uses the mesh. var triangleMeshShape = new TriangleMeshShape(triangleMesh); // Optional: Assign a spatial partitioning scheme to the triangle mesh. (A spatial partition // adds an additional memory overhead, but it improves collision detection speed tremendously!) triangleMeshShape.Partition = new CompressedAabbTree { // The tree is automatically built using a mixed top-down/bottom-up approach. Bottom-up // building is slower but produces better trees. If the tree building takes too long, // we can lower the BottomUpBuildThreshold (default is 128). BottomUpBuildThreshold = 0, }; _rigidBody = new RigidBody(triangleMeshShape, new MassFrame(), null) { Pose = _pose, Scale = _scale, MotionType = MotionType.Static }; // Add rigid body to physics simulation and model to scene. var simulation = _services.GetInstance<Simulation>(); simulation.RigidBodies.Add(_rigidBody); }
public ConvexDecompositionSample(Microsoft.Xna.Framework.Game game) : base(game) { SampleFramework.IsMouseVisible = false; GraphicsScreen.ClearBackground = true; GraphicsScreen.BackgroundColor = Color.CornflowerBlue; SetCamera(new Vector3F(3, 3, 3), 0.8f, -0.6f); // Load model. _modelNode = ContentManager.Load<ModelNode>("Saucer/Saucer").Clone(); // Combine all meshes of the model into a single TriangleMesh. TriangleMesh mesh = new TriangleMesh(); foreach (var meshNode in _modelNode.GetChildren().OfType<MeshNode>()) { var childMesh = MeshHelper.ToTriangleMesh(meshNode.Mesh); childMesh.Transform(meshNode.PoseWorld * Matrix44F.CreateScale(meshNode.ScaleWorld)); mesh.Add(childMesh); } // Start convex decomposition on another thread. _convexDecomposition = new ConvexDecomposition(); _convexDecomposition.ProgressChanged += OnProgressChanged; _convexDecomposition.AllowedConcavity = 0.8f; _convexDecomposition.IntermediateVertexLimit = 65536; _convexDecomposition.VertexLimit = 64; // 0 gives optimal results but is the slowest. Small positive values improve // speed but the result might be less optimal. _convexDecomposition.SmallIslandBoost = 0.02f; _convexDecomposition.SampleTriangleCenters = true; _convexDecomposition.SampleTriangleVertices = true; // Experimental multithreading. Enable at own risk ;-) _convexDecomposition.EnableMultithreading = true; _convexDecomposition.DecomposeAsync(mesh); }
public void ComputeCollisionBvh() { CollisionObject a = new CollisionObject(); TriangleMesh meshA = new TriangleMesh(); meshA.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(0, 0, 1), new Vector3F(1, 0, 0)), false); var meshShapeA = new TriangleMeshShape(); meshShapeA.Mesh = meshA; meshShapeA.Partition = new AabbTree<int>(); ((GeometricObject)a.GeometricObject).Shape = meshShapeA; CollisionObject b = new CollisionObject(); TriangleMesh meshB = new TriangleMesh(); meshB.Add(new Triangle(new Vector3F(2, 0, 0), new Vector3F(2, 0, 1), new Vector3F(3, 0, 0)), false); TriangleMeshShape meshShapeB = new TriangleMeshShape(); meshShapeB.Mesh = meshB; meshShapeB.Partition = new AabbTree<int>(); ((GeometricObject)b.GeometricObject).Shape = meshShapeB; ((GeometricObject)b.GeometricObject).Pose = new Pose(new Vector3F(-2, 0, 0)); CollisionAlgorithm algo = new TriangleMeshAlgorithm(new CollisionDetection()); Assert.AreEqual(true, algo.HaveContact(a, b)); }
/// <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> /// This creates a mesh with a single degenerate triangle that represents the ray. /// </remarks> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { // Make a mesh with 1 degenerate triangle TriangleMesh mesh = new TriangleMesh(); mesh.Add( new Triangle { Vertex0 = Origin, Vertex1 = Origin, Vertex2 = Origin + Direction * Length, }, true, Numeric.EpsilonF, false); return mesh; }
public static void ToTriangleMesh(this Submesh submesh, TriangleMesh triangleMesh) { // This method is similar to TriangleMesh.FromModel(). if (submesh == null) throw new ArgumentNullException("submesh"); if (triangleMesh == null) throw new ArgumentNullException("triangleMesh"); if (submesh.PrimitiveType != PrimitiveType.TriangleList) throw new NotSupportedException("All submeshes must be triangle lists. ToTriangleMesh() does not support other primitive types."); if (submesh.VertexBuffer == null) return; if (triangleMesh.Vertices == null) triangleMesh.Vertices = new List<Vector3F>(submesh.VertexCount); if (triangleMesh.Indices == null) triangleMesh.Indices = new List<int>(submesh.PrimitiveCount * 3); // Get vertex element info. var vertexDeclaration = submesh.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."); Vector3[] positions = new Vector3[submesh.VertexCount]; submesh.VertexBuffer.GetData( submesh.StartVertex * vertexDeclaration.VertexStride + positionElement.Offset, positions, 0, submesh.VertexCount, vertexDeclaration.VertexStride); if (submesh.IndexBuffer != null) { // 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((Vector3F)p); // Get indices. int indexElementSize = (submesh.IndexBuffer.IndexElementSize == IndexElementSize.SixteenBits) ? 2 : 4; if (indexElementSize == 2) { ushort[] indices = new ushort[submesh.PrimitiveCount * 3]; submesh.IndexBuffer.GetData( submesh.StartIndex * 2, indices, 0, submesh.PrimitiveCount * 3); // Add indices to triangle mesh. for (int i = 0; i < submesh.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[submesh.PrimitiveCount * 3]; submesh.IndexBuffer.GetData( submesh.StartIndex * 4, indices, 0, submesh.PrimitiveCount * 3); // Add indices to triangle mesh. for (int i = 0; i < submesh.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 { // No index buffer: int vertexCount = triangleMesh.Vertices.Count; for (int i = 0; i < submesh.VertexCount; i += 3) { triangleMesh.Vertices.Add((Vector3F)positions[i]); triangleMesh.Vertices.Add((Vector3F)positions[i + 1]); triangleMesh.Vertices.Add((Vector3F)positions[i + 2]); triangleMesh.Indices.Add(i + vertexCount); triangleMesh.Indices.Add(i + 2 + vertexCount); // DigitalRune Geometry uses other winding order! triangleMesh.Indices.Add(i + 1 + vertexCount); } } }
/// <overloads> /// <summary> /// Creates a <see cref="TriangleMesh"/> (DigitalRune.Geometry) for the <see cref="Mesh"/> or /// <see cref="Submesh"/> (DigitalRune.Graphics). /// </summary> /// </overloads> /// /// <summary> /// Creates a <see cref="TriangleMesh"/> from a <see cref="Mesh"/>. /// </summary> /// <param name="mesh">The mesh.</param> /// <returns>A triangle mesh containing all triangles of the specified mesh.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> /// <exception cref="NotSupportedException"> /// A submeshes uses a primitive type other than triangle lists. Other primitive types are not /// supported. /// </exception> /// <exception cref="NotSupportedException"> /// The vertex position format of a submesh is not <see cref="Vector3"/>. Only /// <see cref="Vector3"/> is supported /// </exception> public static TriangleMesh ToTriangleMesh(this Mesh mesh) { if (mesh == null) throw new ArgumentNullException("mesh"); var triangleMesh = new TriangleMesh(); foreach (var submesh in mesh.Submeshes) ToTriangleMesh(submesh, triangleMesh); return triangleMesh; }
/// <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) { // Half extent: float halfExtentX = _widthX / 2; float halfExtentY = _widthY / 2; float halfExtentZ = _widthZ / 2; TriangleMesh mesh = new TriangleMesh(); // -y face mesh.Add(new Triangle { Vertex0 = new Vector3F(-halfExtentX, -halfExtentY, halfExtentZ), Vertex1 = new Vector3F(-halfExtentX, -halfExtentY, -halfExtentZ), Vertex2 = new Vector3F(halfExtentX, -halfExtentY, -halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(halfExtentX, -halfExtentY, -halfExtentZ), Vertex1 = new Vector3F(halfExtentX, -halfExtentY, halfExtentZ), Vertex2 = new Vector3F(-halfExtentX, -halfExtentY, halfExtentZ), }, true); // +x face mesh.Add(new Triangle { Vertex0 = new Vector3F(halfExtentX, halfExtentY, halfExtentZ), Vertex1 = new Vector3F(halfExtentX, -halfExtentY, halfExtentZ), Vertex2 = new Vector3F(halfExtentX, -halfExtentY, -halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(halfExtentX, -halfExtentY, -halfExtentZ), Vertex1 = new Vector3F(halfExtentX, halfExtentY, -halfExtentZ), Vertex2 = new Vector3F(halfExtentX, halfExtentY, halfExtentZ), }, true); // -z face mesh.Add(new Triangle { Vertex0 = new Vector3F(halfExtentX, halfExtentY, -halfExtentZ), Vertex1 = new Vector3F(halfExtentX, -halfExtentY, -halfExtentZ), Vertex2 = new Vector3F(-halfExtentX, -halfExtentY, -halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(-halfExtentX, -halfExtentY, -halfExtentZ), Vertex1 = new Vector3F(-halfExtentX, halfExtentY, -halfExtentZ), Vertex2 = new Vector3F(halfExtentX, halfExtentY, -halfExtentZ), }, true); // -x face mesh.Add(new Triangle { Vertex0 = new Vector3F(-halfExtentX, halfExtentY, -halfExtentZ), Vertex1 = new Vector3F(-halfExtentX, -halfExtentY, -halfExtentZ), Vertex2 = new Vector3F(-halfExtentX, -halfExtentY, halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(-halfExtentX, -halfExtentY, halfExtentZ), Vertex1 = new Vector3F(-halfExtentX, halfExtentY, halfExtentZ), Vertex2 = new Vector3F(-halfExtentX, halfExtentY, -halfExtentZ), }, true); // +z face mesh.Add(new Triangle { Vertex0 = new Vector3F(-halfExtentX, halfExtentY, halfExtentZ), Vertex1 = new Vector3F(-halfExtentX, -halfExtentY, halfExtentZ), Vertex2 = new Vector3F(halfExtentX, -halfExtentY, halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(halfExtentX, -halfExtentY, halfExtentZ), Vertex1 = new Vector3F(halfExtentX, halfExtentY, halfExtentZ), Vertex2 = new Vector3F(-halfExtentX, halfExtentY, halfExtentZ), }, true); // +y face mesh.Add(new Triangle { Vertex0 = new Vector3F(-halfExtentX, halfExtentY, -halfExtentZ), Vertex1 = new Vector3F(-halfExtentX, halfExtentY, halfExtentZ), Vertex2 = new Vector3F(halfExtentX, halfExtentY, halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(halfExtentX, halfExtentY, halfExtentZ), Vertex1 = new Vector3F(halfExtentX, halfExtentY, -halfExtentZ), Vertex2 = new Vector3F(-halfExtentX, halfExtentY, -halfExtentZ), }, true); return mesh; }
public void Setup() { // Make a unit cube. _mesh = new TriangleMesh(); // Bottom _mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(1, 1, 0), new Vector3F(1, 0, 0)), true); _mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(0, 1, 0), new Vector3F(1, 1, 0)), true); // Top _mesh.Add(new Triangle(new Vector3F(0, 0, 1), new Vector3F(1, 0, 1), new Vector3F(1, 1, 1)), true); _mesh.Add(new Triangle(new Vector3F(0, 0, 1), new Vector3F(1, 1, 1), new Vector3F(0, 1, 1)), true); // Left _mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(1, 0, 0), new Vector3F(1, 0, 1)), true); _mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(1, 0, 1), new Vector3F(0, 0, 1)), true); // Right _mesh.Add(new Triangle(new Vector3F(0, 1, 0), new Vector3F(0, 1, 1), new Vector3F(1, 1, 1)), true); _mesh.Add(new Triangle(new Vector3F(0, 1, 0), new Vector3F(1, 1, 1), new Vector3F(1, 1, 0)), true); // Front _mesh.Add(new Triangle(new Vector3F(1, 0, 0), new Vector3F(1, 1, 0), new Vector3F(1, 1, 1)), true); _mesh.Add(new Triangle(new Vector3F(1, 0, 0), new Vector3F(1, 1, 1), new Vector3F(1, 0, 1)), true); // Back _mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(0, 0, 1), new Vector3F(0, 1, 1)), true); _mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(0, 1, 1), new Vector3F(0, 1, 0)), true); _meshShape = new TriangleMeshShape { Mesh = _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> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle { Vertex0 = new Vector3F(_widthX / 2, _widthY / 2, 0), Vertex1 = new Vector3F(-_widthX / 2, _widthY / 2, 0), Vertex2 = new Vector3F(-_widthX / 2, -_widthY / 2, 0), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(-_widthX / 2, -_widthY / 2, 0), Vertex1 = new Vector3F(_widthX / 2, -_widthY / 2, 0), Vertex2 = new Vector3F(_widthX / 2, _widthY / 2, 0), }, true); return mesh; }
public static TriangleMesh CreateIcosphere(int subdivisions, bool hemisphere) { if (subdivisions < 0) throw new ArgumentOutOfRangeException("subdivisions", "The number of subdivisions must not be negative."); if (hemisphere && subdivisions < 1) throw new ArgumentException("The number of subdivisions must greater than 0 to create a hemisphere."); // The icosphere is created by subdividing an icosahedron. // (http://en.wikipedia.org/wiki/Icosahedron) var mesh = CreateIcosahedron(); var tempMesh = new TriangleMesh(); for (int s = 0; s < subdivisions; s++) { int numberOfTriangles = mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) { // Split triangle into 4 triangles. Triangle triangle = mesh.GetTriangle(i); Vector3F v01 = (triangle.Vertex0 + triangle.Vertex1) / 2; Vector3F v12 = (triangle.Vertex1 + triangle.Vertex2) / 2; Vector3F v20 = (triangle.Vertex2 + triangle.Vertex0) / 2; tempMesh.Add(new Triangle(triangle.Vertex0, v01, v20)); tempMesh.Add(new Triangle(v01, v12, v20)); tempMesh.Add(new Triangle(v01, triangle.Vertex1, v12)); tempMesh.Add(new Triangle(v20, v12, triangle.Vertex2)); } MathHelper.Swap(ref mesh, ref tempMesh); tempMesh.Vertices.Clear(); tempMesh.Indices.Clear(); if (hemisphere && s == 0) { // Delete negative hemisphere after first subdivision. numberOfTriangles = mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) { Triangle triangle = mesh.GetTriangle(i); if (Numeric.IsLess(triangle.Vertex0.Y, 0) || Numeric.IsLess(triangle.Vertex1.Y, 0) || Numeric.IsLess(triangle.Vertex2.Y, 0)) { // Skip triangles in negative hemisphere. continue; } tempMesh.Add(triangle); } MathHelper.Swap(ref mesh, ref tempMesh); tempMesh.Vertices.Clear(); tempMesh.Indices.Clear(); } } mesh.WeldVertices(); // The mesh is now a refined icosahedron. Adjust the position of the // vertices to approximate a sphere. int numberOfVertices = mesh.Vertices.Count; for (int i = 0; i < numberOfVertices; i++) { Vector3F v = mesh.Vertices[i]; // Unit sphere: ||v|| = 1 v.Normalize(); mesh.Vertices[i] = v; } return mesh; }
private static TriangleMesh CreateIcosahedron() { var triangleMesh = new TriangleMesh(); // 12 vertices var vertices = triangleMesh.Vertices; vertices.Add(new Vector3F(0.0f, 1.0f, 0.0f)); vertices.Add(new Vector3F(0.894425f, 0.447215f, 0.0f)); vertices.Add(new Vector3F(0.276385f, 0.447215f, -0.85064f)); vertices.Add(new Vector3F(-0.7236f, 0.447215f, -0.52572f)); vertices.Add(new Vector3F(-0.7236f, 0.447215f, 0.52572f)); vertices.Add(new Vector3F(0.276385f, 0.447215f, 0.85064f)); vertices.Add(new Vector3F(0.7236f, -0.447215f, -0.52572f)); vertices.Add(new Vector3F(-0.276385f, -0.447215f, -0.85064f)); vertices.Add(new Vector3F(-0.894425f, -0.447215f, 0.0f)); vertices.Add(new Vector3F(-0.276385f, -0.447215f, 0.85064f)); vertices.Add(new Vector3F(0.7236f, -0.447215f, 0.52572f)); vertices.Add(new Vector3F(0.0f, -1.0f, 0.0f)); // 20 faces var indices = triangleMesh.Indices; indices.Add(0); indices.Add(1); indices.Add(2); indices.Add(0); indices.Add(2); indices.Add(3); indices.Add(0); indices.Add(3); indices.Add(4); indices.Add(0); indices.Add(4); indices.Add(5); indices.Add(0); indices.Add(5); indices.Add(1); indices.Add(1); indices.Add(10); indices.Add(6); indices.Add(1); indices.Add(6); indices.Add(2); indices.Add(2); indices.Add(6); indices.Add(7); indices.Add(2); indices.Add(7); indices.Add(3); indices.Add(3); indices.Add(7); indices.Add(8); indices.Add(3); indices.Add(8); indices.Add(4); indices.Add(4); indices.Add(8); indices.Add(9); indices.Add(4); indices.Add(9); indices.Add(5); indices.Add(5); indices.Add(9); indices.Add(10); indices.Add(5); indices.Add(10); indices.Add(1); indices.Add(11); indices.Add(10); indices.Add(9); indices.Add(11); indices.Add(9); indices.Add(8); indices.Add(11); indices.Add(8); indices.Add(7); indices.Add(11); indices.Add(7); indices.Add(6); indices.Add(11); indices.Add(6); indices.Add(10); return triangleMesh; }
private static Submesh CreateGeoClipmapMesh(GraphicsDevice graphicsDevice, int numberOfLevels, int cellsPerLevel, bool useDiamondTessellation) { if (graphicsDevice == null) throw new ArgumentNullException("graphicsDevice"); if (cellsPerLevel < 1) throw new ArgumentOutOfRangeException("cellsPerLevel", "The number of cells per level must be greater than 0."); // Round to even number of cells. if (cellsPerLevel % 2 != 0) cellsPerLevel++; // Allocate a mesh with conservative list capacities. int cellsPerLevelUpperBound = (cellsPerLevel + 3) * (cellsPerLevel + 3); int verticesPerLevelUpperBound = useDiamondTessellation ? cellsPerLevelUpperBound * 9 : cellsPerLevelUpperBound * 4; int indicesPerLevelUpperBound = useDiamondTessellation ? cellsPerLevelUpperBound * 8 * 3 : cellsPerLevelUpperBound * 2 * 3; var triangleMesh = new TriangleMesh(verticesPerLevelUpperBound * numberOfLevels, indicesPerLevelUpperBound * numberOfLevels); // The levels are created in a separate mesh and then combined into the final mesh. var triangleMeshes = new TriangleMesh[numberOfLevels]; triangleMeshes[0] = triangleMesh; for (int i = 1; i < numberOfLevels; i++) triangleMeshes[i] = new TriangleMesh(verticesPerLevelUpperBound, indicesPerLevelUpperBound); //for (int level = 0; level < numberOfLevels; level++) Parallel.For(0, numberOfLevels, level => { var levelTriangleMesh = triangleMeshes[level]; int cellSize = (1 << level); float y = level; // Store LOD in Y coordinate. Debug.Assert(cellsPerLevel % 2 == 0); int halfWidthInCells = cellsPerLevel / 2; int halfWidth = cellSize * (halfWidthInCells); int minInclusive = -halfWidth - cellSize; // We add an extra border on the top and left. int maxInclusive = halfWidth - cellSize; // Normal loop limit without extra border. int previousHalfWidth = (level > 0) ? halfWidth / 2 : -1; if (useDiamondTessellation) { #region ----- Diamond tessellation ----- int index = 0; for (int z = minInclusive; z <= maxInclusive; z += cellSize) { for (int x = minInclusive; x <= maxInclusive; x += cellSize) { // No triangles in the area which are covered by previous levels. // (We compare cell centers with radius of last LOD.) if (Math.Max(Math.Abs(x + cellSize / 2), Math.Abs(z + cellSize / 2)) < previousHalfWidth) continue; // Each cell will be tessellated like this: // A-----B-----C // | \ | / | // | \ | / | // D-----E-----F // | / | \ | // | / | \ | // G-----H-----I var indexA = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z)); var indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + 0.5f * cellSize, y, z)); var indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z)); var indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z + 0.5f * cellSize)); var indexE = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + 0.5f * cellSize, y, z + 0.5f * cellSize)); var indexF = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z + 0.5f * cellSize)); var indexG = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z + cellSize)); var indexH = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + 0.5f * cellSize, y, z + cellSize)); var indexI = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z + cellSize)); // Triangles using ADEG: if (x != minInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexD); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexD); } else { // The outer cells are tessellated differently to stitch to the next level. // A-----B-----C // | \ | / | // | \ | / | // | E-----F // | / | \ | // | / | \ | // G-----H-----I levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexA); } // Triangles using ABCE: if (z != minInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexB); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexC); } // Triangles using CEFI: if (x != maxInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexF); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexF); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexI); } // Triangles using EGHI: if (z != maxInclusive) { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexH); levelTriangleMesh.Indices.Add(indexG); levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexH); } else { levelTriangleMesh.Indices.Add(indexE); levelTriangleMesh.Indices.Add(indexI); levelTriangleMesh.Indices.Add(indexG); } } } Debug.Assert(levelTriangleMesh.Vertices.Count <= verticesPerLevelUpperBound, "Bad estimate for upper bound of vertices."); Debug.Assert(levelTriangleMesh.Indices.Count <= indicesPerLevelUpperBound, "Bad estimate for upper bound of indices."); levelTriangleMesh.WeldVertices(0.1f); #endregion } else { #region ----- Simple tessellation ----- // Add one extra border to hide gaps. minInclusive -= cellSize; maxInclusive += cellSize; int index = 0; for (int z = minInclusive; z <= maxInclusive; z += cellSize) { for (int x = minInclusive; x <= maxInclusive; x += cellSize) { // No triangles in the area which are covered by previous levels. // (We compare cell centers with radius of last LOD.) if (Math.Max(Math.Abs(x + cellSize / 2), Math.Abs(z + cellSize / 2)) < previousHalfWidth) continue; // Each 2x2 cells will be tessellated like this: // A-----B // | / | // | / | // C-----D int indexA = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z)); int indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z)); int indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x, y, z + cellSize)); int indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3F(x + cellSize, y, z + cellSize)); levelTriangleMesh.Indices.Add(indexA); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexC); levelTriangleMesh.Indices.Add(indexB); levelTriangleMesh.Indices.Add(indexD); } } Debug.Assert(levelTriangleMesh.Vertices.Count <= verticesPerLevelUpperBound, "Bad estimate for upper bound of vertices."); Debug.Assert(levelTriangleMesh.Indices.Count <= indicesPerLevelUpperBound, "Bad estimate for upper bound of indices."); levelTriangleMesh.WeldVertices(0.1f); #endregion } }); // Combine meshes. for (int i = 1; i < numberOfLevels; i++) triangleMesh.Add(triangleMeshes[i]); var vertices = new TerrainVertex[triangleMesh.Vertices.Count]; { int index = 0; for (int i = 0; i < vertices.Length; i++) { Vector3F v = triangleMesh.Vertices[i]; vertices[index++] = new TerrainVertex(new HalfVector4(v.X, v.Y, v.Z, 1)); } } var submesh = CreateSubmesh(graphicsDevice, vertices, triangleMesh.Indices.ToArray()); //Debug.WriteLine("Number of terrain vertices:" + vertices.Length); //Debug.WriteLine("Number of terrain triangles:" + triangleMesh.Indices.Count / 3); return submesh; }
public void EmptyTriangleMesh() { var m = new TriangleMesh(); Assert.AreEqual(0, m.GetVolume()); }
/// <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 CreateObjects() { // Create two collision objects with triangle mesh shapes. var meshA = new TriangleMesh(); meshA.Add(new Triangle(new Vector3F(0, 1, 0), new Vector3F(0, 1, 0), new Vector3F(0, 1, 0))); meshA.Add(new Triangle(new Vector3F(0, 1, 0), new Vector3F(0, 1, 0), new Vector3F(0, 1, 0))); var shapeA = new TriangleMeshShape() { Partition = new CompressedAabbTree() }; var poseA = new Pose(new Vector3F(-1, 0, 0)); _objectA = new CollisionObject(new GeometricObject(shapeA, poseA)); var meshB = new BoxShape(0.2f, 2, 1f).GetMesh(0.05f, 4); var shapeB = new TriangleMeshShape(meshB, true) { Partition = new CompressedAabbTree() }; var poseB = new Pose(new Vector3F(0.1f, 0, 0)); _objectB = new CollisionObject(new GeometricObject(shapeB, poseB)); }
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 (Vector3F.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); }
// Computes the volume of the submerged mesh part and the center of buoyancy. private float GetSubmergedVolume(Vector3F scale, Pose pose, TriangleMesh mesh, out Vector3F center) { center = Vector3F.Zero; // Get surface plane in local space. Plane planeLocal = new Plane { Normal = pose.ToLocalDirection(Surface.Normal), DistanceFromOrigin = Surface.DistanceFromOrigin - Vector3F.Dot(Surface.Normal, pose.Position), }; const float tinyDepth = -1e-6f; // Vertex heights relative to surface plane. Positive = above water. int numberOfVertices = mesh.Vertices.Count; // Compute depth of each vertex. List<float> depths = ResourcePools<float>.Lists.Obtain(); // Use try-finally block to properly recycle resources from pool. try { int numberOfSubmergedVertices = 0; int sampleVertexIndex = 0; for (int i = 0; i < numberOfVertices; i++) { float depth = Vector3F.Dot(planeLocal.Normal, mesh.Vertices[i] * scale) - planeLocal.DistanceFromOrigin; if (depth < tinyDepth) { numberOfSubmergedVertices++; sampleVertexIndex = i; } depths.Add(depth); } // Abort if no vertex is in water. if (numberOfSubmergedVertices == 0) return 0; // Get the reference point. We project a submerged vertex onto the surface plane. Vector3F point = mesh.Vertices[sampleVertexIndex] - depths[sampleVertexIndex] * planeLocal.Normal; float volume = 0; // Add contribution of each triangle. int numberOfTriangles = mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) { // Triangle vertex indices. int i0 = mesh.Indices[i * 3 + 0]; int i1 = mesh.Indices[i * 3 + 1]; int i2 = mesh.Indices[i * 3 + 2]; // Vertices and depths. Vector3F v0 = mesh.Vertices[i0] * scale; float d0 = depths[i0]; Vector3F v1 = mesh.Vertices[i1] * scale; float d1 = depths[i1]; Vector3F v2 = mesh.Vertices[i2] * scale; float d2 = depths[i2]; if (d0 * d1 < 0) { // v0 - v1 crosses the surface. volume += ClipTriangle(point, v0, v1, v2, d0, d1, d2, ref center); } else if (d0 * d2 < 0) { // v0 - v2 crosses the surface. volume += ClipTriangle(point, v2, v0, v1, d2, d0, d1, ref center); } else if (d1 * d2 < 0) { // v1 - v2 crosses the surface. volume += ClipTriangle(point, v1, v2, v0, d1, d2, d0, ref center); } else if (d0 < 0 || d1 < 0 || d2 < 0) { // Fully submerged triangle. volume += GetSignedTetrahedronVolume(point, v0, v1, v2, ref center); } } // If the volume is near zero or negative (numerical errors), we abort. const float tinyVolume = 1e-6f; if (volume <= tinyVolume) { center = Vector3F.Zero; return 0; } // Normalize the center (was weighted by volume). center = center / volume; // Transform center to world space. center = pose.ToWorldPosition(center); return volume; } finally { // Recycle temporary collections. ResourcePools<float>.Lists.Recycle(depths); } }
public TriangulationSample(Microsoft.Xna.Framework.Game game) : base(game) { GraphicsScreen.ClearBackground = true; _mesh = new TriangleMesh(); }
/// <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> /// <remarks> /// This method creates a triangle mesh that represents a square lying in the plane. The square /// has an edge length of <see cref="MeshSize"/>. /// </remarks> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { Vector3F center = Normal * DistanceFromOrigin; Vector3F orthoNormal1 = Normal.Orthonormal1; Vector3F orthoNormal2 = Normal.Orthonormal2; // Plane // Make 4 triangles around the center. TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle { Vertex0 = center, Vertex1 = center + orthoNormal1 * MeshSize / 2 - orthoNormal2 * MeshSize / 2, Vertex2 = center + orthoNormal1 * MeshSize / 2 + orthoNormal2 * MeshSize / 2, }, true); mesh.Add(new Triangle { Vertex0 = center, Vertex1 = center + orthoNormal1 * MeshSize / 2 + orthoNormal2 * MeshSize / 2, Vertex2 = center - orthoNormal1 * MeshSize / 2 + orthoNormal2 * MeshSize / 2, }, true); mesh.Add(new Triangle { Vertex0 = center, Vertex1 = center - orthoNormal1 * MeshSize / 2 + orthoNormal2 * MeshSize / 2, Vertex2 = center - orthoNormal1 * MeshSize / 2 - orthoNormal2 * MeshSize / 2, }, true); mesh.Add(new Triangle { Vertex0 = center, Vertex1 = center - orthoNormal1 * MeshSize / 2 - orthoNormal2 * MeshSize / 2, Vertex2 = center + orthoNormal1 * MeshSize / 2 - orthoNormal2 * MeshSize / 2, }, true); return mesh; }
public static TriangleMesh ToTriangleMesh(this Submesh submesh) { var triangleMesh = new TriangleMesh(); ToTriangleMesh(submesh, triangleMesh); return triangleMesh; }
/// <summary> /// Converts this mesh to a <see cref="TriangleMesh"/>. /// </summary> /// <returns>A triangle mesh that represents the same mesh.</returns> /// <remarks> /// This mesh must consist only of faces that are convex polygons (triangles, quads, etc.). /// Concave faces will not be triangulated correctly. /// </remarks> /// <exception cref="GeometryException">The DCEL mesh is invalid.</exception> public TriangleMesh ToTriangleMesh() { TriangleMesh triMesh = new TriangleMesh(); UpdateCache(); int numberOfVertices = Vertices.Count; int numberOfFaces = Faces.Count; for (int i = 0; i < numberOfVertices; i++) { DcelVertex vertex = Vertices[i]; // Store the vertex indices in the InternalTags of the vertices. vertex.InternalTag = i; // Add all vertices to the triangle mesh. triMesh.Vertices.Add(vertex.Position); } for (int i = 0; i < numberOfFaces; i++) { var face = Faces[i]; var startEdge = face.Boundary; var nextEdge = startEdge.Next; // Tag edges to see if they have been visited. startEdge.InternalTag = 1; nextEdge.InternalTag = 1; // Triangulate polygon. var v0 = startEdge.Origin; var v1 = nextEdge.Origin; nextEdge = nextEdge.Next; while (nextEdge != startEdge) { if (nextEdge == null || nextEdge.InternalTag == 1) throw new GeometryException("DCEL mesh is invalid."); nextEdge.InternalTag = 1; var v2 = nextEdge.Origin; // The tags of the vertices store their index number. triMesh.Indices.Add(v0.InternalTag); triMesh.Indices.Add(v1.InternalTag); triMesh.Indices.Add(v2.InternalTag); v1 = v2; nextEdge = nextEdge.Next; } } ResetInternalTags(); return triMesh; }
internal static void CreateVertexBuffer(GraphicsDevice graphicsDevice, TriangleMesh mesh, float angleLimit, out VertexBuffer vertexBuffer, out PrimitiveType primitiveType, out int primitiveCount) { // Note: We do not use shared vertices and an IndexBuffer because a vertex can be used by // several triangles with different normals. if (graphicsDevice == null) throw new ArgumentNullException("graphicsDevice"); if (mesh == null) throw new ArgumentNullException("mesh"); var numberOfTriangles = mesh.NumberOfTriangles; if (numberOfTriangles == 0) { primitiveType = PrimitiveType.TriangleList; primitiveCount = 0; vertexBuffer = null; return; } primitiveType = PrimitiveType.TriangleList; primitiveCount = numberOfTriangles; int vertexCount = numberOfTriangles * 3; // Create vertex data for a triangle list. var vertices = new VertexPositionNormal[vertexCount]; // Create vertex normals. var normals = mesh.ComputeNormals(false, angleLimit); for (int i = 0; i < numberOfTriangles; i++) { var i0 = mesh.Indices[i * 3 + 0]; var i1 = mesh.Indices[i * 3 + 1]; var i2 = mesh.Indices[i * 3 + 2]; var v0 = mesh.Vertices[i0]; var v1 = mesh.Vertices[i1]; var v2 = mesh.Vertices[i2]; Vector3F n0, n1, n2; if (angleLimit < 0) { // If the angle limit is negative, ComputeNormals() returns one normal per vertex. n0 = normals[i0]; n1 = normals[i1]; n2 = normals[i2]; } else { // If the angle limits is >= 0, ComputeNormals() returns 3 normals per triangle. n0 = normals[i * 3 + 0]; n1 = normals[i * 3 + 1]; n2 = normals[i * 3 + 2]; } // Add new vertex data. // DigitalRune.Geometry uses counter-clockwise front faces. XNA uses // clockwise front faces (CullMode.CullCounterClockwiseFace) per default. // Therefore we change the vertex orientation of the triangles. vertices[i * 3 + 0] = new VertexPositionNormal((Vector3)v0, (Vector3)n0); vertices[i * 3 + 1] = new VertexPositionNormal((Vector3)v2, (Vector3)n2); // v2 instead of v1! vertices[i * 3 + 2] = new VertexPositionNormal((Vector3)v1, (Vector3)n1); } // Create a vertex buffer. vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionNormal), vertexCount, BufferUsage.None); vertexBuffer.SetData(vertices); }
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 (Vector3F.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; }
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) { TriangleMesh mesh = new TriangleMesh(); int numberOfCellsInX = _numberOfSamplesX - 1; int numberOfCellsInZ = _numberOfSamplesZ - 1; // Add vertex positions. for (int z = 0; z < _numberOfSamplesZ; z++) { for (int x = 0; x < _numberOfSamplesX; x++) { mesh.Vertices.Add( new Vector3F( _originX + (float)x / numberOfCellsInX * WidthX, _samples[z * NumberOfSamplesX + x], _originZ + (float)z / numberOfCellsInZ * WidthZ)); } } // Add triangle indices. for (int z = 0; z < numberOfCellsInZ; z++) { for (int x = 0; x < numberOfCellsInX; x++) { // Check for holes. // Shared vertices of both triangles. var height = _samples[(z + 1) * _numberOfSamplesX + x] * _samples[z * _numberOfSamplesX + (x + 1)]; if (!Numeric.IsFinite(height)) continue; // First triangle. if (Numeric.IsFinite(_samples[z * _numberOfSamplesX + x])) { mesh.Indices.Add(z * _numberOfSamplesX + x); mesh.Indices.Add((z + 1) * _numberOfSamplesX + x); mesh.Indices.Add(z * _numberOfSamplesX + x + 1); } // Second triangle. if (Numeric.IsFinite(_samples[(z + 1) * _numberOfSamplesX + (x + 1)])) { mesh.Indices.Add(z * _numberOfSamplesX + x + 1); mesh.Indices.Add((z + 1) * _numberOfSamplesX + x); mesh.Indices.Add((z + 1) * _numberOfSamplesX + x + 1); } } } 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); alpha = ConstantsF.TwoPi / numberOfSegments; Vector3F r0 = new Vector3F(_radius, 0, 0); QuaternionF rotation = QuaternionF.CreateRotationZ(alpha); TriangleMesh mesh = new TriangleMesh(); for (int i = 1; i <= numberOfSegments; i++) { Vector3F r1 = rotation.Rotate(r0); mesh.Add(new Triangle { Vertex0 = Vector3F.Zero, Vertex1 = r0, Vertex2 = r1, }, true); r0 = r1; } return mesh; }
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((Vector3F)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); }