public void SerializationBinary() { var a = new TriangleMesh(); a.Add(new Triangle(new Vector3(0, 1, 2), new Vector3(3, 4, 5), new Vector3(6, 7, 8)), false); a.Add(new Triangle(new Vector3(-0, -1, -2), new Vector3(-3, -4, -5), new Vector3(-6, -7, -8)), false); // Serialize object. var stream = new MemoryStream(); var formatter = new BinaryFormatter(); formatter.Serialize(stream, a); // Deserialize object. stream.Position = 0; var deserializer = new BinaryFormatter(); var b = (TriangleMesh)deserializer.Deserialize(stream); Assert.AreEqual(a.NumberOfTriangles, b.NumberOfTriangles); for (int i = 0; i < a.Vertices.Count; i++) { Assert.AreEqual(a.Vertices[i], b.Vertices[i]); } for (int i = 0; i < a.Indices.Count; i++) { Assert.AreEqual(a.Indices[i], b.Indices[i]); } }
public void SerializationXml() { var a = new TriangleMesh(); a.Add(new Triangle(new Vector3(0, 1, 2), new Vector3(3, 4, 5), new Vector3(6, 7, 8)), true); a.Add(new Triangle(new Vector3(-0, -1, -2), new Vector3(-3, -4, -5), new Vector3(-6, -7, -8)), true); // Serialize object. var stream = new MemoryStream(); var serializer = new XmlSerializer(typeof(TriangleMesh)); serializer.Serialize(stream, a); // Output generated xml. Can be manually checked in output window. stream.Position = 0; var xml = new StreamReader(stream).ReadToEnd(); Trace.WriteLine("Serialized Object:\n" + xml); // Deserialize object. stream.Position = 0; var deserializer = new XmlSerializer(typeof(TriangleMesh)); var b = (TriangleMesh)deserializer.Deserialize(stream); Assert.AreEqual(a.NumberOfTriangles, b.NumberOfTriangles); for (int i = 0; i < a.Vertices.Count; i++) { Assert.AreEqual(a.Vertices[i], b.Vertices[i]); } for (int i = 0; i < a.Indices.Count; i++) { Assert.AreEqual(a.Indices[i], b.Indices[i]); } }
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); }
public void AddMesh() { TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(1, 1, 1), new Vector3F(1, 0, 2)), true); mesh.Add(new Triangle(new Vector3F(0, 1, 0), new Vector3F(1, 2, 1), new Vector3F(1, 1, 1)), true); var mesh2 = new TriangleMesh(); mesh2.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(1, 1, 1), new Vector3F(1, 0, 1)), true); mesh2.Add(mesh, true); Assert.AreEqual(3, mesh2.NumberOfTriangles); Assert.AreEqual(new Triangle(new Vector3F(0, 1, 0), new Vector3F(1, 2, 1), new Vector3F(1, 1, 1)), mesh2.GetTriangle(2)); }
public void Transform() { var mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3(0, 1, 2), new Vector3(3, 4, 5), new Vector3(6, 7, 8)), true); mesh.Add(new Triangle(new Vector3(-0, -1, -2), new Vector3(-3, -4, -5), new Vector3(-6, -7, -8)), true); var trans = RandomHelper.Random.NextMatrix(-1, 1); mesh.Transform(trans); Assert.AreEqual(trans.TransformPosition(new Vector3(0, 1, 2)), mesh.Vertices[0]); Assert.AreEqual(trans.TransformPosition(new Vector3(-6, -7, -8)), mesh.Vertices[5]); }
public void AddMesh() { TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3(0, 0, 0), new Vector3(1, 1, 1), new Vector3(1, 0, 2)), true); mesh.Add(new Triangle(new Vector3(0, 1, 0), new Vector3(1, 2, 1), new Vector3(1, 1, 1)), true); var mesh2 = new TriangleMesh(); mesh2.Add(new Triangle(new Vector3(0, 0, 0), new Vector3(1, 1, 1), new Vector3(1, 0, 1)), true); mesh2.Add(mesh, true); Assert.AreEqual(3, mesh2.NumberOfTriangles); Assert.AreEqual(new Triangle(new Vector3(0, 1, 0), new Vector3(1, 2, 1), new Vector3(1, 1, 1)), mesh2.GetTriangle(2)); }
/// <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(this), true); 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) { // Convert absolute error to relative error. float maxExtent = GetAabb(Vector3.One, Pose.Identity).Extent.LargestComponent; float relativeThreshold = !Numeric.IsZero(maxExtent) ? absoluteDistanceThreshold / maxExtent : Numeric.EpsilonF; // Get meshes of children and add them to mesh in parent space. TriangleMesh mesh = new TriangleMesh(); int numberOfGeometries = Children.Count; for (int childIndex = 0; childIndex < numberOfGeometries; childIndex++) { IGeometricObject geometricObject = Children[childIndex]; // Get child mesh. var childMesh = geometricObject.Shape.GetMesh(relativeThreshold, iterationLimit); // Transform child mesh into local space of this parent shape. childMesh.Transform(geometricObject.Pose.ToMatrix() * Matrix.CreateScale(geometricObject.Scale)); // Add to parent mesh. mesh.Add(childMesh, false); } 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; Vector3 r0 = new Vector3(_radius, 0, 0); Quaternion rotation = Quaternion.CreateRotationZ(alpha); TriangleMesh mesh = new TriangleMesh(); for (int i = 1; i <= numberOfSegments; i++) { Vector3 r1 = rotation.Rotate(r0); mesh.Add(new Triangle { Vertex0 = Vector3.Zero, Vertex1 = r0, Vertex2 = r1, }, true); r0 = r1; } return(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)); }
public void AddTriangleShouldRemoveDegenerateTriangle2() { var mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1.000001f)), false, 0.001f, true); Assert.AreEqual(0, mesh.NumberOfTriangles); }
public void GetTriangleException2() { TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3(0, 0, 0), new Vector3(1, 1, 1), new Vector3(1, 0, 1)), false); mesh.GetTriangle(1); }
public void WeldVertices() { TriangleMesh mesh = new TriangleMesh(); Assert.AreEqual(0, mesh.WeldVertices()); mesh.Add(new Triangle(new Vector3(1, 2, 3), new Vector3(3, 4, 5), new Vector3(1.00001f, 2.00001f, 3f)), false); Assert.AreEqual(3, mesh.Vertices.Count); Assert.Throws(typeof(ArgumentOutOfRangeException), () => mesh.WeldVertices(-0.1f)); Assert.AreEqual(1, mesh.WeldVertices(0.0001f)); Assert.AreEqual(2, mesh.Vertices.Count); var w = Stopwatch.StartNew(); mesh = new SphereShape(0.5f).GetMesh(0.001f, 7); w.Stop(); //Assert.AreEqual(0, w.Elapsed.TotalMilliseconds); for (int i = 0; i < mesh.Vertices.Count; i++) { for (int j = i + 1; j < mesh.Vertices.Count; j++) { Assert.IsFalse(Vector3.AreNumericallyEqual(mesh.Vertices[i], mesh.Vertices[j])); } } // Second time does nothing. Assert.AreEqual(0, mesh.WeldVertices()); }
/// <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; Vector3 r0 = new Vector3(_radius, 0, 0); Vector3 tip = new Vector3(0, _height, 0); Quaternion rotation = Quaternion.CreateRotationY(alpha); TriangleMesh mesh = new TriangleMesh(); for (int i = 1; i <= numberOfSegments; i++) { Vector3 r1 = rotation.Rotate(r0); // Bottom triangle mesh.Add(new Triangle { Vertex0 = Vector3.Zero, Vertex1 = r1, Vertex2 = r0, }, false); // Side triangle mesh.Add(new Triangle { Vertex0 = r0, Vertex1 = r1, Vertex2 = tip, }, false); r0 = r1; } mesh.WeldVertices(); return(mesh); }
/// <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); }
/// <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 void Clone() { var mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3F(0, 1, 2), new Vector3F(3, 4, 5), new Vector3F(6, 7, 8)), true); mesh.Add(new Triangle(new Vector3F(-0, -1, -2), new Vector3F(-3, -4, -5), new Vector3F(-6, -7, -8)), true); mesh.Tag = new SphereShape(3); var clone = mesh.Clone(); Assert.AreEqual(mesh.NumberOfTriangles, clone.NumberOfTriangles); Assert.AreEqual(mesh.GetTriangle(0), clone.GetTriangle(0)); Assert.AreEqual(mesh.GetTriangle(1), clone.GetTriangle(1)); Assert.AreSame(mesh.Tag, clone.Tag); mesh.Tag = new MemoryStream(); clone = mesh.Clone(); Assert.AreSame(mesh.Tag, clone.Tag); }
public void Clone() { var mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3(0, 1, 2), new Vector3(3, 4, 5), new Vector3(6, 7, 8)), true); mesh.Add(new Triangle(new Vector3(-0, -1, -2), new Vector3(-3, -4, -5), new Vector3(-6, -7, -8)), true); mesh.Tag = new SphereShape(3); var clone = mesh.Clone(); Assert.AreEqual(mesh.NumberOfTriangles, clone.NumberOfTriangles); Assert.AreEqual(mesh.GetTriangle(0), clone.GetTriangle(0)); Assert.AreEqual(mesh.GetTriangle(1), clone.GetTriangle(1)); Assert.AreSame(mesh.Tag, clone.Tag); mesh.Tag = new MemoryStream(); clone = mesh.Clone(); Assert.AreSame(mesh.Tag, clone.Tag); }
public void Validate6() { GlobalSettings.ValidationLevel = 0xff; var scene = new Scene(); // This is allowed. var n = new TestSceneNode { Shape = Shape.Empty }; scene.Children.Add(n); // Invalid changes of already added node: var mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3F(1), new Vector3F(2), new Vector3F(3))); mesh.Add(new Triangle(new Vector3F(4), new Vector3F(float.NaN, 5, 5), new Vector3F(6))); var meshShape = new TriangleMeshShape(mesh); Assert.Throws <GraphicsException>(() => n.Shape = meshShape); }
public void AddTriangleWithoutMergingAndWithDestroyedLists() { var mesh = new TriangleMesh(); // Destroy lists. mesh.Vertices = null; mesh.Indices = null; Assert.AreEqual(0, mesh.NumberOfTriangles); mesh.Add(new Triangle(new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1.000001f)), false, 0.001f, false); Assert.AreEqual(3, mesh.Vertices.Count); }
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); }
public void AddTriangleWithoutMergingAndWithDestroyedLists() { var mesh = new TriangleMesh(); // Destroy lists. mesh.Vertices = null; mesh.Indices = null; Assert.AreEqual(0, mesh.NumberOfTriangles); mesh.Add(new Triangle(new Vector3F(1, 1, 1), new Vector3F(1, 1, 1), new Vector3F(1, 1, 1.000001f)), false, 0.001f, false); Assert.AreEqual(3, mesh.Vertices.Count); }
/// <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) { Vector3 center = Normal * DistanceFromOrigin; Vector3 orthoNormal1 = Normal.Orthonormal1; Vector3 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 void GetTriangle() { Triangle t1 = new Triangle(new Vector3(0, 0, 0), new Vector3(1, 1, 1), new Vector3(1, 0, 1)); TriangleMesh mesh = new TriangleMesh(); mesh.Add(t1, false); var t2 = mesh.GetTriangle(0); Assert.AreEqual(t1.Vertex0, t2.Vertex0); Assert.AreEqual(t1.Vertex1, t2.Vertex1); Assert.AreEqual(t1.Vertex2, t2.Vertex2); }
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)); }
/// <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 returns a mesh with a single degenerate triangle. The triangle represents a /// line with the length <see cref="MeshSize"/>. The triangle is centered on /// <see cref="PointOnLine"/>. /// </remarks> protected override TriangleMesh OnGetMesh(float absoluteDistanceThreshold, int iterationLimit) { Vector3F start = PointOnLine - Direction * (MeshSize / 2); Vector3F end = PointOnLine + Direction * (MeshSize / 2); // Make a mesh with 1 degenerate triangle TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle { Vertex0 = start, Vertex1 = start, Vertex2 = end, }, true, Numeric.EpsilonF, false); 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 creates a mesh with a single degenerate triangle that represents the line segment. /// </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 = Start, Vertex1 = Start, Vertex2 = End, }, true, Numeric.EpsilonF, false); return(mesh); }
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 * Matrix.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); }
/// <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); }
public void AddTriangleToleranceMustBeGreaterNull() { TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle(), true, 0, true); }
public void AddTriangleShouldRemoveDegenerateTriangle2() { var mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3F(1, 1, 1), new Vector3F(1, 1, 1), new Vector3F(1, 1, 1.000001f)), false, 0.001f, true); Assert.AreEqual(0, mesh.NumberOfTriangles); }
public void WeldVertices() { TriangleMesh mesh = new TriangleMesh(); Assert.AreEqual(0, mesh.WeldVertices()); mesh.Add(new Triangle(new Vector3F(1, 2, 3), new Vector3F(3, 4, 5), new Vector3F(1.00001f, 2.00001f, 3f)), false); Assert.AreEqual(3, mesh.Vertices.Count); Assert.Throws(typeof(ArgumentOutOfRangeException), () => mesh.WeldVertices(-0.1f)); Assert.AreEqual(1, mesh.WeldVertices(0.0001f)); Assert.AreEqual(2, mesh.Vertices.Count); var w = Stopwatch.StartNew(); mesh = new SphereShape(0.5f).GetMesh(0.001f, 7); w.Stop(); //Assert.AreEqual(0, w.Elapsed.TotalMilliseconds); for (int i = 0; i < mesh.Vertices.Count; i++) { for (int j = i + 1; j < mesh.Vertices.Count; j++) { Assert.IsFalse(Vector3F.AreNumericallyEqual(mesh.Vertices[i], mesh.Vertices[j])); } } // Second time does nothing. Assert.AreEqual(0, mesh.WeldVertices()); }
public void SerializationBinary() { var a = new TriangleMesh(); a.Add(new Triangle(new Vector3F(0, 1, 2), new Vector3F(3, 4, 5), new Vector3F(6, 7, 8)), false); a.Add(new Triangle(new Vector3F(-0, -1, -2), new Vector3F(-3, -4, -5), new Vector3F(-6, -7, -8)), false); // Serialize object. var stream = new MemoryStream(); var formatter = new BinaryFormatter(); formatter.Serialize(stream, a); // Deserialize object. stream.Position = 0; var deserializer = new BinaryFormatter(); var b = (TriangleMesh)deserializer.Deserialize(stream); Assert.AreEqual(a.NumberOfTriangles, b.NumberOfTriangles); for (int i = 0; i < a.Vertices.Count; i++) Assert.AreEqual(a.Vertices[i], b.Vertices[i]); for (int i = 0; i < a.Indices.Count; i++) Assert.AreEqual(a.Indices[i], b.Indices[i]); }
public void GetTriangleException2() { TriangleMesh mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3F(0, 0, 0), new Vector3F(1, 1, 1), new Vector3F(1, 0, 1)), false); mesh.GetTriangle(1); }
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) { 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 Vector3(x, y, z)); var indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + 0.5f * cellSize, y, z)); var indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z)); var indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z + 0.5f * cellSize)); var indexE = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + 0.5f * cellSize, y, z + 0.5f * cellSize)); var indexF = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z + 0.5f * cellSize)); var indexG = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z + cellSize)); var indexH = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + 0.5f * cellSize, y, z + cellSize)); var indexI = index++; levelTriangleMesh.Vertices.Add(new Vector3(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); } else { // 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 Vector3(x, y, z)); int indexB = index++; levelTriangleMesh.Vertices.Add(new Vector3(x + cellSize, y, z)); int indexC = index++; levelTriangleMesh.Vertices.Add(new Vector3(x, y, z + cellSize)); int indexD = index++; levelTriangleMesh.Vertices.Add(new Vector3(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); } }); // 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++) { Vector3 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); }
/// <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) { // Get coordinates of corners: float near = -Math.Min(Near, Far); float far = -Math.Max(Near, Far); float leftNear = Math.Min(Left, Right); float rightNear = Math.Max(Left, Right); float topNear = Math.Max(Top, Bottom); float bottomNear = Math.Min(Top, Bottom); float farFactor = 1 / near * far; // Multiply near-values by this factor to get far-values. float leftFar = leftNear * farFactor; float rightFar = rightNear * farFactor; float topFar = topNear * farFactor; float bottomFar = bottomNear * farFactor; TriangleMesh mesh = new TriangleMesh(); // -y face mesh.Add(new Triangle { Vertex0 = new Vector3F(leftNear, bottomNear, near), Vertex1 = new Vector3F(leftFar, bottomFar, far), Vertex2 = new Vector3F(rightFar, bottomFar, far), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(rightFar, bottomFar, far), Vertex1 = new Vector3F(rightNear, bottomNear, near), Vertex2 = new Vector3F(leftNear, bottomNear, near), }, true); // +x face mesh.Add(new Triangle { Vertex0 = new Vector3F(rightNear, topNear, near), Vertex1 = new Vector3F(rightNear, bottomNear, near), Vertex2 = new Vector3F(rightFar, bottomFar, far), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(rightFar, bottomFar, far), Vertex1 = new Vector3F(rightFar, topFar, far), Vertex2 = new Vector3F(rightNear, topNear, near), }, true); // -z face mesh.Add(new Triangle { Vertex0 = new Vector3F(rightFar, topFar, far), Vertex1 = new Vector3F(rightFar, bottomFar, far), Vertex2 = new Vector3F(leftFar, bottomFar, far), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(leftFar, bottomFar, far), Vertex1 = new Vector3F(leftFar, topFar, far), Vertex2 = new Vector3F(rightFar, topFar, far), }, true); // -x face mesh.Add(new Triangle { Vertex0 = new Vector3F(leftFar, topFar, far), Vertex1 = new Vector3F(leftFar, bottomFar, far), Vertex2 = new Vector3F(leftNear, bottomNear, near), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(leftNear, bottomNear, near), Vertex1 = new Vector3F(leftNear, topNear, near), Vertex2 = new Vector3F(leftFar, topFar, far), }, true); // +z face mesh.Add(new Triangle { Vertex0 = new Vector3F(leftNear, topNear, near), Vertex1 = new Vector3F(leftNear, bottomNear, near), Vertex2 = new Vector3F(rightNear, bottomNear, near), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(rightNear, bottomNear, near), Vertex1 = new Vector3F(rightNear, topNear, near), Vertex2 = new Vector3F(leftNear, topNear, near), }, true); // +y face mesh.Add(new Triangle { Vertex0 = new Vector3F(leftFar, topFar, far), Vertex1 = new Vector3F(leftNear, topNear, near), Vertex2 = new Vector3F(rightNear, topNear, near), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3F(rightNear, topNear, near), Vertex1 = new Vector3F(rightFar, topFar, far), Vertex2 = new Vector3F(leftFar, topFar, far), }, true); return(mesh); }
public void GetTriangle() { Triangle t1 = new Triangle(new Vector3F(0, 0, 0), new Vector3F(1, 1, 1), new Vector3F(1, 0, 1)); TriangleMesh mesh = new TriangleMesh(); mesh.Add(t1, false); var t2 = mesh.GetTriangle(0); Assert.AreEqual(t1.Vertex0, t2.Vertex0); Assert.AreEqual(t1.Vertex1, t2.Vertex1); Assert.AreEqual(t1.Vertex2, t2.Vertex2); }
public void SerializationXml() { var a = new TriangleMesh(); a.Add(new Triangle(new Vector3F(0, 1, 2), new Vector3F(3, 4, 5), new Vector3F(6, 7, 8)), true); a.Add(new Triangle(new Vector3F(-0, -1, -2), new Vector3F(-3, -4, -5), new Vector3F(-6, -7, -8)), true); // Serialize object. var stream = new MemoryStream(); var serializer = new XmlSerializer(typeof(TriangleMesh)); serializer.Serialize(stream, a); // Output generated xml. Can be manually checked in output window. stream.Position = 0; var xml = new StreamReader(stream).ReadToEnd(); Trace.WriteLine("Serialized Object:\n" + xml); // Deserialize object. stream.Position = 0; var deserializer = new XmlSerializer(typeof(TriangleMesh)); var b = (TriangleMesh)deserializer.Deserialize(stream); Assert.AreEqual(a.NumberOfTriangles, b.NumberOfTriangles); for (int i = 0; i < a.Vertices.Count; i++) Assert.AreEqual(a.Vertices[i], b.Vertices[i]); for (int i = 0; i < a.Indices.Count; i++) Assert.AreEqual(a.Indices[i], b.Indices[i]); }
public void Transform() { var mesh = new TriangleMesh(); mesh.Add(new Triangle(new Vector3F(0, 1, 2), new Vector3F(3, 4, 5), new Vector3F(6, 7, 8)), true); mesh.Add(new Triangle(new Vector3F(-0, -1, -2), new Vector3F(-3, -4, -5), new Vector3F(-6, -7, -8)), true); var trans = RandomHelper.Random.NextMatrix44F(-1, 1); mesh.Transform(trans); Assert.AreEqual(trans.TransformPosition(new Vector3F(0, 1, 2)), mesh.Vertices[0]); Assert.AreEqual(trans.TransformPosition(new Vector3F(-6, -7, -8)), mesh.Vertices[5]); }
/// <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 Vector3(-halfExtentX, -halfExtentY, halfExtentZ), Vertex1 = new Vector3(-halfExtentX, -halfExtentY, -halfExtentZ), Vertex2 = new Vector3(halfExtentX, -halfExtentY, -halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3(halfExtentX, -halfExtentY, -halfExtentZ), Vertex1 = new Vector3(halfExtentX, -halfExtentY, halfExtentZ), Vertex2 = new Vector3(-halfExtentX, -halfExtentY, halfExtentZ), }, true); // +x face mesh.Add(new Triangle { Vertex0 = new Vector3(halfExtentX, halfExtentY, halfExtentZ), Vertex1 = new Vector3(halfExtentX, -halfExtentY, halfExtentZ), Vertex2 = new Vector3(halfExtentX, -halfExtentY, -halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3(halfExtentX, -halfExtentY, -halfExtentZ), Vertex1 = new Vector3(halfExtentX, halfExtentY, -halfExtentZ), Vertex2 = new Vector3(halfExtentX, halfExtentY, halfExtentZ), }, true); // -z face mesh.Add(new Triangle { Vertex0 = new Vector3(halfExtentX, halfExtentY, -halfExtentZ), Vertex1 = new Vector3(halfExtentX, -halfExtentY, -halfExtentZ), Vertex2 = new Vector3(-halfExtentX, -halfExtentY, -halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3(-halfExtentX, -halfExtentY, -halfExtentZ), Vertex1 = new Vector3(-halfExtentX, halfExtentY, -halfExtentZ), Vertex2 = new Vector3(halfExtentX, halfExtentY, -halfExtentZ), }, true); // -x face mesh.Add(new Triangle { Vertex0 = new Vector3(-halfExtentX, halfExtentY, -halfExtentZ), Vertex1 = new Vector3(-halfExtentX, -halfExtentY, -halfExtentZ), Vertex2 = new Vector3(-halfExtentX, -halfExtentY, halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3(-halfExtentX, -halfExtentY, halfExtentZ), Vertex1 = new Vector3(-halfExtentX, halfExtentY, halfExtentZ), Vertex2 = new Vector3(-halfExtentX, halfExtentY, -halfExtentZ), }, true); // +z face mesh.Add(new Triangle { Vertex0 = new Vector3(-halfExtentX, halfExtentY, halfExtentZ), Vertex1 = new Vector3(-halfExtentX, -halfExtentY, halfExtentZ), Vertex2 = new Vector3(halfExtentX, -halfExtentY, halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3(halfExtentX, -halfExtentY, halfExtentZ), Vertex1 = new Vector3(halfExtentX, halfExtentY, halfExtentZ), Vertex2 = new Vector3(-halfExtentX, halfExtentY, halfExtentZ), }, true); // +y face mesh.Add(new Triangle { Vertex0 = new Vector3(-halfExtentX, halfExtentY, -halfExtentZ), Vertex1 = new Vector3(-halfExtentX, halfExtentY, halfExtentZ), Vertex2 = new Vector3(halfExtentX, halfExtentY, halfExtentZ), }, true); mesh.Add(new Triangle { Vertex0 = new Vector3(halfExtentX, halfExtentY, halfExtentZ), Vertex1 = new Vector3(halfExtentX, halfExtentY, -halfExtentZ), Vertex2 = new Vector3(-halfExtentX, halfExtentY, -halfExtentZ), }, true); return(mesh); }