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()); }
private XbimTriangulatedMesh TriangulateFaces(IList <IIfcFace> ifcFaces, int entityLabel, float precision) { var faceId = 0; var faceCount = ifcFaces.Count; var triangulatedMesh = new XbimTriangulatedMesh(faceCount, precision); foreach (var ifcFace in ifcFaces) { //improves performance and reduces memory load var tess = new Tess(); var contours = new List <ContourVertex[]>(/*Count?*/); foreach (var bound in ifcFace.Bounds) //build all the loops { var polyLoop = bound.Bound as IIfcPolyLoop; if (polyLoop == null) { continue; //skip empty faces } var polygon = polyLoop.Polygon; if (polygon.Count < 3) { continue; //skip non-polygonal faces } var is3D = (polygon[0].Dim == 3); var contour = new ContourVertex[polygon.Count]; if (bound.Orientation) { for (var j = 0; j < polygon.Count; j++) { var v = new Vec3(polygon[j].X, polygon[j].Y, is3D ? polygon[j].Z : 0); triangulatedMesh.AddVertex(v, ref contour[j]); } } else { var i = 0; for (var j = polygon.Count - 1; j >= 0; j--) { var v = new Vec3(polygon[j].X, polygon[j].Y, is3D ? polygon[j].Z : 0); triangulatedMesh.AddVertex(v, ref contour[i]); i++; } } contours.Add(contour); } if (contours.Any()) { if (contours.Count == 1 && contours[0].Length == 3) //its a triangle just grab it { triangulatedMesh.AddTriangle(contours[0][0].Data, contours[0][1].Data, contours[0][2].Data, faceId); faceId++; } //else //if (contours.Count == 1 && contours[0].Length == 4) //its a quad just grab it //{ // foreach (var v in contours[0]) // { // Console.WriteLine("{0:F4} ,{1:F4}, {2:F4}", v.Position.X, v.Position.Y, v.Position.Z); // } // Console.WriteLine(""); // triangulatedMesh.AddTriangle(contours[0][0].Data, contours[0][1].Data, contours[0][3].Data, faceId); // triangulatedMesh.AddTriangle(contours[0][3].Data, contours[0][1].Data, contours[0][2].Data, faceId); // faceId++; //} else //it is multi-sided and may have holes { tess.AddContours(contours); tess.Tessellate(WindingRule.EvenOdd, ElementType.Polygons, 3); var faceIndices = new List <int>(tess.ElementCount * 3); var elements = tess.Elements; var contourVerts = tess.Vertices; for (var j = 0; j < tess.ElementCount * 3; j++) { var idx = contourVerts[elements[j]].Data; if (idx < 0) //WE HAVE INSERTED A POINT { //add it to the mesh triangulatedMesh.AddVertex(contourVerts[elements[j]].Position, ref contourVerts[elements[j]]); } faceIndices.Add(contourVerts[elements[j]].Data); } if (faceIndices.Count > 0) { for (var j = 0; j < tess.ElementCount; j++) { var p1 = faceIndices[j * 3]; var p2 = faceIndices[j * 3 + 1]; var p3 = faceIndices[j * 3 + 2]; triangulatedMesh.AddTriangle(p1, p2, p3, faceId); } faceId++; } } } } triangulatedMesh.UnifyFaceOrientation(entityLabel); return(triangulatedMesh); }
public static void Cross(ref Vec3 u, ref Vec3 v, out Vec3 cross) { cross.X = u.Y * v.Z - u.Z * v.Y; cross.Y = u.Z * v.X - u.X * v.Z; cross.Z = u.X * v.Y - u.Y * v.X; }
public static void Dot(ref Vec3 u, ref Vec3 v, out double dot) { dot = u.X * v.X + u.Y * v.Y + u.Z * v.Z; }
public static void Neg(ref Vec3 v) { v.X = -v.X; v.Y = -v.Y; v.Z = -v.Z; }
public static void AddTo(ref Vec3 lhs, ref Vec3 rhs) { lhs.X += rhs.X; lhs.Y += rhs.Y; lhs.Z += rhs.Z; }
public static void Sub(ref Vec3 lhs, ref Vec3 rhs, out Vec3 result) { result.X = lhs.X - rhs.X; result.Y = lhs.Y - rhs.Y; result.Z = lhs.Z - rhs.Z; }