//TO DO: probably replace this method //TO DO: Add binary reader /// <summary> /// Loads a convex hull file made up of several convex segments from a .hull file generated in .obj format by /// ConvexDecomposition utility. /// </summary> /// <param name="reader">The stream from which to load the convex hulls.</param> /// <returns>An array containing the loaded ConvexSegment's.</returns> public static ConvexSegment[] LoadMultipleConvexHulls(StreamReader reader) { // 32 is the default number of hulls generated const int NumHulls = 32; string line; int verticesSoFar = 0; ConvexSegment[] hulls = new ConvexSegment[NumHulls]; // Read through unnecessary lines for (int i = 1; i <= 10; i++) { line = reader.ReadLine(); } for (int currentHull = 0; currentHull < NumHulls; ++currentHull) { line = reader.ReadLine(); line = reader.ReadLine(); string[] lineSplit = Split(line, ' '); int numVerts = int.Parse(lineSplit[5]); int numTriangles = int.Parse(lineSplit[8]); line = reader.ReadLine(); Vector3[] vertices = LoadObjVertices(reader, numVerts, 1); int[] triangles = LoadObjTriangles(reader, numTriangles, verticesSoFar, 1); verticesSoFar += vertices.Length; hulls[currentHull] = new ConvexSegment(vertices, triangles); } return(hulls); }
/// <summary> /// Test if two convex hulls are intersecting. /// </summary> /// <param name="left">The first hull to test for collisions.</param> /// <param name="leftEntitySpace">The transform which takes the first hull from object to world space.</param> /// <param name="right">The second hull to test for collisions with the first.</param> /// <param name="rightEntitySpace">The transform which takes the second hull from object to world space.</param> /// <returns>A CollisionResult if a collision was found otherwise null.</returns> public static CollisionResult <ConvexHull> Collided(ConvexHull left, Matrix leftEntitySpace, ConvexHull right, Matrix rightEntitySpace) { Vector3 furthestFaceNormal = new Vector3(); Vector3 furthestVertex = new Vector3(); float furthestDistance = 0.0f; bool collided = false; Matrix leftTransform = left.Transform * // from right hull object space to right entity object space leftEntitySpace; // from right entity object space to world space Matrix rightTransform = right.Transform * // from left hull object space to left entity object space rightEntitySpace; // from left entity object space to world space // Convert the convex segments from hull object space to world space. ConvexSegment leftSeg = left.Hull.Transform(leftTransform); ConvexSegment rightSeg = right.Hull.Transform(rightTransform); // Test if any points in the right hull lie within the left hull. foreach (Vector3 v in rightSeg.Vertices) { Vector3 normal; float distance; if (ConvexSegment.IsPointInHull(leftSeg, v, out normal, out distance)) { if (distance > furthestDistance) { collided = true; furthestDistance = distance; furthestVertex = v; furthestFaceNormal = normal; } } } if (collided) { return(new CollisionResult <ConvexHull>(left, right, furthestFaceNormal, furthestVertex, furthestDistance)); } else { return(null); } }
/// <summary> /// Tests if p is inside hull and returns the closestFace to p and the distance to that face. The point must be inside all /// faces of the convex hull. /// </summary> /// <param name="hull">The hull to test the point against.</param> /// <param name="p">The point being tested.</param> /// <param name="closestFaceNormal">If the point is in the hull this value is set to the normal of the closest face to the point.</param> /// <param name="closestDistance">If the point is in the hull this value is set to the distance to the closest face to /// the point.</param> /// <returns>True if p is inside hull.</returns> public static bool IsPointInHull(ConvexSegment hull, Vector3 p, out Vector3 closestFaceNormal, out float closestDistance) { closestFaceNormal = new Vector3(); closestDistance = float.PositiveInfinity; bool inside = true; // triStart is the starting point of a triangle in the list of triangles for (int triStart = 0; triStart < hull.triangles.Length / 3; ++triStart) { Vector3 a = hull.vertices[hull.triangles[3 * triStart]]; Vector3 n = hull.normals[triStart]; // If the angle between n and AP is larger then pi/2 then AP must be behind ABC Vector3 ap = p - a; // cosTheta is really |n||AP|cos(theta) but since |n| and |AP| are both positive it won't effect the result. float cosTheta = Vector3.Dot(n, ap); // distance = |<n, AP> n| = |<n, AP>||n| = |n||AP||cos(theta)| // However, since the point is outside the hull if cos(theta) < 0 // distance = |n||AP|cos(theta) float distance = -cosTheta; inside &= cosTheta < 0; if (distance < closestDistance) { closestFaceNormal = n; closestDistance = distance; } if (!inside) { break; } } return(inside); }
/// <summary> /// Create a convex hull from its position and orientation. /// </summary> /// <param name="hull">The underlying convex segment.</param> /// <param name="position">The position of the convex segment.</param> /// <param name="orientation">The orientation of the convex segment.</param> public ConvexHull(ConvexSegment hull, Vector3 position, Matrix orientation) : this(hull, orientation *Matrix.CreateTranslation(position)) { }
/// <summary> /// Construct a convex hull from a ConvexSegment and a corresponding transform. /// </summary> /// <param name="hull">The underlying convex segment.</param> /// <param name="transform">The transform which positions and orients the convex segment.</param> public ConvexHull(ConvexSegment hull, Matrix transform) { this.hull = hull; this.transform = transform; }