private List<Face> Tessellate(Face f) { // Base Case: Face is already a triangle if (f.vertices.Count() == 3) { return new List<Face>(new Face[] { f }); } else { int verts = f.vertices.Count(); // Find an ear on the face, remove it for (int i = 0; i < verts; i++) { Vector3 v1 = f.vertices[i]; Vector3 v2 = f.vertices[(i + 1) % verts]; Vector3 v3 = f.vertices[(i + 2) % verts]; Face tri = new Face(); tri.vertices.Add(v1); tri.vertices.Add(v2); tri.vertices.Add(v3); bool anyPointInPolygon = false; foreach (Vector3 p in f.vertices) { if (p != v1 && p != v2 && p != v3 && IsPointInPolygon(tri, p)) { anyPointInPolygon = true; break; } } // First check: see if any point in the original polygon is inside the new triangle if (anyPointInPolygon) //AnyPointInPolygon(tri, f.vertices)) { // Can't use this triangle, move onto the next one } else { // Second Check: see if the midpoint on the new line is within the original polygon Vector3 midpoint = (v1 + v3) / 2; if (!IsPointInPolygon(f, midpoint)) { // Can't use this triangle, move onto the next one } else { // Triangle satisfies criteria for an ear! Add it to the face list. List<Face> faces = new List<Face>(); faces.Add(tri); f.vertices.RemoveAt((i + 1) % verts); faces.AddRange(Tessellate(f)); return faces; } } } } // If we get here, the face has no ear and more than 3 vertices... Bad situation! //throw new Exception("Failed to tessellate face!"); badfaces.Add(f); return new List<Face>(); }
public void addFace(Vertex[] vertices) { HalfEdge[] currentHalfEdges = new HalfEdge[vertices.Length]; //add halfedges for (int curr = 0; curr < vertices.Length; curr++) { int next_he = (curr + 1) % vertices.Length; // look if half-edge already exists HalfEdge exist = existHalfEdge(vertices[curr], vertices[next_he]); // if it does, add it to the array, otherwise create it if (exist == null) { // create half edge from current to next vertex currentHalfEdges[curr] = new HalfEdge(vertices[curr], vertices[next_he]); HalfEdge twin = new HalfEdge(vertices[next_he],vertices[curr]); currentHalfEdges[curr].twin = twin; twin.twin = currentHalfEdges[curr]; // add halfedge to polynet halfedges dictionary this.halfEdges.Add(vertices[curr], vertices[next_he], currentHalfEdges[curr]); this.halfEdges.Add(vertices[next_he], vertices[curr], twin); } else { currentHalfEdges[curr] = exist; } } // create face with some halfedge Face face = new Face(currentHalfEdges[0]); // calculate face's normal face.normal = faceNormal(vertices); // add face to faces list faces.Add(face); // link halfedges with next and prev for(int i = 0; i < currentHalfEdges.Length; i++){ int next = (i + 1) % currentHalfEdges.Length; int prev = (currentHalfEdges.Length + i - 1) % currentHalfEdges.Length; currentHalfEdges[i].next = currentHalfEdges[next]; currentHalfEdges[i].prev = currentHalfEdges[prev]; // link face to halfedge currentHalfEdges[i].face = face; } }
static bool IsPointInPolygon(Face f, Vector3 point) { // TODO: Implement a ray tracing approach instead - angle summation can be unreliable and slow. float angle = 0; for (int i = 0; i < f.vertices.Count(); i++) { Vector3 v1 = f.vertices[i] - point; Vector3 v2 = f.vertices[(i + 1) % f.vertices.Count()] - point; float direction = Vector3.Dot (Vector3.Cross(v1, v2), f.Normal) / (v1.Length * v2.Length); if (direction > -.0001) // Biased towards within polygon { angle += (float)Math.Acos(Vector3.Dot(v1, v2) / (v1.Length * v2.Length)); } else { angle -= (float)Math.Acos(Vector3.Dot(v1, v2) / (v1.Length * v2.Length)); } } angle = (float)Math.Abs(angle); //Console.WriteLine("In Face = {0}, Angle = {1} | {2}", Utilities.MathHelper.NearlyEqual (angle, OpenTK.MathHelper.TwoPi), angle, OpenTK.MathHelper.TwoPi); return Utilities.MathHelper.NearlyEqual(angle, OpenTK.MathHelper.TwoPi) || angle > OpenTK.MathHelper.TwoPi; }
public void LoadObj(string filepath) { string[] strings = System.IO.File.ReadAllLines(filepath); Regex vertexRegex = new Regex (@"^v\s+(?<x>\S+)\s+(?<y>\S+)\s+(?<z>\S+)", RegexOptions.IgnoreCase); //Regex faceRegex = new Regex (@"^f(?<face_data>\s+\w+)+", RegexOptions.IgnoreCase); //Regex f = new Regex(@"^f(?<face_data>[^/]*/(?<itemNumber>\w+))+", RegexOptions.IgnoreCase); Regex faceRegex = new Regex(@"^f(?<face_data>\s+(?<vertex>\d+)/?(?<texture_coordinate>\d+)?/?(?<vertex_normal>\d+)?)+", RegexOptions.IgnoreCase); //Regex f = new Regex(@"(?<container>\d{5})(?<serial>[^/]*/(?<itemNumber>\w{5})(?<quantity>\d{3}))+", RegexOptions.IgnoreCase); // "f 1/1/1 2/2/1 3/3/1 4/4/1 5/5/1" foreach (string s in strings) { // Lines starting with v are a vertex: // "v 10.2426 4.5e-013 -31.7638" if (vertexRegex.IsMatch(s)) { Match m = vertexRegex.Match(s); float x = float.Parse(m.Groups["x"].Value); float y = float.Parse(m.Groups["y"].Value); float z = float.Parse(m.Groups["z"].Value); vertices.Add(new Vector3 (x, z, y) * 1000); //Console.WriteLine("Vertex Found: {0}", v); } else if (faceRegex.IsMatch(s)) { Match m = faceRegex.Match(s); //Console.WriteLine(m.Groups["face_data"].Captures.Count); //Console.WriteLine(m.Groups["vertex"].Captures.Count); //Console.WriteLine(m.Groups["texture_coordinate"].Captures.Count); //Console.WriteLine(m.Groups["vertex_normal"].Captures.Count); Face face = new Face(); CaptureCollection vert_captures = m.Groups["vertex"].Captures; CaptureCollection texcoord_captures = m.Groups["texture_coordinate"].Captures; CaptureCollection norm_captures = m.Groups["vertex_normal"].Captures; for (int i = 0; i < vert_captures.Count; i++) { int vert_index = int.Parse(vert_captures[i].Value) - 1; if (vert_index < 0 || vert_index > vertices.Count) { Console.WriteLine("Bad vertex index {0}, only {1} vertices loaded", vert_index, vertices.Count); } else { face.vertices.Add(this.vertices[vert_index]); } } if (texcoord_captures.Count == vert_captures.Count) { // TODO: Add texture coordinates to the face } if (norm_captures.Count == vert_captures.Count) { // TODO: Add vertex normals to the face } if (face.vertices.Count < 3) { Console.WriteLine("Bad face defined, less than 3 vertices"); } else { AddFace(face); } } } }
public Plane(Face f) { point = f.vertices[0]; normal = f.Normal; }
public void AddFace(Face f) { //this.faces.Add(f); this.faces.AddRange(Tessellate(f)); }
private PolyLine TrianglePlaneIntersect(Face f, Plane p) { PolyLine polyLine = new PolyLine(); float epsilon = 0.01f; // TODO: Auto compute based on scale float epsilon_unit = 0.00001f; // Unit size epsilon value Vector3 f_normal = f.Normal; f_normal.Normalize(); p.normal.Normalize(); if ((f_normal - p.normal).Length < epsilon_unit || (f_normal + p.normal).Length < epsilon_unit) { // No intersection } else { Vector3 expected_direction = Vector3.Cross(f.Normal, p.normal); // Assume we're dealing with triangles only int verts = f.vertices.Count(); if (verts != 3) { throw new Exception("The number of vertices is not 3!"); } float[] d = new float[3]; for (int i = 0; i < 3; i++) { d[i] = p.Distance(f.vertices[i]); } for (int i = 0; i < 3; i++) { // Is the line on the plane? if (Math.Abs(d[i]) < epsilon && Math.Abs(d[(i + 1) % 3]) < epsilon) { polyLine.points.Add(f.vertices[i]); polyLine.points.Add(f.vertices[(i + 1) % 3]); break; } } if (polyLine.points.Count() == 0) { // Line not on a plain: might have an intersection with a point and the opposite line for (int i = 0; i < 3; i++) { float d1 = d[i]; float d2 = d[(i + 1) % 3]; float d3 = d[(i + 2) % 3]; if (Math.Abs(d[i]) < epsilon && Math.Sign(d2) != Math.Sign(d3)) { d2 = Math.Abs(d2); d3 = Math.Abs(d3); // One negative, one positive float total = d2 + d3; Vector3 result = (f.vertices[(i + 1) % 3] * d3 + f.vertices[(i + 2) % 3] * d2) / total; polyLine.points.Add(f.vertices[i]); polyLine.points.Add(result); break; } } if (polyLine.points.Count() == 0) { // No edge in plane and no point + line intersect: maybe two lines intersect? for (int i = 0; i < 3; i++) { // Intersection with an edge if (Math.Sign(d[i]) != Math.Sign(d[(i + 1) % 3])) { float d1 = Math.Abs(d[i]); float d2 = Math.Abs(d[(i + 1) % 3]); float total = d1 + d2; Vector3 result = (f.vertices[i] * d2 + f.vertices[(i + 1) % 3] * d1) / total; polyLine.points.Add(result); if (polyLine.points.Count() == 2) { break; } } } } } if (polyLine.points.Count() >= 2) { //DrawCone1(polyLine.points[0], polyLine.points[1]); Vector3 direction = polyLine.points[1] - polyLine.points[0]; if (Vector3.Dot(direction, expected_direction) < 0) { PolyLine reversed = new PolyLine(); reversed.points.Add(polyLine.points[1]); reversed.points.Add(polyLine.points[0]); polyLine = reversed; } // // // Color[] colors = new Color[] { Color.DarkRed, Color.LightGreen, Color.DarkBlue }; // int i = 0; // GL.Begin(BeginMode.LineLoop); // foreach (Vector3 v in polyLine.points) // { // GL.Color3(colors[i++]); // GL.Vertex3(v); // // } // GL.End(); // // GL.PointSize(10); // GL.Color3(Color.Orange); // GL.Begin(BeginMode.Points); // foreach (Vector3 v in polyLine.points) // { // GL.Vertex3(v); // } // GL.End(); // GL.PointSize(1); } } return polyLine; }