/// <summary> /// Calculates the normal for a connected triangle edge, assumes the edge is part of a complete triangle and there are 3 triangle edges /// </summary> public bool ComputeTriangleNormal(XbimTriangleEdge[] edges) { var p1 = _vertices[edges[0].StartVertexIndex].Position; var p2 = _vertices[edges[0].NextEdge.StartVertexIndex].Position; var p3 = _vertices[edges[0].NextEdge.NextEdge.StartVertexIndex].Position; var ax = p1.X; var bx = p2.X; var cx = p3.X; var ay = p1.Y; var by = p2.Y; var cy = p3.Y; var az = p1.Z; var bz = p2.Z; var cz = p3.Z; // calculate normal of a triangle var v = new Vec3( (by - ay) * (cz - az) - (bz - az) * (cy - ay), (bz - az) * (cx - ax) - (bx - ax) * (cz - az), (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) ); if (Vec3.Normalize(ref v)) { edges[0].Normal = v; edges[1].Normal = v; edges[2].Normal = v; return(true); } return(false); }
public static void ComputeNewellsNormal(ContourVertex[] vertices, ref Vec3 normal) { float x = 0, y = 0, z = 0; ContourVertex current; var previous = new ContourVertex(); int count = 0; int total = vertices.Length; for (var i = 0; i <= total; i++) { if (i < total) { current = vertices[i]; } else { current = vertices[0]; } if (count > 0) { float xn = previous.Position.X; float yn = previous.Position.Y; float zn = previous.Position.Z; float xn1 = current.Position.X; float yn1 = current.Position.Y; float zn1 = current.Position.Z; x += (yn - yn1) * (zn + zn1); y += (xn + xn1) * (zn - zn1); z += (xn - xn1) * (yn + yn1); } previous = current; count++; } normal.X = x; normal.Y = y; normal.Z = z; Vec3.Normalize(ref normal); }
public void BalanceNormals() { const double minAngle = Math.PI / 5; //set up the base normals foreach (var faceGroup in Faces) { foreach (var triangle in faceGroup.Value) { ComputeTriangleNormal(triangle); } } var edgesAtVertex = _faces.Values.SelectMany(el => el).SelectMany(e => e).Where(e => e != null).GroupBy(k => k.StartVertexIndex); foreach (var edges in edgesAtVertex) { //create a set of faces to divide the point into a set of connected faces var faceSet = new List <List <XbimTriangleEdge> >();//the first face set at this point //find an unconnected edge if one exists var unconnectedEdges = edges.Where(e => e.AdjacentEdge == null); var freeEdges = unconnectedEdges as IList <XbimTriangleEdge> ?? unconnectedEdges.ToList(); if (!freeEdges.Any()) //they are all connected to each other so find the first sharp edge or the any one if none sharp, this stops a face being split { XbimTriangleEdge nextConnectedEdge = edges.First(); freeEdges = new List <XbimTriangleEdge>(1) { edges.First() }; //take the first if we don't find a sharp edge //now look for any connected edges var visited = new HashSet <long>(); do { visited.Add(nextConnectedEdge.EdgeId); nextConnectedEdge = nextConnectedEdge.NextEdge.NextEdge.AdjacentEdge; if (nextConnectedEdge != null) { if (visited.Contains(nextConnectedEdge.EdgeId)) //we have been here before { break; //we are looping or at the start } //if the edge is sharp and the triangle is not colinear, start here var nextAngle = nextConnectedEdge.Angle; if (nextAngle > minAngle && nextConnectedEdge.Normal.IsValid) { freeEdges = new List <XbimTriangleEdge>(1) { nextConnectedEdge }; break; } } } while (nextConnectedEdge != null); } foreach (var edge in freeEdges) { var face = new List <XbimTriangleEdge> { edge }; faceSet.Add(face); XbimTriangleEdge nextConnectedEdge = edge; //now look for any connected edges var visited = new HashSet <long>(); do { visited.Add(nextConnectedEdge.EdgeId); var nextConnectedEdgeCandidate = nextConnectedEdge.NextEdge.NextEdge.AdjacentEdge; while (nextConnectedEdgeCandidate != null && !visited.Contains(nextConnectedEdgeCandidate.EdgeId) && !nextConnectedEdgeCandidate.Normal.IsValid) //skip colinear triangles { //set the colinear triangle to have the same normals as the current edge nextConnectedEdgeCandidate.Normal = nextConnectedEdge.Normal; nextConnectedEdgeCandidate.NextEdge.Normal = nextConnectedEdge.Normal; nextConnectedEdgeCandidate.NextEdge.NextEdge.Normal = nextConnectedEdge.Normal; visited.Add(nextConnectedEdgeCandidate.EdgeId); nextConnectedEdgeCandidate = nextConnectedEdgeCandidate.NextEdge.NextEdge.AdjacentEdge; } nextConnectedEdge = nextConnectedEdgeCandidate; if (nextConnectedEdge != null) { if (visited.Contains(nextConnectedEdge.EdgeId)) { break; //we are looping or at the start } //if the edge is sharp start a new face var angle = nextConnectedEdge.Angle; if (angle > minAngle && nextConnectedEdge.Normal.IsValid) { face = new List <XbimTriangleEdge>(); faceSet.Add(face); } face.Add(nextConnectedEdge); } } while (nextConnectedEdge != null); //move on to next face } //we have our smoothing groups foreach (var vertexEdges in faceSet.Where(f => f.Count > 1)) { var vertexNormal = Vec3.Zero; foreach (var edge in vertexEdges) { if (edge.Normal.IsValid) { Vec3.AddTo(ref vertexNormal, ref edge.Normal); } } Vec3.Normalize(ref vertexNormal); foreach (var edge in vertexEdges) { edge.Normal = vertexNormal; } } } //now regroup faces _faces = _faces.Values.SelectMany(v => v).GroupBy(t => ComputeTrianglePackedNormalInt(t)).ToDictionary(k => k.Key, v => v.ToList()); }