/** * Flip triangle normal orientation. * */ public static void Flip(ITriangleMesh mesh) { for (int i = 0; i < mesh.GetNumberOfTriangles(); i++) { mesh.GetTriangle(i).Flip(); } }
/// <summary> /// Gets the enclosed volume of a triangle mesh. /// </summary> /// <param name="triangleMesh">The triangle mesh.</param> /// <returns> /// The enclosed volume of the given triangle mesh. /// </returns> /// <remarks> /// <para> /// This method assumes that the given triangle mesh is a closed mesh without holes. /// </para> /// <para> /// Remember: To compute the volume of a scaled mesh, you can compute the volume of the /// unscaled mesh and multiply the result with the scaling factors: /// </para> /// <para> /// <i>volume<sub>scaled</sub> = volume<sub>unscaled</sub> * scale<sub>X</sub> * scale<sub>Y</sub> * scale<sub>Z</sub></i> /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="triangleMesh"/> is <see langword="null"/>. /// </exception> public static float GetVolume(this ITriangleMesh triangleMesh) { if (triangleMesh == null) { throw new ArgumentNullException("triangleMesh"); } int numberOfTriangles = triangleMesh.NumberOfTriangles; if (numberOfTriangles == 0) { return(0); } // The reference points is on the first triangle. So the first tetrahedron has no volume. var p = triangleMesh.GetTriangle(0).Vertex0; float volume = 0; for (int i = 1; i < numberOfTriangles; i++) { var triangle = triangleMesh.GetTriangle(i); float tetrahedronVolume = GetSignedTetrahedronVolume(p, ref triangle); volume += tetrahedronVolume; } return(volume); }
/** * Create cube. * */ public static void CreateCube(ITriangleMesh mesh, float sideLength) { mesh.Clear(); mesh.AddVertex(new Vector3(-sideLength / 2.0f, -sideLength / 2.0f, -sideLength / 2.0f)); mesh.AddVertex(new Vector3(sideLength / 2.0f, -sideLength / 2.0f, -sideLength / 2.0f)); mesh.AddVertex(new Vector3(sideLength / 2.0f, sideLength / 2.0f, -sideLength / 2.0f)); mesh.AddVertex(new Vector3(-sideLength / 2.0f, sideLength / 2.0f, -sideLength / 2.0f)); mesh.AddVertex(new Vector3(-sideLength / 2.0f, -sideLength / 2.0f, sideLength / 2.0f)); mesh.AddVertex(new Vector3(sideLength / 2.0f, -sideLength / 2.0f, sideLength / 2.0f)); mesh.AddVertex(new Vector3(sideLength / 2.0f, sideLength / 2.0f, sideLength / 2.0f)); mesh.AddVertex(new Vector3(-sideLength / 2.0f, sideLength / 2.0f, sideLength / 2.0f)); mesh.AddTriangle(new Triangle(0, 2, 1)); mesh.AddTriangle(new Triangle(0, 3, 2)); mesh.AddTriangle(new Triangle(1, 2, 5)); mesh.AddTriangle(new Triangle(2, 6, 5)); mesh.AddTriangle(new Triangle(4, 5, 6)); mesh.AddTriangle(new Triangle(4, 6, 7)); mesh.AddTriangle(new Triangle(4, 7, 0)); mesh.AddTriangle(new Triangle(0, 7, 3)); mesh.AddTriangle(new Triangle(3, 7, 2)); mesh.AddTriangle(new Triangle(2, 7, 6)); mesh.AddTriangle(new Triangle(4, 0, 1)); mesh.AddTriangle(new Triangle(1, 5, 4)); mesh.ComputeTriangleNormals(); }
public static void Translate(ITriangleMesh mesh, Vector3 translation) { for (int i = 0; i < mesh.GetNumberOfVertices(); i++) { mesh.SetVertex(i, Vector3.Add(mesh.GetVertex(i), translation)); } }
/** * Read an OBJ file an create a mesh from the content. * */ public void Read(string filename, ITriangleMesh mesh) { this.mesh = mesh; mesh.Clear(); // Read input string objSource = System.IO.File.ReadAllText(AssetPath.GetPathToAsset(filename)); string[] lines = objSource.Split('\n'); foreach (String line in lines) { string[] tokens = line.Trim().Split(' ', '\t'); if (tokens.Length > 0) { if (tokens [0].CompareTo("v") == 0) { ParseVertex(tokens); } else if (tokens [0].CompareTo("f") == 0) { ParseFacet(tokens); } else if (tokens [0].CompareTo("vt") == 0) { ParseTextureCoordinate(tokens); } } } mesh.ComputeTriangleNormals(); Console.WriteLine("Read mesh from file " + filename + " with " + mesh.GetNumberOfTriangles() + " triangles and " + mesh.GetNumberOfVertices() + " vertices."); }
// Copy members of the given effector. protected override void CloneCore(ParticleEffector source) { base.CloneCore(source); var sourceTyped = (StartOnMeshEffector)source; Parameter = sourceTyped.Parameter; Mesh = sourceTyped.Mesh; }
// Copy members of the given effector. protected override void CloneCore(ParticleEffector source) { base.CloneCore(source); var sourceTyped = (StartOnMeshEffector)source; Parameter = sourceTyped.Parameter; Mesh = sourceTyped.Mesh; }
/// <summary> /// Decomposes the specified mesh. /// </summary> /// <param name="mesh">The mesh.</param> /// <remarks> /// This method blocks until the decomposition has finished. The result is available /// in the property <see cref="Decomposition"/>. /// </remarks> public void Decompose(ITriangleMesh mesh) { if (mesh == null) { throw new ArgumentNullException("mesh"); } _mesh = mesh; DoWork(); }
/** * Compute the normal of the border edge. * */ private Vector3 ComputeBorderEdgeNormal(TriangleEdge edge, ITriangleMesh mesh) { Vector3 v0 = mesh.GetVertex(edge.A); Vector3 v1 = mesh.GetVertex(edge.B); Vector3 e = Vector3.Subtract(v1, v0); Vector3 n = Vector3.Cross(e, Vector3.UnitZ); n.Normalize(); return(n); }
public static void CreateSquare(ITriangleMesh mesh, float extend) { mesh.Clear(); mesh.AddVertex(new Vector3(-extend, 0, -extend)); mesh.AddVertex(new Vector3(extend, 0, -extend)); mesh.AddVertex(new Vector3(extend, 0, extend)); mesh.AddVertex(new Vector3(-extend, 0, extend)); mesh.AddTriangle(0, 2, 1); mesh.AddTriangle(0, 3, 2); mesh.ComputeTriangleNormals(); }
/// <summary> /// Initializes a new instance of the <see cref="TriangleMeshShape"/> class from the given /// triangle mesh. /// </summary> /// <param name="mesh">The mesh.</param> /// <param name="enableContactWelding"> /// If set to <see langword="true"/> contact welding is enabled; otherwise, the shape will not /// use contact welding. See <see cref="EnableContactWelding"/> for more information. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> public TriangleMeshShape(ITriangleMesh mesh, bool enableContactWelding) { if (mesh == null) { throw new ArgumentNullException("mesh"); } _mesh = mesh; EnableContactWelding = enableContactWelding; }
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> /// Initializes a new instance of the <see cref="TriangleMeshShape"/> class from the given /// triangle mesh. /// </summary> /// <param name="mesh">The mesh.</param> /// <param name="enableContactWelding"> /// If set to <see langword="true"/> contact welding is enabled; otherwise, the shape will not /// use contact welding. See <see cref="EnableContactWelding"/> for more information. /// </param> /// <param name="partition"> /// The spatial partition (see <see cref="Partition"/>). Can be <see langword="null"/> if no /// partition should be used. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> public TriangleMeshShape(ITriangleMesh mesh, bool enableContactWelding, ISpatialPartition <int> partition) { if (mesh == null) { throw new ArgumentNullException("mesh"); } _mesh = mesh; Partition = partition; EnableContactWelding = enableContactWelding; }
/// <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); }
private ITriangleMesh CreateMiddle(ITriangleMesh mesh, List <Hexagon> borderHexagons, float sideLength) { ITriangleMesh bottomMesh = new TriangleMesh(); foreach (Hexagon hex in borderHexagons) { ITriangleMesh hexMesh = hex.GenerateMesh(); TriangleMeshFactory.Flip(hexMesh); TriangleMeshFactory.Unite(bottomMesh, hexMesh); } TriangleMeshFactory.Translate(bottomMesh, new Vector3(0, 0, -sideLength)); return(bottomMesh); }
public override void InitContent() { GetRoot().LightPosition = new Vector3(0, -2, 2); GetRoot().Animated = true; IslandGenerator gen = new IslandGenerator(); ITriangleMesh mesh = gen.GenerateIslandMesh(); TriangleMeshNode node = new TriangleMeshNode(mesh); node.ShowBorder = true; node.SetColor(Color4.DarkGreen); node.ShowNormals = false; GetRoot().AddChild(node); }
public ITriangleMesh GenerateIslandMesh() { HexagonField field = new HexagonField(); field.GenerateField(); ITriangleMesh mesh = new TriangleMesh(); // Top ITriangleMesh topMesh = CreateTopSurface(field.GetAllHexagons()); TriangleMeshFactory.Unite(mesh, topMesh); // Grass float grassBorderHeight = field.SideLength * 0.1f; Vector2[] grassBoundaryTexCoords = { new Vector2(0.1f, 0.1f), new Vector2(0.1f, 0.2f), new Vector2(0.2f, 0.1f), new Vector2(0.2f, 0.2f) }; ITriangleMesh grassBoundary = CreateBoundary(mesh, field.SideLength, grassBorderHeight, grassBorderHeight, new Vector3(0, 0, 0), grassBoundaryTexCoords); TriangleMeshFactory.Unite(mesh, grassBoundary); // Middle-bottom ITriangleMesh middleMesh = CreateMiddle(mesh, field.GetBorderHexagons(), field.SideLength); TriangleMeshFactory.Unite(mesh, middleMesh); ITriangleMesh bottomMesh = CreateTopSurface(field.GetNonBorderHexagons()); // Mud float mudHeight = 2 * field.SideLength; float mudBorderHeight = field.SideLength * 0.3f; Vector2[] mudBoundaryTexCoords = { new Vector2(0.7f, 0.1f), new Vector2(0.7f, 0.2f), new Vector2(0.8f, 0.1f), new Vector2(0.8f, 0.2f) }; ITriangleMesh mudBoundary = CreateBoundary(bottomMesh, mudHeight, 0, mudBorderHeight, new Vector3(0, 0, 0), mudBoundaryTexCoords); TriangleMeshFactory.Translate(mudBoundary, new Vector3(0, 0, -field.SideLength)); TriangleMeshFactory.Unite(mesh, mudBoundary); // Bottom TriangleMeshFactory.Flip(bottomMesh); TriangleMeshFactory.Translate(bottomMesh, new Vector3(0, 0, -(mudHeight + field.SideLength))); TriangleMeshFactory.Unite(mesh, bottomMesh); mesh = TriangleMeshFactory.Snap(mesh, 1e-5f); mesh.ComputeTriangleNormals(); mesh.SetTexture(new Texture("textures/island.png")); return(mesh); }
/// <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)); }
// Handling of non-uniform scaling: // http://en.wikipedia.org/wiki/Scaling_(geometry) about non-uniform scaling: // "Such a scaling changes ... the volume by the product of all three [scale factors]." /// <summary> /// Gets the contact of ray with a triangle mesh. /// </summary> /// <param name="triangleMesh">The triangle mesh.</param> /// <param name="ray">The ray.</param> /// <param name="hitDistance"> /// The hit distance. This is the distance on the ray from the ray origin to the contact. /// </param> /// <returns> /// <see langword="true"/> if the ray hits the front face of a triangle mesh triangle; /// otherwise, <see langword="false"/>. /// </returns> /// <remarks> /// This method returns any contact, not necessarily the first contact of the ray with the /// triangle mesh! The mesh triangles are treated as one-sided. /// </remarks> internal static bool GetContact(ITriangleMesh triangleMesh, Ray ray, out float hitDistance) { for (int i = 0; i < triangleMesh.NumberOfTriangles; i++) { var triangle = triangleMesh.GetTriangle(i); bool hit = GetContact(ray, triangle, false, out hitDistance); if (hit) { return(true); } } hitDistance = float.NaN; return(false); }
public void Add(ITriangleMesh mesh, bool weldVerticesBruteForce) { var triangleMesh = mesh as TriangleMesh; if (triangleMesh != null && !weldVerticesBruteForce) { // Special: mesh is TriangleMesh and no welding. if (triangleMesh.Vertices == null) { return; } if (triangleMesh.Indices == null) { return; } if (Vertices == null) { Vertices = new List <Vector3>(triangleMesh.Vertices.Count); } int numberOfNewIndices = triangleMesh.Indices.Count; if (Indices == null) { Indices = new List <int>(numberOfNewIndices); } // Add new vertices. int oldNumberOfVertices = Vertices.Count; Vertices.AddRange(triangleMesh.Vertices); // Add new indices. Add offset to all indices. for (int i = 0; i < numberOfNewIndices; i++) { Indices.Add(triangleMesh.Indices[i] + oldNumberOfVertices); } return; } int numberOfTriangles = mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) { Add(mesh.GetTriangle(i), weldVerticesBruteForce); } }
/** * Create the mesh for the top. * */ private ITriangleMesh CreateTopSurface(List <Hexagon> hexagons) { ITriangleMesh mesh = new TriangleMesh(); foreach (Hexagon hex in hexagons) { if (!hex.ConsistencyCheck()) { Console.WriteLine("Inconsistency detected!"); } ITriangleMesh hexMesh = hex.GenerateMesh(); TriangleMeshFactory.Unite(mesh, hexMesh); } mesh = TriangleMeshFactory.Snap(mesh, 1e-5f); return(mesh); }
/// <inheritdoc/> protected override void CloneCore(Shape sourceShape) { var source = (TriangleMeshShape)sourceShape; _mesh = source.Mesh; if (source.Partition != null) { Partition = source.Partition.Clone(); } if (source.TriangleNeighbors != null) { TriangleNeighbors = new List <int>(source.TriangleNeighbors); } }
/// <summary> /// Decomposes the specified mesh (asynchronously). /// </summary> /// <param name="mesh">The mesh.</param> /// <remarks> /// <para> /// This method does not block. The flag <see cref="IsBusy"/> is set until the decomposition has /// finished. The event <see cref="ProgressChanged"/> informs you on the current progress. The /// event <see cref="Completed"/> is raised when the decomposition is finished. The current /// intermediate decomposition result is available in <see cref="Decomposition"/>. Retrieving /// the result while the decomposition is running is possible but will temporarily block the /// decomposition process. /// </para> /// <para> /// <strong>Thread-Safety:</strong><br/> /// The <paramref name="mesh"/> must not be modified while the decomposition is in progress. /// </para> /// </remarks> /// <exception cref="InvalidOperationException"> /// Convex decomposition is already in progress. /// </exception> public void DecomposeAsync(ITriangleMesh mesh) { if (mesh == null) { throw new ArgumentNullException("mesh"); } if (IsBusy) { throw new InvalidOperationException("Convex decomposition is already in progress."); } _mesh = mesh; _cancel = false; IsBusy = true; Parallel.Start(DoWork); }
public static ITriangleMesh Snap(ITriangleMesh mesh, float epsilon) { Dictionary <int, int> vertexMapping = new Dictionary <int, int>(); ITriangleMesh result = new TriangleMesh(); for (int i = 0; i < mesh.GetNumberOfVertices(); i++) { Vector3 vi = mesh.GetVertex(i); bool found = false; for (int j = 0; j < result.GetNumberOfVertices(); j++) { Vector3 vj = result.GetVertex(j); if (Vector3.Subtract(vi, vj).Length < epsilon) { vertexMapping[i] = j; found = true; } } if (!found) { int idx = result.AddVertex(vi); vertexMapping[i] = idx; } } for (int i = 0; i < mesh.GetNumberOfTexCoords(); i++) { result.AddTextureCoordinate(mesh.GetTextureCoordinate(i)); } for (int i = 0; i < mesh.GetNumberOfTriangles(); i++) { Triangle t = mesh.GetTriangle(i).Clone(); t.A = vertexMapping[t.A]; t.B = vertexMapping[t.B]; t.C = vertexMapping[t.C]; result.AddTriangle(t); } Console.WriteLine("Mesh snapping: " + mesh.GetNumberOfVertices() + " -> " + result.GetNumberOfVertices() + " verts."); return(result); }
/** * Generated the unification of two meshes. Attention: no new mesh is generated, in fact the * geometry of mesh2 is added to mesh1. */ public static void Unite(ITriangleMesh mesh1, ITriangleMesh mesh2) { int numberOfVertsMesh1 = mesh1.GetNumberOfVertices(); int numberOfTexCoordsMesh1 = mesh1.GetNumberOfTexCoords(); for (int i = 0; i < mesh2.GetNumberOfVertices(); i++) { mesh1.AddVertex(mesh2.GetVertex(i)); } for (int i = 0; i < mesh2.GetNumberOfTexCoords(); i++) { mesh1.AddTextureCoordinate(mesh2.GetTextureCoordinate(i)); } for (int i = 0; i < mesh2.GetNumberOfTriangles(); i++) { Triangle t = mesh2.GetTriangle(i).Clone(); t.VertexIndexOffset(numberOfVertsMesh1); t.TexCoordsIndexOffset(numberOfTexCoordsMesh1); mesh1.AddTriangle(t); } mesh1.ComputeTriangleNormals(); }
public TriangleLight(RayEngineScene sc, int triIndex, ITriangleMesh mesh, RgbSpectrum gain = new RgbSpectrum()) : this(sc, string.Empty, gain) { Gain = (RgbSpectrumInfo)gain; triangleIndex = triIndex; this.Owner = mesh; //Configuration.Instance.Get("Integrator.ComputeNormals", false); //Configuration.Instance.Get("Integrator.InverseLightNormals", false); var multiplicator = normalInverse ? -1f : 1f; this.TriangleNormal = multiplicator * ((computeNormal || !mesh.HasNormals) ? scene.Triangles[triangleIndex].ComputeNormal(scene.Vertices) : (Normal)scene.SceneGeometry.Normals[Math.Min(scene.SceneGeometry.Normals.Length - 1, triIndex)]); this.area = Math.Max(scene.Triangles[triangleIndex].AreaV(scene.Vertices), 0.00001f); Assert.IsNotNaN(this.TriangleNormal.x); Assert.IsTrue(this.area > 0f); lightSpectra = GlobalConfiguration.Instance.SpectralRendering ? (ISpectrum)spectra : gain; }
/** * Create sphere. * */ public static void CreateSphere(ITriangleMesh mesh, float radius, int resolution) { mesh.Clear(); float dTheta = (float)(Math.PI / (resolution + 1)); float dPhi = (float)(Math.PI * 2.0 / resolution); // Create vertices // 0-180 degrees: i, theta for (int i = 0; i < resolution; i++) { // 0-360 degres: j, phi for (int j = 0; j < resolution; j++) { Vector3 p0 = EvaluateSpherePoint((i + 1) * dTheta, j * dPhi, radius); mesh.AddVertex(p0); } } int leftIndex = mesh.AddVertex(new Vector3(0, 0, radius)); int rightIndex = mesh.AddVertex(new Vector3(0, 0, -radius)); // Add triangles for (int i = 0; i < resolution - 1; i++) { for (int j = 0; j < resolution; j++) { mesh.AddTriangle(GetSphereIndex(i, j, resolution), GetSphereIndex(i + 1, j, resolution), GetSphereIndex(i + 1, j + 1, resolution)); mesh.AddTriangle(GetSphereIndex(i, j, resolution), GetSphereIndex(i + 1, j + 1, resolution), GetSphereIndex(i, j + 1, resolution)); } } for (int j = 0; j < resolution; j++) { mesh.AddTriangle(GetSphereIndex(0, j, resolution), GetSphereIndex(0, (j + 1) % resolution, resolution), leftIndex); mesh.AddTriangle(GetSphereIndex(resolution - 1, j, resolution), rightIndex, GetSphereIndex(resolution - 1, (j + 1) % resolution, resolution)); } mesh.ComputeTriangleNormals(); }
public void CreateShadowPolygons(Vector3 lightPosition, float extend, ITriangleMesh shadowPolygonMesh) { shadowPolygonMesh.Clear(); ArrayList silhouetteEdges = GetSilhouette(lightPosition); for (int i = 0; i < silhouetteEdges.Count; i++) { Edge edge = (Edge)silhouetteEdges[i]; Vector3 v0 = GetVertex(edge.a); Vector3 v1 = GetVertex(edge.b); Vector3 dv0 = Vector3.Multiply(Vector3.Subtract(v0, lightPosition).Normalized(), extend); Vector3 dv1 = Vector3.Multiply(Vector3.Subtract(v1, lightPosition).Normalized(), extend); Vector3 v0Dash = Vector3.Add(v0, dv0); Vector3 v1Dash = Vector3.Add(v1, dv1); int v0Index = shadowPolygonMesh.AddVertex(v0); int v1Index = shadowPolygonMesh.AddVertex(v1); int v0DashIndex = shadowPolygonMesh.AddVertex(v0Dash); int v1DashIndex = shadowPolygonMesh.AddVertex(v1Dash); shadowPolygonMesh.AddTriangle(v0Index, v0DashIndex, v1DashIndex); shadowPolygonMesh.AddTriangle(v0Index, v1DashIndex, v1Index); } shadowPolygonMesh.ComputeTriangleNormals(); }
// Computes the surface covariance matrix for a convex hull of the given points. private static MatrixF ComputeCovarianceMatrixFromSurface(ITriangleMesh mesh) { // Compute covariance matrix for the surface of the triangle mesh. // See Physics-Based Animation for a derivation. Variable names are the same as in // the book. // ... Better look at Real-Time Collision Detection p. 108. The Physics-Based Animation // book has errors. MatrixF C = new MatrixF(3, 3); // The covariance matrix. float A = 0; // Total surface area. Vector3F mS = Vector3F.Zero; // Mean point of the entire surface. for (int k = 0; k < mesh.NumberOfTriangles; k++) { var triangle = mesh.GetTriangle(k); var pK = triangle.Vertex0; var qK = triangle.Vertex1; var rK = triangle.Vertex2; var mK = 1f / 3f * (pK + qK + rK); var uK = qK - pK; var vK = rK - pK; var Ak = 0.5f * Vector3F.Cross(uK, vK).Length; A += Ak; mS += Ak * mK; for (int i = 0; i < 3; i++) for (int j = i; j < 3; j++) C[i, j] += Ak / 12f * (9 * mK[i] * mK[j] + pK[i] * pK[j] + qK[i] * qK[j] + rK[i] * rK[j]); } mS /= A; for (int i = 0; i < 3; i++) for (int j = i; j < 3; j++) C[i, j] = 1 / A * C[i, j] - mS[i] * mS[j]; // Set the other half of the symmetric matrix. for (int i = 0; i < 3; i++) for (int j = i + 1; j < 3; j++) C[j, i] = C[i, j]; return C; }
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); }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- protected override void OnLoad() { // Add rigid bodies to simulation. var simulation = _services.GetInstance <Simulation>(); // We use a random number generator with a custom seed. RandomHelper.Random = new Random(123); // ----- Add a ground plane. AddBody(simulation, "GroundPlane", Pose.Identity, new PlaneShape(Vector3F.UnitY, 0), MotionType.Static); // ----- Create a small flying sphere. AddBody(simulation, "Sphere", new Pose(new Vector3F(0, 1f, 0)), new SphereShape(0.2f), MotionType.Static); // ----- Create small walls at the level boundary. AddBody(simulation, "WallLeft", new Pose(new Vector3F(-30, 1, 0)), new BoxShape(0.3f, 2, 60), MotionType.Static); AddBody(simulation, "WallRight", new Pose(new Vector3F(30, 1, 0)), new BoxShape(0.3f, 2, 60), MotionType.Static); AddBody(simulation, "WallFront", new Pose(new Vector3F(0, 1, -30)), new BoxShape(60, 2, 0.3f), MotionType.Static); AddBody(simulation, "WallBack", new Pose(new Vector3F(0, 1, 30)), new BoxShape(60, 2, 0.3f), MotionType.Static); // ----- Create a few bigger objects. // We position the boxes so that we have a few corners we can run into. Character controllers // should be stable when the user runs into corners. AddBody(simulation, "House0", new Pose(new Vector3F(10, 1, -10)), new BoxShape(8, 2, 8f), MotionType.Static); AddBody(simulation, "House1", new Pose(new Vector3F(13, 1, -4)), new BoxShape(2, 2, 4), MotionType.Static); AddBody(simulation, "House2", new Pose(new Vector3F(10, 2, -15), Matrix33F.CreateRotationY(-0.3f)), new BoxShape(8, 4, 2), MotionType.Static); // ----- Create stairs with increasing step height. // Each step is a box. With this object we can test if our character can climb up // stairs. The character controller has a step height limit. Increasing step heights // let us test if the step height limit works. float startHeight = 0; const float stepDepth = 1f; for (int i = 0; i < 10; i++) { float stepHeight = 0.1f + i * 0.05f; Vector3F position = new Vector3F(0, startHeight + stepHeight / 2, -2 - i * stepDepth); AddBody(simulation, "Step" + i, new Pose(position), new BoxShape(2, stepHeight, stepDepth), MotionType.Static); startHeight += stepHeight; } // ----- V obstacle to test if we get stuck. AddBody(simulation, "V0", new Pose(new Vector3F(-5.5f, 0, 10), QuaternionF.CreateRotationZ(0.2f)), new BoxShape(1f, 2f, 2), MotionType.Static); AddBody(simulation, "V1", new Pose(new Vector3F(-4, 0, 10), QuaternionF.CreateRotationZ(-0.2f)), new BoxShape(1f, 2f, 2), MotionType.Static); // ----- Create a height field. // Terrain that is uneven is best modeled with a height field. Height fields are faster // than general triangle meshes. // The height direction is the y direction. // The height field lies in the x/z plane. var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; // Create arbitrary height values. for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { if (x == 0 || z == 0 || x == 19 || z == 19) { // Set this boundary elements to a low height, so that the height field is connected // to the ground. samples[z * numberOfSamplesX + x] = -1; } else { // A sine/cosine function that creates some interesting waves. samples[z * numberOfSamplesX + x] = 1.0f + (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 1.0f); } } } var heightField = new HeightField(0, 0, 20, 20, samples, numberOfSamplesX, numberOfSamplesZ); AddBody(simulation, "HeightField", new Pose(new Vector3F(10, 0, 10)), heightField, MotionType.Static); // ----- Create rubble on the floor (small random objects on the floor). // Our character should be able to move over small bumps on the ground. for (int i = 0; i < 50; i++) { Vector3F position = new Vector3F(RandomHelper.Random.NextFloat(-5, 5), 0, RandomHelper.Random.NextFloat(10, 20)); QuaternionF orientation = RandomHelper.Random.NextQuaternionF(); Vector3F size = RandomHelper.Random.NextVector3F(0.05f, 0.8f); AddBody(simulation, "Stone" + i, new Pose(position, orientation), new BoxShape(size), MotionType.Static); } // ----- Create some slopes to see how our character performs on/under sloped surfaces. // Here we can test how the character controller behaves if the head touches an inclined // ceiling. AddBody(simulation, "SlopeGround", new Pose(new Vector3F(-2, 1.8f, -12), QuaternionF.CreateRotationX(0.4f)), new BoxShape(2, 0.5f, 10), MotionType.Static); AddBody(simulation, "SlopeRoof", new Pose(new Vector3F(-2, 5.6f, -12), QuaternionF.CreateRotationX(-0.4f)), new BoxShape(2, 0.5f, 10), MotionType.Static); // Create slopes with increasing tilt angles. // The character controller has a slope limit. Only flat slopes should be climbable. // Movement between slopes should be smooth. Vector3F slopePosition = new Vector3F(-17, -0.25f, 6); BoxShape slopeShape = new BoxShape(8, 0.5f, 5); for (int i = 1; i < 8; i++) { Matrix33F oldRotation = Matrix33F.CreateRotationX((i - 1) * MathHelper.ToRadians(10)); Matrix33F rotation = Matrix33F.CreateRotationX(i * MathHelper.ToRadians(10)); slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2 + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2; AddBody(simulation, "Slope" + i, new Pose(slopePosition, rotation), slopeShape, MotionType.Static); } // Create slopes with decreasing tilt angles. slopePosition = new Vector3F(-8, -2, 5); slopeShape = new BoxShape(8f, 0.5f, 5); for (int i = 1; i < 8; i++) { Matrix33F oldRotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - (i - 1) * MathHelper.ToRadians(10)); Matrix33F rotation = Matrix33F.CreateRotationX(MathHelper.ToRadians(40) - i * MathHelper.ToRadians(10)); slopePosition += (oldRotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2 + (rotation * new Vector3F(0, 0, -slopeShape.WidthZ)) / 2; Vector3F position = slopePosition - rotation * new Vector3F(0, slopeShape.WidthY / 2, 0); AddBody(simulation, "Slope2" + i, new Pose(position, rotation), slopeShape, MotionType.Static); } // ----- Create a slope with a wall on one side. // This objects let's us test how the character controller behaves while falling and // sliding along a vertical wall. (Run up the slope and then jump down while moving into // the wall.) AddBody(simulation, "LongSlope", new Pose(new Vector3F(-24, 3, -10), Matrix33F.CreateRotationX(0.4f)), new BoxShape(4, 5f, 30), MotionType.Static); AddBody(simulation, "LongSlopeWall", new Pose(new Vector3F(-26, 5, -10)), new BoxShape(0.5f, 10f, 25), MotionType.Static); // ----- Create a trigger object that represents a ladder. var ladder = AddBody(simulation, "Ladder", new Pose(new Vector3F(-25.7f, 5, 0)), new BoxShape(0.5f, 10f, 1), MotionType.Static); ladder.CollisionObject.Type = CollisionObjectType.Trigger; // ----- Create a mesh object to test walking on triangle meshes. // Normally, the mesh would be loaded from a file. Here, we make a composite shape and // let DigitalRune Geometry compute a mesh for it. Then we throw away the composite // shape and use only the mesh. (We do this to test triangle meshes. Using the composite // shape instead of the triangle mesh would be a lot faster.) CompositeShape compositeShape = new CompositeShape(); compositeShape.Children.Add(new GeometricObject(heightField, Pose.Identity)); compositeShape.Children.Add(new GeometricObject(new CylinderShape(1, 2), new Pose(new Vector3F(10, 1, 10)))); compositeShape.Children.Add(new GeometricObject(new SphereShape(3), new Pose(new Vector3F(15, 0, 15)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(4, 4, 3), new Pose(new Vector3F(15, 1.5f, 5)))); compositeShape.Children.Add(new GeometricObject(new BoxShape(4, 4, 3), new Pose(new Vector3F(15, 1.5f, 0)))); ITriangleMesh mesh = compositeShape.GetMesh(0.01f, 3); TriangleMeshShape meshShape = new TriangleMeshShape(mesh); // Collision detection speed for triangle meshes can be improved by using a spatial // partition. Here, we assign an AabbTree to the triangle mesh shape. The tree is // built automatically when needed and it stores triangle indices (therefore the generic // parameter of the AabbTree is int). meshShape.Partition = new AabbTree <int>() { // 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, }; // Contact welding creates smoother contact normals - but it costs a bit of performance. meshShape.EnableContactWelding = true; AddBody(simulation, "Mesh", new Pose(new Vector3F(-30, 0, 10)), meshShape, MotionType.Static); // ----- Create a seesaw. var seesawBase = AddBody(simulation, "SeesawBase", new Pose(new Vector3F(5, 0.5f, 0)), new BoxShape(0.2f, 1, 1), MotionType.Static); var seesaw = AddBody(simulation, "Seesaw", new Pose(new Vector3F(5, 1.05f, 0)), new BoxShape(5, 0.1f, 1), MotionType.Dynamic); // Attach the seesaw to the base using a hinge joint. simulation.Constraints.Add(new HingeJoint { BodyA = seesaw, BodyB = seesawBase, AnchorPoseALocal = new Pose(new Vector3F(0, 0, 0), new Matrix33F(0, 0, -1, 0, 1, 0, 1, 0, 0)), AnchorPoseBLocal = new Pose(new Vector3F(0, 0.5f, 0), new Matrix33F(0, 0, -1, 0, 1, 0, 1, 0, 0)), CollisionEnabled = false, }); // ----- A platform that is moving up/down. _elevator = AddBody(simulation, "Elevator", new Pose(new Vector3F(5, -1f, 5)), new BoxShape(3, 1f, 3), MotionType.Kinematic); _elevator.LinearVelocity = new Vector3F(2, 2, 0); // ----- A platform that is moving sideways. _pusher = AddBody(simulation, "Pusher", new Pose(new Vector3F(15, 0.5f, 0)), new BoxShape(3, 1f, 3), MotionType.Kinematic); _pusher.LinearVelocity = new Vector3F(0, 0, 2); // ----- Create conveyor belt with two static boxes on the sides. AddBody(simulation, "ConveyorSide0", new Pose(new Vector3F(19, 0.25f, 0)), new BoxShape(0.8f, 0.5f, 8f), MotionType.Static); AddBody(simulation, "ConveyorSide1", new Pose(new Vector3F(21, 0.25f, 0)), new BoxShape(0.8f, 0.5f, 8f), MotionType.Static); // The conveyor belt is a simple box with a special material. var conveyorBelt = AddBody(simulation, "ConveyorBelt", new Pose(new Vector3F(20, 0.25f, 0)), new BoxShape(1f, 0.51f, 8f), MotionType.Static); UniformMaterial materialWithSurfaceMotion = new UniformMaterial("ConveyorBelt", true) // Important: The second parameter enables the surface { // motion. It has to be set to true in the constructor! SurfaceMotion = new Vector3F(0, 0, 1), // The surface motion relative to the object. }; conveyorBelt.Material = materialWithSurfaceMotion; // ----- Distribute a few dynamic spheres and boxes across the landscape. SphereShape sphereShape = new SphereShape(0.5f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-15, 15); position.Y = 20; AddBody(simulation, "Sphere" + i, new Pose(position), sphereShape, MotionType.Dynamic); } BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-15, 15); position.Y = 20; AddBody(simulation, "Box" + i, new Pose(position), boxShape, MotionType.Dynamic); } }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- #if XNA || MONOGAME /// <summary> /// Sets the triangle mesh. (For use by the content pipeline only.) /// </summary> /// <param name="mesh">The triangle mesh.</param> internal void SetMesh(ITriangleMesh mesh) { _mesh = mesh; }
public static void GetMass(ITriangleMesh mesh, out float mass, out Vector3F centerOfMass, out Matrix33F inertia) { if (mesh == null) { throw new ArgumentNullException("mesh"); } // Integral variables. float i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0, i8 = 0, i9 = 0; int numberOfTriangles = mesh.NumberOfTriangles; for (int triangleIndex = 0; triangleIndex < numberOfTriangles; triangleIndex++) { var triangle = mesh.GetTriangle(triangleIndex); // Vertex coordinates. Vector3F v0 = triangle.Vertex0; Vector3F v1 = triangle.Vertex1; Vector3F v2 = triangle.Vertex2; // Edges and cross products of edges Vector3F a = v1 - v0; Vector3F b = v2 - v0; Vector3F d = Vector3F.Cross(a, b); // Compute integral terms. float f1x, f2x, f3x, g0x, g1x, g2x; ComputePolyhedronMassSubExpressions(v0.X, v1.X, v2.X, out f1x, out f2x, out f3x, out g0x, out g1x, out g2x); float f1y, f2y, f3y, g0y, g1y, g2y; ComputePolyhedronMassSubExpressions(v0.Y, v1.Y, v2.Y, out f1y, out f2y, out f3y, out g0y, out g1y, out g2y); float f1z, f2z, f3z, g0z, g1z, g2z; ComputePolyhedronMassSubExpressions(v0.Z, v1.Z, v2.Z, out f1z, out f2z, out f3z, out g0z, out g1z, out g2z); // Update integrals. i0 += d.X * f1x; i1 += d.X * f2x; i2 += d.Y * f2y; i3 += d.Z * f2z; i4 += d.X * f3x; i5 += d.Y * f3y; i6 += d.Z * f3z; i7 += d.X * (v0.Y * g0x + v1.Y * g1x + v2.Y * g2x); i8 += d.Y * (v0.Z * g0y + v1.Z * g1y + v2.Z * g2y); i9 += d.Z * (v0.X * g0z + v1.X * g1z + v2.X * g2z); } i0 /= 6.0f; i1 /= 24.0f; i2 /= 24.0f; i3 /= 24.0f; i4 /= 60.0f; i5 /= 60.0f; i6 /= 60.0f; i7 /= 120.0f; i8 /= 120.0f; i9 /= 120.0f; mass = i0; centerOfMass = 1.0f / mass * new Vector3F(i1, i2, i3); // Clamp to zero. if (Numeric.IsZero(centerOfMass.X)) { centerOfMass.X = 0; } if (Numeric.IsZero(centerOfMass.Y)) { centerOfMass.Y = 0; } if (Numeric.IsZero(centerOfMass.Z)) { centerOfMass.Z = 0; } // Inertia around the world origin. inertia.M00 = i5 + i6; inertia.M11 = i4 + i6; inertia.M22 = i4 + i5; inertia.M01 = inertia.M10 = Numeric.IsZero(i7) ? 0 : -i7; inertia.M12 = inertia.M21 = Numeric.IsZero(i8) ? 0 : -i8; inertia.M02 = inertia.M20 = Numeric.IsZero(i9) ? 0 : -i9; // Inertia around center of mass. inertia = GetUntranslatedMassInertia(mass, inertia, centerOfMass); }
//-------------------------------------------------------------- /// <summary> /// Decomposes the specified mesh. /// </summary> /// <param name="mesh">The mesh.</param> /// <remarks> /// This method blocks until the decomposition has finished. The result is available /// in the property <see cref="Decomposition"/>. /// </remarks> public void Decompose(ITriangleMesh mesh) { if (mesh == null) throw new ArgumentNullException("mesh"); _mesh = mesh; DoWork(); }
public RollingSphereSample(Microsoft.Xna.Framework.Game game) : base(game) { // To demonstrate the problems with triangle meshes we increase the gravity and let a // sphere roll over a curved surface. // Add basic force effects. Simulation.ForceEffects.Add(new Gravity { Acceleration = new Vector3(0, -30, 0) }); // Higher gravity to make the problem more visible. Simulation.ForceEffects.Add(new Damping()); // Use the custom contact filter to improve sphere contacts. _sphereContactFilter = new SphereContactFilter(); Simulation.CollisionDomain.CollisionDetection.ContactFilter = _sphereContactFilter; // The triangle mesh could be loaded from a file, such as an XNA Model. // In this example will create a height field and convert the height field into a triangle mesh. var numberOfSamplesX = 60; var numberOfSamplesZ = 10; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { samples[z * numberOfSamplesX + x] = (float)(Math.Sin(x / 6f) * 10f + 5f); } } var heightField = new HeightField(0, 0, 70, 30, samples, numberOfSamplesX, numberOfSamplesZ); // Convert the height field to a triangle mesh. ITriangleMesh mesh = heightField.GetMesh(0.01f, 3); // Create a shape for the triangle mesh. _triangleMeshShape = new TriangleMeshShape(mesh); // Enable contact welding. And set the welding limit to 1 for maximal effect. _triangleMeshShape.EnableContactWelding = true; _originalWeldingLimit = TriangleMeshAlgorithm.WeldingLimit; TriangleMeshAlgorithm.WeldingLimit = 1; // 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() { BottomUpBuildThreshold = 0 }; // Create a static rigid body using the shape and add it to the simulation. // We explicitly specify a mass frame. We can use any mass frame for static bodies (because // static bodies are effectively treated as if they have infinite mass). If we do not specify // a mass frame in the rigid body constructor, the constructor will automatically compute an // approximate mass frame (which can take some time for large meshes). var ground = new RigidBody(_triangleMeshShape, new MassFrame(), null) { Pose = new Pose(new Vector3(-34, 0, -40f)), MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(ground); SphereShape sphereShape = new SphereShape(0.5f); _sphere = new RigidBody(sphereShape); Simulation.RigidBodies.Add(_sphere); _enableSmoothMovement = true; _timeUntilReset = TimeSpan.Zero; }
/// <overloads> /// <summary> /// Adds triangles to the triangle mesh. /// </summary> /// </overloads> /// /// <summary> /// Adds the triangles of the specified mesh (without vertex welding). /// </summary> /// <param name="mesh">The mesh.</param> /// <remarks> /// This method does not perform vertex welding and does not remove degenerate triangles. /// </remarks> public void Add(ITriangleMesh mesh) { Add(mesh, false); }
public void Initialize(IRayEngineScene scen, params object[] data) { zeroSpectra = GlobalConfiguration.Instance.SpectralRendering ? (ISpectrum)ZeroCSpectrumArray : this.RgbSpectrumZeroArray; var scn = (RayEngineScene)scen; if (mesh == null && !string.IsNullOrWhiteSpace(MeshName)) { this.mesh = scn.Meshes.FirstOrDefault( item => item.MeshName.Equals(MeshName, StringComparison.CurrentCultureIgnoreCase)); if (this.mesh != null) { if (this.mesh.MeshProfile == null) { this.mesh.MeshProfile = new LightsourceProfile() { Light = this }; } else { var lightsourceProfile = this.mesh.MeshProfile as LightsourceProfile; if (lightsourceProfile != null) lightsourceProfile.Light = this; } meshMeshArea = 0; for (int i = mesh.StartTriangle; i < mesh.EndTriangle; i++) { meshMeshArea += scn.Triangles[i].AreaV(scn.Vertices); } } } if (mesh == null) { throw new ArgumentException("Cant find light mesh", this.LightName ?? this.MeshName); } triangleSampleData = new TriangleSample[this.mesh.TrianglesCount]; this.scene = scn; for (int i = mesh.StartTriangle, j = 0; i < mesh.EndTriangle; i++, j++) { triangleSampleData[j] = new TriangleSample(scene.Triangles[i].AreaV(scene.Vertices), NormalModifier * scene.Triangles[i].ComputeNormal(scene.Vertices)); } triangleSampleData.PartialSort((a, b) => a.Item1.CompareTo(a.Item1), 0, triangleSampleData.Length); if (gain.IsBlack() || infoGain == null) { gain = scn.DefaultLightGain; infoGain = new RgbSpectrumInfo(gain); } lightSpectra = GlobalConfiguration.Instance.SpectralRendering ? spectra.ToArray() : gain.ToArray(); }
private void DoWork() { // The locks are released regularly, so that the Decomposition property can be // accessed. Exception exception = null; try { if ((GlobalSettings.ValidationLevelInternal & GlobalSettings.ValidationLevelUserHighExpensive) != 0) ValidateInput(); lock (_syncRoot) { _decomposition = null; // Create the Dual graph. CreateDualGraph(); } // Partitioning process: MergeIslands(); // Each internal loop is locked. lock (_syncRoot) { if (!_cancel) CreateCompositeShape(); } if (!_cancel) OnProgressChanged(100); } catch (Exception e) { exception = e; if (!IsBusy) // Throw only when in synchronous decomposition. throw; } finally { _mesh = null; if (IsBusy) // IsBusy is only set for async operations. { IsBusy = false; // Raise completed event. var handler = Completed; if (handler != null) handler(this, new AsyncCompletedEventArgs(exception, _cancel, null)); } } }
public static bool IsVolume(ITriangleMesh mesh) { return mesh.MeshName.NameContains("volume","_volume","volume_"); }
/// <summary> /// Initializes a new instance of the <see cref="TriangleMeshShape"/> class from the given /// triangle mesh. /// </summary> /// <param name="mesh">The mesh.</param> /// <param name="enableContactWelding"> /// If set to <see langword="true"/> contact welding is enabled; otherwise, the shape will not /// use contact welding. See <see cref="EnableContactWelding"/> for more information. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> public TriangleMeshShape(ITriangleMesh mesh, bool enableContactWelding) { if (mesh == null) throw new ArgumentNullException("mesh"); _mesh = mesh; EnableContactWelding = enableContactWelding; }
private void DoWork() { // The locks are released regularly, so that the Decomposition property can be // accessed. Exception exception = null; try { if ((GlobalSettings.ValidationLevelInternal & GlobalSettings.ValidationLevelUserHighExpensive) != 0) { ValidateInput(); } lock (_syncRoot) { _decomposition = null; // Create the Dual graph. CreateDualGraph(); } // Partitioning process: MergeIslands(); // Each internal loop is locked. lock (_syncRoot) { if (!_cancel) { CreateCompositeShape(); } } if (!_cancel) { OnProgressChanged(100); } } catch (Exception e) { exception = e; if (!IsBusy) // Throw only when in synchronous decomposition. { throw; } } finally { _mesh = null; if (IsBusy) // IsBusy is only set for async operations. { IsBusy = false; // Raise completed event. var handler = Completed; if (handler != null) { handler(this, new AsyncCompletedEventArgs(exception, _cancel, null)); } } } }
public static void GetMass(ITriangleMesh mesh, out float mass, out Vector3F centerOfMass, out Matrix33F inertia) { if (mesh == null) throw new ArgumentNullException("mesh"); // Integral variables. float i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0, i8 = 0, i9 = 0; int numberOfTriangles = mesh.NumberOfTriangles; for (int triangleIndex = 0; triangleIndex < numberOfTriangles; triangleIndex++) { var triangle = mesh.GetTriangle(triangleIndex); // Vertex coordinates. Vector3F v0 = triangle.Vertex0; Vector3F v1 = triangle.Vertex1; Vector3F v2 = triangle.Vertex2; // Edges and cross products of edges Vector3F a = v1 - v0; Vector3F b = v2 - v0; Vector3F d = Vector3F.Cross(a, b); // Compute integral terms. float f1x, f2x, f3x, g0x, g1x, g2x; ComputePolyhedronMassSubExpressions(v0.X, v1.X, v2.X, out f1x, out f2x, out f3x, out g0x, out g1x, out g2x); float f1y, f2y, f3y, g0y, g1y, g2y; ComputePolyhedronMassSubExpressions(v0.Y, v1.Y, v2.Y, out f1y, out f2y, out f3y, out g0y, out g1y, out g2y); float f1z, f2z, f3z, g0z, g1z, g2z; ComputePolyhedronMassSubExpressions(v0.Z, v1.Z, v2.Z, out f1z, out f2z, out f3z, out g0z, out g1z, out g2z); // Update integrals. i0 += d.X * f1x; i1 += d.X * f2x; i2 += d.Y * f2y; i3 += d.Z * f2z; i4 += d.X * f3x; i5 += d.Y * f3y; i6 += d.Z * f3z; i7 += d.X * (v0.Y * g0x + v1.Y * g1x + v2.Y * g2x); i8 += d.Y * (v0.Z * g0y + v1.Z * g1y + v2.Z * g2y); i9 += d.Z * (v0.X * g0z + v1.X * g1z + v2.X * g2z); } i0 /= 6.0f; i1 /= 24.0f; i2 /= 24.0f; i3 /= 24.0f; i4 /= 60.0f; i5 /= 60.0f; i6 /= 60.0f; i7 /= 120.0f; i8 /= 120.0f; i9 /= 120.0f; mass = i0; centerOfMass = 1.0f / mass * new Vector3F(i1, i2, i3); // Clamp to zero. if (Numeric.IsZero(centerOfMass.X)) centerOfMass.X = 0; if (Numeric.IsZero(centerOfMass.Y)) centerOfMass.Y = 0; if (Numeric.IsZero(centerOfMass.Z)) centerOfMass.Z = 0; // Inertia around the world origin. inertia.M00 = i5 + i6; inertia.M11 = i4 + i6; inertia.M22 = i4 + i5; inertia.M01 = inertia.M10 = Numeric.IsZero(i7) ? 0 : -i7; inertia.M12 = inertia.M21 = Numeric.IsZero(i8) ? 0 : -i8; inertia.M02 = inertia.M20 = Numeric.IsZero(i9) ? 0 : -i9; // Inertia around center of mass. inertia = GetUntranslatedMassInertia(mass, inertia, centerOfMass); }
/// <inheritdoc/> protected override void CloneCore(Shape sourceShape) { var source = (TriangleMeshShape)sourceShape; _mesh = source.Mesh; if (source.Partition != null) Partition = source.Partition.Clone(); if (source.TriangleNeighbors != null) TriangleNeighbors = new List<int>(source.TriangleNeighbors); }
public void Init(RayEngineScene scene, ITriangleMesh[] geometry) { var sceneModel = scene; meshes = new List<ITriangleMesh>(); meshes.AddRange(geometry); this.sceneVertices = sceneModel.Vertices; var trianglesCache = scene.Triangles.ToList(); Comparison<Triangle> tc = (t1, t2) => CompareTriangles(t1, t2); //trianglesCache.Sort(tc); this.triangles = trianglesCache; //this.BuildTree(trianglesCache, 0, trianglesCache.Count); AABB bound = trianglesCache.Select(prim => prim.WorldBound(sceneVertices)).Aggregate((b1, b2) => b1.Union(b2)); var prims = trianglesCache.Select( (item, index) => new BvhBuildNode() { Bound = item.WorldBound(sceneVertices), PrimitiveCount = 1, PrimitiveStart = index }).ToList(); this.RootNode = BuildQualityTree(prims, 0, trianglesCache.Count, bound); Tracer.TraceLine("Linearize BVH"); this.LinearizeBVH(); Tracer.TraceLine("Complete"); Tracer.TraceLine("Tree is builded, {0} nodes total", bvhNodesCount); }
public void Add(ITriangleMesh mesh, bool weldVerticesBruteForce) { var triangleMesh = mesh as TriangleMesh; if (triangleMesh != null && !weldVerticesBruteForce) { // Special: mesh is TriangleMesh and no welding. if (triangleMesh.Vertices == null) return; if (triangleMesh.Indices == null) return; if (Vertices == null) Vertices = new List<Vector3F>(triangleMesh.Vertices.Count); int numberOfNewIndices = triangleMesh.Indices.Count; if (Indices == null) Indices = new List<int>(numberOfNewIndices); // Add new vertices. int oldNumberOfVertices = Vertices.Count; Vertices.AddRange(triangleMesh.Vertices); // Add new indices. Add offset to all indices. for (int i = 0; i < numberOfNewIndices; i++) Indices.Add(triangleMesh.Indices[i] + oldNumberOfVertices); return; } int numberOfTriangles = mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) Add(mesh.GetTriangle(i), weldVerticesBruteForce); }
// Handling of non-uniform scaling: // http://en.wikipedia.org/wiki/Scaling_(geometry) about non-uniform scaling: // "Such a scaling changes ... the volume by the product of all three [scale factors]." /// <summary> /// Gets the contact of ray with a triangle mesh. /// </summary> /// <param name="triangleMesh">The triangle mesh.</param> /// <param name="ray">The ray.</param> /// <param name="hitDistance"> /// The hit distance. This is the distance on the ray from the ray origin to the contact. /// </param> /// <returns> /// <see langword="true"/> if the ray hits the front face of a triangle mesh triangle; /// otherwise, <see langword="false"/>. /// </returns> /// <remarks> /// This method returns any contact, not necessarily the first contact of the ray with the /// triangle mesh! The mesh triangles are treated as one-sided. /// </remarks> internal static bool GetContact(ITriangleMesh triangleMesh, Ray ray, out float hitDistance) { for (int i = 0; i < triangleMesh.NumberOfTriangles; i++) { var triangle = triangleMesh.GetTriangle(i); bool hit = GetContact(ray, triangle, false, out hitDistance); if (hit) return true; } hitDistance = float.NaN; return false; }
/// <summary> /// Initializes a new instance of the <see cref="TriangleMeshShape"/> class from the given /// triangle mesh. /// </summary> /// <param name="mesh">The mesh.</param> /// <param name="enableContactWelding"> /// If set to <see langword="true"/> contact welding is enabled; otherwise, the shape will not /// use contact welding. See <see cref="EnableContactWelding"/> for more information. /// </param> /// <param name="partition"> /// The spatial partition (see <see cref="Partition"/>). Can be <see langword="null"/> if no /// partition should be used. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> public TriangleMeshShape(ITriangleMesh mesh, bool enableContactWelding, ISpatialPartition<int> partition) { if (mesh == null) throw new ArgumentNullException("mesh"); _mesh = mesh; Partition = partition; EnableContactWelding = enableContactWelding; }
/// <summary> /// Decomposes the specified mesh (asynchronously). /// </summary> /// <param name="mesh">The mesh.</param> /// <remarks> /// <para> /// This method does not block. The flag <see cref="IsBusy"/> is set until the decomposition has /// finished. The event <see cref="ProgressChanged"/> informs you on the current progress. The /// event <see cref="Completed"/> is raised when the decomposition is finished. The current /// intermediate decomposition result is available in <see cref="Decomposition"/>. Retrieving /// the result while the decomposition is running is possible but will temporarily block the /// decomposition process. /// </para> /// <para> /// <strong>Thread-Safety:</strong><br/> /// The <paramref name="mesh"/> must not be modified while the decomposition is in progress. /// </para> /// </remarks> /// <exception cref="InvalidOperationException"> /// Convex decomposition is already in progress. /// </exception> public void DecomposeAsync(ITriangleMesh mesh) { if (mesh == null) throw new ArgumentNullException("mesh"); if (IsBusy) throw new InvalidOperationException("Convex decomposition is already in progress."); _mesh = mesh; _cancel = false; IsBusy = true; Parallel.Start(DoWork); }
public TriangleMeshSample(Microsoft.Xna.Framework.Game game) : base(game) { // Add basic force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // The triangle mesh could be loaded from a file, such as an XNA Model. // In this example will use the same height field as in Sample11 and convert // the shape into a triangle mesh. var numberOfSamplesX = 20; var numberOfSamplesZ = 20; var heights = new float[numberOfSamplesX * numberOfSamplesZ]; for (int z = 0; z < numberOfSamplesZ; z++) { for (int x = 0; x < numberOfSamplesX; x++) { heights[z * numberOfSamplesX + x] = (float)(Math.Cos(z / 2f) * Math.Sin(x / 2f) * 5f + 5f); } } HeightField heightField = new HeightField(0, 0, 100, 100, heights, numberOfSamplesX, numberOfSamplesZ); // Convert the height field to a triangle mesh. ITriangleMesh mesh = heightField.GetMesh(0.01f, 3); // Create a shape for the triangle mesh. TriangleMeshShape triangleMeshShape = new TriangleMeshShape(mesh); // Optional: We can enable "contact welding". When this flag is enabled, the triangle shape // precomputes additional internal information for the mesh. The collision detection will // be able to compute better contacts (e.g. better normal vectors at triangle edges). // Pro: Collision detection can compute better contact information. // Con: Contact welding information needs a bit of memory. And the collision detection is // a bit slower. triangleMeshShape.EnableContactWelding = true; // 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, }; // Optional: The partition will be automatically built when needed. For static meshes it is // built only once when it is needed for the first time. Building the AABB tree can take a // few seconds for very large meshes. // By calling Update() manually we can force the partition to be built right now: //triangleMeshShape.Partition.Update(false); // We could also call this method in a background thread while the level is loading. Or, // we can build the triangle mesh and the AABB tree in the XNA content pipeline and avoid the // building of the tree at runtime (see Sample 33). // Create a static rigid body using the shape and add it to the simulation. // We explicitly specify a mass frame. We can use any mass frame for static bodies (because // static bodies are effectively treated as if they have infinite mass). If we do not specify // a mass frame in the rigid body constructor, the constructor will automatically compute an // approximate mass frame (which can take some time for large meshes). var ground = new RigidBody(triangleMeshShape, new MassFrame(), null) { Pose = new Pose(new Vector3F(-50, 0, -50f)), MotionType = MotionType.Static, }; Simulation.RigidBodies.Add(ground); // Distribute a few spheres and boxes across the landscape. SphereShape sphereShape = new SphereShape(0.5f); for (int i = 0; i < 30; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-30, 30); position.Y = 20; RigidBody body = new RigidBody(sphereShape) { Pose = new Pose(position), }; Simulation.RigidBodies.Add(body); } BoxShape boxShape = new BoxShape(1, 1, 1); for (int i = 0; i < 30; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-30, 30); position.Y = 20; RigidBody body = new RigidBody(boxShape) { Pose = new Pose(position), }; Simulation.RigidBodies.Add(body); } }
public static ParticleSystem Create(ITriangleMesh mesh, ContentManager contentManager) { var ps = new ParticleSystem { Name = "GlowingMeshEffect", MaxNumberOfParticles = 100 }; ps.Parameters.AddUniform<float>(ParticleParameterNames.Lifetime).DefaultValue = 1.0f; ps.Effectors.Add(new StreamEmitter { DefaultEmissionRate = 100, }); // The particles start on random positions on the surface of the given triangle mesh. ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Position); ps.Effectors.Add(new StartOnMeshEffector { Parameter = ParticleParameterNames.Position, Mesh = mesh }); // Just to demonstrate a new custom effector: // The size follows a user-defined curve using the FuncEffector. ps.Parameters.AddVarying<float>(ParticleParameterNames.Size); ps.Effectors.Add(new FuncEffector<float, float> { InputParameter = ParticleParameterNames.NormalizedAge, OutputParameter = ParticleParameterNames.Size, Func = age => 6.7f * age * (1 - age) * (1 - age) * 0.4f, }); ps.Parameters.AddVarying<Vector3F>(ParticleParameterNames.Color); ps.Effectors.Add(new StartValueEffector<Vector3F> { Parameter = ParticleParameterNames.Color, Distribution = new BoxDistribution { MinValue = new Vector3F(0.5f, 0.5f, 0.5f), MaxValue = new Vector3F(1, 1, 1) } }); ps.Parameters.AddVarying<float>(ParticleParameterNames.Alpha); ps.Effectors.Add(new FuncEffector<float, float> { InputParameter = ParticleParameterNames.NormalizedAge, OutputParameter = ParticleParameterNames.Alpha, Func = age => 6.7f * age * (1 - age) * (1 - age), }); ps.Parameters.AddUniform<Texture2D>(ParticleParameterNames.Texture).DefaultValue = contentManager.Load<Texture2D>("Particles/Star"); ps.Parameters.AddUniform<float>(ParticleParameterNames.BlendMode).DefaultValue = 0; ps.Parameters.AddUniform<BillboardOrientation>(ParticleParameterNames.BillboardOrientation).DefaultValue = BillboardOrientation.ScreenAligned; ParticleSystemValidator.Validate(ps); return ps; }
/// <summary> /// Draws the triangles of the given mesh (with counter-clockwise winding for front faces). /// </summary> /// <param name="mesh">The triangle mesh.</param> /// <param name="pose">The pose.</param> /// <param name="scale">The scale.</param> /// <param name="color">The color.</param> /// <param name="drawWireFrame"> /// If set to <see langword="true"/> the wire-frame is drawn; otherwise the object is drawn /// with solid faces. /// </param> /// <param name="drawOverScene"> /// If set to <see langword="true"/> the object is drawn over the graphics scene (depth-test /// disabled). /// </param> /// <remarks> /// Warning: Calling this method every frame to render the same triangle mesh is very /// inefficient! If the triangle mesh does not change, call <see cref="DrawShape"/> with a /// <see cref="TriangleMeshShape"/> instead! /// </remarks> /// <exception cref="NotSupportedException"> /// Drawing solid objects with disabled depth test is not yet supported. /// </exception> public void DrawTriangles(ITriangleMesh mesh, Pose pose, Vector3F scale, Color color, bool drawWireFrame, bool drawOverScene) { if (!Enabled || mesh == null) return; if (!drawWireFrame && drawOverScene) throw new NotSupportedException("Drawing solid objects with disabled depth test is not yet supported."); TriangleBatch batch; if (drawOverScene) batch = OverSceneWireframeTriangleBatch; else if (drawWireFrame) batch = InSceneWireframeTriangleBatch; else if (color.A == 255) batch = OpaqueTriangleBatch; else batch = TransparentTriangleBatch; if (Vector3F.AreNumericallyEqual(scale, Vector3F.One) && !pose.HasRotation && !pose.HasTranslation) { int numberOfTriangles = mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) { var triangle = mesh.GetTriangle(i); var normal = Vector3F.Cross(triangle.Vertex1 - triangle.Vertex0, triangle.Vertex2 - triangle.Vertex0); // (normal is normalized in the BasicEffect HLSL.) // Draw with swapped winding order! batch.Add(ref triangle.Vertex0, ref triangle.Vertex2, ref triangle.Vertex1, ref normal, ref color); } } else { var transform = pose * Matrix44F.CreateScale(scale); int numberOfTriangles = mesh.NumberOfTriangles; for (int i = 0; i < numberOfTriangles; i++) { var triangle = mesh.GetTriangle(i); // Transform to world space. triangle.Vertex0 = transform.TransformPosition(triangle.Vertex0); triangle.Vertex1 = transform.TransformPosition(triangle.Vertex1); triangle.Vertex2 = transform.TransformPosition(triangle.Vertex2); var normal = Vector3F.Cross(triangle.Vertex1 - triangle.Vertex0, triangle.Vertex2 - triangle.Vertex0); // (normal is normalized in the BasicEffect HLSL.) // Draw with swapped winding order! batch.Add(ref triangle.Vertex0, ref triangle.Vertex2, ref triangle.Vertex1, ref normal, ref color); } } }
/// <summary> /// Initializes a new instance of the <see cref="TriangleMeshShape"/> class from the given /// triangle mesh. /// </summary> /// <param name="mesh">The mesh.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="mesh"/> is <see langword="null"/>. /// </exception> public TriangleMeshShape(ITriangleMesh mesh) : this(mesh, false, null) { }