/// <summary> /// Pass in a cloud of points and an array of triangle indices shall be returned. /// </summary> /// <param name="points">Points to create hull from.</param> /// <returns>Triangle index array of hull.</returns> public static int[] ComputeHull(Vector3[] points) { if (points.Length < 3) return null; //local copy of the point set Face.points = points; //calculates the first convex tetrahedron //creates a face with the first 3 vertices Face faceA = new Face(0, 1, 2); //this is the center of the tetrahedron, all face should point outwards: //they should not be visible to the centroid Vector3 v = Centroid(points, 3, faceA); if (faceA.IsVisible(v)) { faceA.Flip(); } Face face0 = new Face(3, faceA.i0, faceA.i1); if (face0.IsVisible(v)) { face0.Flip(); } Face face1 = new Face(3, faceA.i1, faceA.i2); if (face1.IsVisible(v)) { face1.Flip(); } Face face2 = new Face(3, faceA.i2, faceA.i0); if (face2.IsVisible(v)) { face2.Flip(); } //store the tetrahedron faces in the valid faces list validFaces = new List<Face> { faceA, face0, face1, face2 }; visibleFaces = new List<Face>(points.Length); tmpFaces = new List<Face>(points.Length); //so as we have a convex tetrahedron, we can skip the first 4 points for (int i = 4; i < points.Length; i++) { //for each avaiable vertices v = points[i]; //checks the point's visibility from all faces visibleFaces.Clear(); for (int j = 0; j < validFaces.Count; j++) { if (validFaces[j].IsVisible(v)) { visibleFaces.Add(validFaces[j]); } } //the vertex is not visible : it is inside the convex hull, keep on if (visibleFaces.Count == 0) continue; //the vertex is outside the convex hull //delete all visible faces from the valid List for (int index = 0; index < visibleFaces.Count; index++) { validFaces.Remove(visibleFaces[index]); } //special case : only one face is visible //it's ok to create 3 faces directly for they won't enclose any other point if (visibleFaces.Count == 1) { faceA = visibleFaces[0]; validFaces.Add(new Face(i, faceA.i0, faceA.i1)); validFaces.Add(new Face(i, faceA.i1, faceA.i2)); validFaces.Add(new Face(i, faceA.i2, faceA.i0)); continue; } if (visibleFaces.Count > 2000) { Debug.LogWarning("Visible faces is too big, " + visibleFaces.Count + " cancelling operation."); return new int[0]; } //creates all possible new faces from the visibleFaces tmpFaces.Clear(); for (int k = 0; k < visibleFaces.Count; k++) { tmpFaces.Add(new Face(i, visibleFaces[k].i0, visibleFaces[k].i1)); tmpFaces.Add(new Face(i, visibleFaces[k].i1, visibleFaces[k].i2)); tmpFaces.Add(new Face(i, visibleFaces[k].i2, visibleFaces[k].i0)); } if (tmpFaces.Count > 8000) { Debug.LogWarning("Temp faces is too big, " + tmpFaces.Count + " cancelling operation."); return new int[0]; } //Face other; for (int l = 0; l < tmpFaces.Count; l++) { Face face = tmpFaces[l]; //search if there is a point in front of the face : //this means the face doesn't belong to the convex hull for (int index1 = 0; index1 < tmpFaces.Count; index1++) { if (face != tmpFaces[index1]) { if (face.IsVisible(tmpFaces[index1].Centroid)) { face = null; break; } } } //the face has no point in front of it if (face != null) { validFaces.Add(face); } } } var result = new int[validFaces.Count * 3]; int vertIndex = 0; for (int i = 0; i < validFaces.Count; i++) { result[vertIndex++] = validFaces[i].i0; result[vertIndex++] = validFaces[i].i1; result[vertIndex++] = validFaces[i].i2; } return result; }
private static Vector3 Centroid(Vector3[] points, int index, Face face) { Vector3 p = points[index]; p += points[face.i0]; p += points[face.i1]; p += points[face.i2]; return p / 4; }