private static void AddToMesh(this Csg.Polygon p, ref Mesh mesh) { // Polygons coming back from Csg can have an arbitrary number // of vertices. We need to retessellate the returned polygon. var tess = new Tess(); tess.NoEmptyPolygons = true; tess.AddContour(p.Vertices.ToContourVertices()); tess.Tessellate(WindingRule.Positive, LibTessDotNet.Double.ElementType.Polygons, 3); for (var i = 0; i < tess.ElementCount; i++) { var a = tess.Vertices[tess.Elements[i * 3]].Position.ToVector3(); var b = tess.Vertices[tess.Elements[i * 3 + 1]].Position.ToVector3(); var c = tess.Vertices[tess.Elements[i * 3 + 2]].Position.ToVector3(); var uva = (Csg.Vector2D)tess.Vertices[tess.Elements[i * 3]].Data; var uvb = (Csg.Vector2D)tess.Vertices[tess.Elements[i * 3 + 1]].Data; var uvc = (Csg.Vector2D)tess.Vertices[tess.Elements[i * 3 + 2]].Data; var v1 = mesh.AddVertex(a, uva.ToUV()); var v2 = mesh.AddVertex(b, uvb.ToUV()); var v3 = mesh.AddVertex(c, uvc.ToUV()); mesh.AddTriangle(v1, v2, v3); } }
internal static Mesh ToMesh(this Tess tess, Transform transform = null, Color color = default, Vector3 normal = default) { var faceMesh = new Mesh(); (Vector3 U, Vector3 V)basis = (default(Vector3), default(Vector3)); for (var i = 0; i < tess.ElementCount; i++) { var a = tess.Vertices[tess.Elements[i * 3]].Position.ToVector3(); var b = tess.Vertices[tess.Elements[i * 3 + 1]].Position.ToVector3(); var c = tess.Vertices[tess.Elements[i * 3 + 2]].Position.ToVector3(); if (transform != null) { a = transform.OfPoint(a); b = transform.OfPoint(b); c = transform.OfPoint(c); } if (i == 0) { // Calculate the texture space basis vectors // from the first triangle. This is acceptable // for planar faces. // TODO: Update this when we support non-planar faces. // https://gamedev.stackexchange.com/questions/172352/finding-texture-coordinates-for-plane basis = Tessellation.Tessellation.ComputeBasisAndNormalForTriangle(a, b, c, out Vector3 naturalNormal); if (normal == default) { normal = naturalNormal; } } var v1 = faceMesh.AddVertex(a, new UV(basis.U.Dot(a), basis.V.Dot(a)), normal, color: color); var v2 = faceMesh.AddVertex(b, new UV(basis.U.Dot(b), basis.V.Dot(b)), normal, color: color); var v3 = faceMesh.AddVertex(c, new UV(basis.U.Dot(c), basis.V.Dot(c)), normal, color: color); faceMesh.AddTriangle(v1, v2, v3); } return(faceMesh); }
/// <summary> /// Construct a mesh from an STL file. /// </summary> /// <param name="stlPath">The path to the STL file.</param> /// <param name="unit">The length unit used in the file.</param> /// <returns></returns> public static Mesh FromSTL(string stlPath, LengthUnit unit = LengthUnit.Millimeter) { List <Vertex> vertexCache = new List <Vertex>(); var mesh = new Mesh(); var conversion = Units.GetConversionToMeters(unit); using (var reader = new StreamReader(stlPath)) { string line; while ((line = reader.ReadLine()) != null) { line = line.TrimStart(); if (line.StartsWith("facet")) { vertexCache.Clear(); } if (line.StartsWith("vertex")) { var splits = line.Split(' '); var x = double.Parse(splits[1]) * conversion; var y = double.Parse(splits[2]) * conversion; var z = double.Parse(splits[3]) * conversion; var v = new Vertex(new Vector3(x, y, z)); mesh.AddVertex(v); vertexCache.Add(v); } if (line.StartsWith("endfacet")) { var t = new Triangle(vertexCache[0], vertexCache[1], vertexCache[2]); if (!HasDuplicatedVertices(t, out _)) { mesh.AddTriangle(t); } } } } mesh.ComputeNormals(); return(mesh); }
public static Elements.Geometry.Mesh ToMesh(this MeshData vMesh) { var mesh = new Elements.Geometry.Mesh(); var vertices = new List <Vertex>(); for (int i = 0; i < vMesh.points.length; i++) { var pt = (vMesh.points[i] as Array <double>).ToVector3(); var normal = (vMesh.normals[i] as Array <double>).ToVector3(); var vertex = new Vertex(pt, normal); vertices.Add(vertex); mesh.AddVertex(vertex); } Console.WriteLine(vertices.Count); for (int i = 0; i < vMesh.faces.length; i++) { var face = vMesh.faces[i] as Array <int>; mesh.AddTriangle(vertices[face[2]], vertices[face[1]], vertices[face[0]]); } return(mesh); }
/// <summary> /// Generates a Roof from a DXF Polyline and supplied elevation and thickness values. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A RoofByDXFOutputs instance containing computed results and the model with any new elements.</returns> public static RoofByDXFOutputs Execute(Dictionary <string, Model> inputModels, RoofByDXFInputs input) { DxfFile dxfFile; using (FileStream fs = new FileStream(input.DXF.LocalFilePath, FileMode.Open)) { dxfFile = DxfFile.Load(fs); } var polygons = new List <Polygon>(); foreach (DxfEntity entity in dxfFile.Entities) { if (entity.EntityType != DxfEntityType.LwPolyline) { continue; } var pline = (DxfLwPolyline)entity; if (pline.IsClosed == false) { continue; } var vertices = pline.Vertices.ToList(); var verts = new List <Vector3>(); vertices.ForEach(v => verts.Add(new Vector3(v.X, v.Y))); polygons.Add(new Polygon(verts)); } if (polygons.Count == 0) { throw new ArgumentException("No LWPolylines found in DXF."); } var highPoint = input.RoofElevation + input.RoofThickness; polygons = polygons.OrderByDescending(p => Math.Abs(p.Area())).ToList(); var polygon = polygons.First().IsClockWise() ? polygons.First().Reversed() : polygons.First(); polygon = polygon.TransformedPolygon(new Transform(0.0, 0.0, highPoint)); var underBoundary = polygon.TransformedPolygon(new Transform(0.0, 0.0, input.RoofThickness * -1.0)); var ePoints = polygon.Vertices.ToList(); var uPoints = underBoundary.Vertices.ToList(); var topSide = polygon.ToMesh(true); var underSide = underBoundary.ToMesh(false); var sideTriangles = new List <Elements.Geometry.Triangle>(); for (var i = 0; i < ePoints.Count; i++) { sideTriangles.Add(new Elements.Geometry.Triangle(new Vertex(ePoints[i]), new Vertex(uPoints[i]), new Vertex(uPoints[(i + 1) % uPoints.Count]))); sideTriangles.Add(new Elements.Geometry.Triangle(new Vertex(ePoints[i]), new Vertex(uPoints[(i + 1) % uPoints.Count]), new Vertex(ePoints[(i + 1) % ePoints.Count]))); } // Create an aggregated list of Triangles representing the Roof envelope. var envTriangles = new List <Elements.Geometry.Triangle>(); topSide.Triangles.ForEach(t => envTriangles.Add(t)); underSide.Triangles.ForEach(t => envTriangles.Add(t)); sideTriangles.ForEach(t => envTriangles.Add(t)); // Create an aggregated list of Vertices representing the Roof envelope. var enVertices = new List <Vertex>(); envTriangles.ForEach(t => enVertices.AddRange(t.Vertices)); // Construct the roof envelope in Elements.Geometry.mesh form. var Envelope = new Elements.Geometry.Mesh(); envTriangles.ForEach(t => Envelope.AddTriangle(t)); enVertices.ForEach(v => Envelope.AddVertex(v)); Envelope.ComputeNormals(); // Construct serializable topside mesh var triangles = new List <triangles>(); var indices = new List <vertices>(); var tsIV = topSide.ToIndexedVertices(); tsIV.triangles.ForEach(t => triangles.Add(new triangles(t))); tsIV.vertices.ForEach(v => indices.Add(new vertices(v.index, v.isBoundary, v.position))); var topside = new Elements.Mesh(triangles, indices); // Construct serializable underside mesh triangles.Clear(); indices.Clear(); var usIV = underSide.ToIndexedVertices(); usIV.triangles.ForEach(t => triangles.Add(new triangles(t))); usIV.vertices.ForEach(v => indices.Add(new vertices(v.index, v.isBoundary, v.position))); var underside = new Elements.Mesh(triangles, indices); // Construct serializable envelope mesh triangles.Clear(); indices.Clear(); var enIV = Envelope.ToIndexedVertices(); enIV.triangles.ForEach(t => triangles.Add(new triangles(t))); enIV.vertices.ForEach(v => indices.Add(new vertices(v.index, v.isBoundary, v.position))); var envelope = new Elements.Mesh(triangles, indices); var roof = new Roof( envelope, topside, underside, underBoundary, input.RoofElevation, highPoint, input.RoofThickness, polygon.Area(), new Transform(), BuiltInMaterials.Concrete, null, false, Guid.NewGuid(), "Roof"); var output = new RoofByDXFOutputs(polygon.Area()); output.Model.AddElement(new MeshElement(Envelope, BuiltInMaterials.Concrete)); output.Model.AddElement(roof); return(output); }
/// <summary> /// A mesh sphere. /// </summary> /// <param name="radius">The radius of the sphere.</param> /// <param name="divisions">The number of tessellations of the sphere.</param> /// <returns>A mesh.</returns> public static Mesh Sphere(double radius, int divisions = 10) { if (divisions < 2) { throw new ArgumentException(nameof(divisions), "The number of divisions must be greater than 2."); } var arc = new Arc(Vector3.Origin, radius, 0, 180).ToPolyline(divisions); var t = new Transform(); var vertices = new Vertex[divisions + 1, divisions + 1]; var mesh = new Mesh(); var div = 360.0 / divisions; for (var u = 0; u <= divisions; u++) { if (u > 0) { t.Rotate(Vector3.XAxis, div); } for (var v = 1; v < divisions; v++) { var pt = t.OfPoint(arc.Vertices[v]); var vx = new Vertex(pt) { UV = new UV((double)v / (double)divisions, (double)u / (double)divisions) }; vertices[u, v] = vx; mesh.AddVertex(vx); if (u > 0 && v > 1) { var a = vertices[u, v]; var b = vertices[u, v - 1]; var c = vertices[u - 1, v - 1]; var d = vertices[u - 1, v]; mesh.AddTriangle(a, b, c); mesh.AddTriangle(a, c, d); } } } var p1 = new Vertex(arc.Start) { UV = new UV(0, 0) }; var p2 = new Vertex(arc.End) { UV = new UV(1, 1) }; mesh.AddVertex(p1); mesh.AddVertex(p2); // Make the end caps separately to manage the singularity // at the poles. Attempting to do this in the algorithm above // will result in duplicate vertices for every arc section, which // is currently illegal in meshes. // TODO: This causes spiraling of the UV coordinates at the poles. for (var u = 1; u <= divisions; u++) { mesh.AddTriangle(p1, vertices[u - 1, 1], vertices[u, 1]); mesh.AddTriangle(p2, vertices[u, divisions - 1], vertices[u - 1, divisions - 1]); } mesh.ComputeNormals(); return(mesh); }
/// <summary> /// Creates a Roof from a supplied Polygon sketch and a supplied elevation. /// </summary> /// <param name="model">The input model.</param> /// <param name="input">The arguments to the execution.</param> /// <returns>A RoofBySketchOutputs instance containing computed results and the model with any new elements.</returns> public static RoofBySketchOutputs Execute(Dictionary <string, Model> inputModels, RoofBySketchInputs input) { var topSide = new Elements.Geometry.Mesh(); var area = 0.0; foreach (var triangle in input.Mesh.Triangles) { var a = topSide.AddVertex(triangle.Vertices[0].Position); var b = topSide.AddVertex(triangle.Vertices[1].Position); var c = topSide.AddVertex(triangle.Vertices[2].Position); var triAng = new Elements.Geometry.Triangle(a, b, c); topSide.AddTriangle(triAng); area += triAng.Area(); } topSide.ComputeNormals(); // Find the Mesh's lowest point and use the // roof thickness to the set the Roof's underside elevation. var vertices = input.Mesh.Vertices.ToList(); vertices = vertices.OrderBy(v => v.Position.Z).ToList(); var elevation = vertices.First().Position.Z - input.Thickness; // Find the topSide Mesh's perimeter points and use them to // construct a Mesh representing the underside of the Roof. var perimeter = topSide.EdgesPerimeters().First(); var ePoints = new List <Vector3>(); perimeter.ForEach(e => ePoints.AddRange(e.Points())); ePoints = ePoints.Distinct().ToList(); var uPoints = new List <Vector3>(); ePoints.ForEach(p => uPoints.Add(new Vector3(p.X, p.Y, elevation))); var underBoundary = new Polygon(uPoints); var underSide = underBoundary.ToMesh(false); // Use the topSide Mesh's edgePoints and the lower Mesh's underPoints // to construct a series of triangles forming the sides of the Roof. var sideTriangles = new List <Elements.Geometry.Triangle>(); for (var i = 0; i < ePoints.Count; i++) { sideTriangles.Add( new Elements.Geometry.Triangle(new Vertex(ePoints[i]), new Vertex(uPoints[i]), new Vertex(uPoints[(i + 1) % uPoints.Count]))); sideTriangles.Add( new Elements.Geometry.Triangle(new Vertex(ePoints[i]), new Vertex(uPoints[(i + 1) % uPoints.Count]), new Vertex(ePoints[(i + 1) % ePoints.Count]))); } // Construct the roof envelope in Elements.Geometry.mesh form. // We add vertices individually by position so that we don't affect // the original vertices of hte individual faces var Envelope = new Elements.Geometry.Mesh(); foreach (var t in topSide.Triangles) { var a = Envelope.AddVertex(t.Vertices[0].Position); var b = Envelope.AddVertex(t.Vertices[1].Position); var c = Envelope.AddVertex(t.Vertices[2].Position); Envelope.AddTriangle(new Triangle(a, b, c)); } foreach (var t in underSide.Triangles) { var a = Envelope.AddVertex(t.Vertices[0].Position); var b = Envelope.AddVertex(t.Vertices[1].Position); var c = Envelope.AddVertex(t.Vertices[2].Position); Envelope.AddTriangle(new Triangle(a, b, c)); } foreach (var t in sideTriangles) { var a = Envelope.AddVertex(t.Vertices[0].Position); var b = Envelope.AddVertex(t.Vertices[1].Position); var c = Envelope.AddVertex(t.Vertices[2].Position); Envelope.AddTriangle(new Triangle(a, b, c)); } // enVertices.ToList().ForEach(v => Envelope.AddVertex(v)); // envTriangles.ToList().ForEach(t => Envelope.AddTriangle(t)); Envelope.ComputeNormals(); //Record roof high point from topSide mesh. var highPoint = topSide.Points().OrderByDescending(p => p.Z).First().Z; // // code for when debugging the function. // var envelope = MakeEnvelope(); // var topside = MakeTopside(); // var underside = MakeUnderside(); // var underBoundary = Polygon.Rectangle(20.0, 20.0); // var elevation = 10.0; // var highPoint = 15.0; // var area = 100.0; var roof = new Roof( Envelope, topSide, underSide, underBoundary, elevation, highPoint, input.Thickness, area, new Transform(), BuiltInMaterials.Concrete, null, false, Guid.NewGuid(), "Roof"); var output = new RoofBySketchOutputs(area); output.Model.AddElement(new MeshElement(Envelope, BuiltInMaterials.Concrete)); output.Model.AddElement(roof); return(output); }