private bool exportMesh(String filename, Mesh mesh) { bool bSaved = false; try { StreamWriter sout = new StreamWriter(filename, false); sout.WriteLine("P1X, P1Y, P1Z, P2X, P2Y, P2Z, P3X, P3Y, P3Z"); for (int tlp = 0; tlp < mesh.NumTriangles; tlp++) { MeshTriangle tri = mesh.get_Triangle(tlp); XYZ p1 = mesh.Vertices[(int)tri.get_Index(0)]; XYZ p2 = mesh.Vertices[(int)tri.get_Index(1)]; XYZ p3 = mesh.Vertices[(int)tri.get_Index(2)]; String tstr = String.Format("{0:0.000}, {1:0.000}, {2:0.000}, {3:0.000}, {4:0.000}, {5:0.000}, {6:0.000}, {7:0.000}, {8:0.000}", new object[] { p1.X, p1.Y, p1.Z, p2.X, p2.Y, p2.Z, p3.X, p3.Y, p3.Z }); sout.WriteLine(tstr); } sout.Close(); bSaved = true; } catch (Exception ex) { MessageBox.Show(ex.Message, "Unable to write file", MessageBoxButtons.OK, MessageBoxIcon.Error); } return(bSaved); }
/***************************************************/ /**** Public Methods ****/ /***************************************************/ public static oM.Geometry.Face FromRevit(this MeshTriangle triangle) { if (triangle == null) { return(null); } return(new oM.Geometry.Face { A = (int)triangle.get_Index(0), B = (int)triangle.get_Index(1), C = (int)triangle.get_Index(2) }); }
static private bool ExportMeshes(List <Mesh> meshes, ref Va3cContainer.Va3cGeometry meshGroupGeometry) { foreach (Mesh mesh in meshes) { int currentPointNum = meshGroupGeometry.data.points; int currentTriNum = meshGroupGeometry.data.triangles; IList <XYZ> points = mesh.Vertices; int pointNum = points.Count; int triNum = mesh.NumTriangles; meshGroupGeometry.data.points += pointNum; meshGroupGeometry.data.triangles += triNum; foreach (XYZ point in points) { meshGroupGeometry.data.vertices.Add((float)point.X); meshGroupGeometry.data.vertices.Add((float)point.Y); meshGroupGeometry.data.vertices.Add((float)point.Z); //test meshGroupGeometry.data.normals.Add(0.0f); meshGroupGeometry.data.normals.Add(0.0f); meshGroupGeometry.data.normals.Add(1.0f); meshGroupGeometry.data.uvs.Add(0.0f); meshGroupGeometry.data.uvs.Add(0.0f); } for (int i = 0; i < triNum; ++i) { MeshTriangle meshTri = mesh.get_Triangle(i); int index0 = (int)meshTri.get_Index(0) + currentPointNum; int index1 = (int)meshTri.get_Index(1) + currentPointNum; int index2 = (int)meshTri.get_Index(2) + currentPointNum; meshGroupGeometry.data.indices.Add(index0); meshGroupGeometry.data.indices.Add(index1); meshGroupGeometry.data.indices.Add(index2); } } if (meshGroupGeometry.data.points > 0 && meshGroupGeometry.data.triangles > 0) { return(true); } return(false); }
/// <summary> /// Function called before and after triangle merging. /// Before merging it calculates the Euler characteristic of the original mesh. /// After merging it calculates the Euler characteristic of the merged mesh. /// </summary> private int CalculateEulerCharacteristic() { int noVertices = 0; int noHoles = 0; // Stays zero if mesh is triangular int noFaces; HashSet <IndexSegment> edges = new HashSet <IndexSegment>(new SegmentComparer(true /*compareBothDirections*/)); if (m_MergedFaceSet.Count != 0) { // Merging already occurred, calculate new Euler characteristic noFaces = m_MergedFaceSet.Count; foreach (int mergedFace in m_MergedFaceSet) { m_FacesCollDict[mergedFace].OuterAndInnerBoundaries.ToList().ForEach(vp => edges.Add(vp.Value)); if (m_FacesCollDict[mergedFace].IndexedInnerBoundaries != null) { noHoles += m_FacesCollDict[mergedFace].IndexedInnerBoundaries.Count; } } noVertices = m_MeshVertices.Count; // m_MeshVertices doesn't contain isolated vertices } else { if (IsMesh) { noVertices = m_MeshGeom.Vertices.Count; noFaces = m_MeshGeom.NumTriangles; for (int faceIdx = 0; faceIdx < noFaces; faceIdx++) { MeshTriangle tri = m_MeshGeom.get_Triangle(faceIdx); edges.Add(new IndexSegment((int)tri.get_Index(0), (int)tri.get_Index(1))); edges.Add(new IndexSegment((int)tri.get_Index(1), (int)tri.get_Index(2))); edges.Add(new IndexSegment((int)tri.get_Index(2), (int)tri.get_Index(0))); } } else { noVertices = m_Geom.VertexCount; noFaces = m_Geom.TriangleCount; for (int faceIdx = 0; faceIdx < noFaces; faceIdx++) { TriangleInShellComponent tri = m_Geom.GetTriangle(faceIdx); edges.Add(new IndexSegment(tri.VertexIndex0, tri.VertexIndex1)); edges.Add(new IndexSegment(tri.VertexIndex1, tri.VertexIndex2)); edges.Add(new IndexSegment(tri.VertexIndex2, tri.VertexIndex0)); } } } // V - E + F - I return(noVertices - edges.Count + noFaces - noHoles); }
public static List <LMAStudio.StreamVR.Common.Models.Face> ConvertToDTO(Autodesk.Revit.DB.Element parent, Autodesk.Revit.DB.Solid geometry) { List <LMAStudio.StreamVR.Common.Models.Face> wallFaces = new List <LMAStudio.StreamVR.Common.Models.Face>(); IEnumerable <Face> faces = geometry.Faces.Cast <Face>(); Face topFace = faces.Where( (f, i) => i == 1 ).FirstOrDefault() ?? faces.FirstOrDefault(); wallFaces = faces.Select((f, index) => { Mesh m = f.Triangulate(); List <int> indices = new List <int>(); for (int i = 0; i < m.NumTriangles; i++) { MeshTriangle mt = m.get_Triangle(i); for (int j = 0; j < 3; j++) { indices.Add((int)mt.get_Index(j)); } } return(new LMAStudio.StreamVR.Common.Models.Face { ElementId = parent.Id.ToString(), FaceIndex = index, MaterialId = f.MaterialElementId?.ToString(), Indices = indices, Vertices = m.Vertices.Select(v => new LMAStudio.StreamVR.Common.Models.XYZ { X = v.X, Y = v.Y, Z = v.Z, }).ToList() }); }).ToList(); return(wallFaces); }
/// <summary> /// Function called before and after triangle merging. /// Before merging it calculates the Euler characteristic of the original mesh. /// After merging it calculates the Euler characteristic of the merged mesh. /// </summary> private int CalculateEulerCharacteristic() { int noVertices = (IsMesh) ? _meshGeom.Vertices.Count : _geom.VertexCount; int noHoles = 0; // Stays zero if mesh is triangular int noFaces; HashSet <IndexSegment> edges = new HashSet <IndexSegment>(new SegmentComparer(true /*compareBothDirections*/)); if (_mergedFaceList.Count != 0) { // Merging already occurred, calculate new Euler characteristic noFaces = _mergedFaceList.Count; foreach (int mergedFace in _mergedFaceList) { edges.UnionWith(facesColl[mergedFace].outerAndInnerBoundaries); if (facesColl[mergedFace].indexedInnerBoundaries != null) { noHoles += facesColl[mergedFace].indexedInnerBoundaries.Count; } } } else { if (IsMesh) { noFaces = _meshGeom.NumTriangles; for (int faceIdx = 0; faceIdx < noFaces; faceIdx++) { MeshTriangle tri = _meshGeom.get_Triangle(faceIdx); edges.Add(new IndexSegment((int)tri.get_Index(0), (int)tri.get_Index(1))); edges.Add(new IndexSegment((int)tri.get_Index(1), (int)tri.get_Index(2))); edges.Add(new IndexSegment((int)tri.get_Index(2), (int)tri.get_Index(0))); } } else { noFaces = _geom.TriangleCount; for (int faceIdx = 0; faceIdx < noFaces; faceIdx++) { TriangleInShellComponent tri = _geom.GetTriangle(faceIdx); edges.Add(new IndexSegment(tri.VertexIndex0, tri.VertexIndex1)); edges.Add(new IndexSegment(tri.VertexIndex1, tri.VertexIndex2)); edges.Add(new IndexSegment(tri.VertexIndex2, tri.VertexIndex0)); } } } // V - E + F - I return(noVertices - edges.Count + noFaces - noHoles); }
public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Document doc = uidoc.Document; Selection sel = uidoc.Selection; ICollection <ElementId> ids = sel.GetElementIds(); if (1 != ids.Count) { message = "Please select an element to export to TWGL."; return(Result.Failed); } Element e = null; foreach (ElementId id in ids) { e = doc.GetElement(id); } // Determine bounding box in order to translate // all coordinates to bounding box midpoint. BoundingBoxXYZ bb = e.get_BoundingBox(null); XYZ pmin = bb.Min; XYZ pmax = bb.Max; XYZ vsize = pmax - pmin; XYZ pmid = pmin + 0.5 * vsize; Options opt = new Options(); GeometryElement geo = e.get_Geometry(opt); List <int> faceIndices = new List <int>(); List <int> faceVertices = new List <int>(); List <double> faceNormals = new List <double>(); int[] triangleIndices = new int[3]; XYZ[] triangleCorners = new XYZ[3]; foreach (GeometryObject obj in geo) { Solid solid = obj as Solid; if (solid != null && 0 < solid.Faces.Size) { faceIndices.Clear(); faceVertices.Clear(); faceNormals.Clear(); foreach (Face face in solid.Faces) { Mesh mesh = face.Triangulate(); int nTriangles = mesh.NumTriangles; IList <XYZ> vertices = mesh.Vertices; int nVertices = vertices.Count; List <int> vertexCoordsMm = new List <int>(3 * nVertices); // A vertex may be reused several times with // different normals for different faces, so // we cannot precalculate normals per vertex. //List<double> normals = new List<double>( 3 * nVertices ); foreach (XYZ v in vertices) { // Translate the entire element geometry // to the bounding box midpoint and scale // to metric millimetres. XYZ p = v - pmid; vertexCoordsMm.Add(Util.FootToMm(p.X)); vertexCoordsMm.Add(Util.FootToMm(p.Y)); vertexCoordsMm.Add(Util.FootToMm(p.Z)); } for (int i = 0; i < nTriangles; ++i) { MeshTriangle triangle = mesh.get_Triangle(i); for (int j = 0; j < 3; ++j) { int k = (int)triangle.get_Index(j); triangleIndices[j] = k; triangleCorners[j] = vertices[k]; } // Calculate constant triangle facet normal. XYZ v = triangleCorners[1] - triangleCorners[0]; XYZ w = triangleCorners[2] - triangleCorners[0]; XYZ triangleNormal = v .CrossProduct(w) .Normalize(); for (int j = 0; j < 3; ++j) { int nFaceVertices = faceVertices.Count; Debug.Assert(nFaceVertices.Equals(faceNormals.Count), "expected equal number of face vertex and normal coordinates"); faceIndices.Add(nFaceVertices / 3); int i3 = triangleIndices[j] * 3; // Rotate the X, Y and Z directions, // since the Z direction points upward // in Revit as opposed to sideways or // outwards or forwards in WebGL. faceVertices.Add(vertexCoordsMm[i3 + 1]); faceVertices.Add(vertexCoordsMm[i3 + 2]); faceVertices.Add(vertexCoordsMm[i3]); if (RetainCurvedSurfaceFacets) { faceNormals.Add(triangleNormal.Y); faceNormals.Add(triangleNormal.Z); faceNormals.Add(triangleNormal.X); } else { UV uv = face.Project( triangleCorners[j]).UVPoint; XYZ normal = face.ComputeNormal(uv); faceNormals.Add(normal.Y); faceNormals.Add(normal.Z); faceNormals.Add(normal.X); } } } } // Scale the vertices to a [-1,1] cube // centered around the origin. Translation // to the origin was already performed above. double scale = 2.0 / Util.FootToMm( Util.MaxCoord(vsize)); string json_geometry_data = GetJsonGeometryData(scale, faceIndices, faceVertices, faceNormals); DisplayWgl(json_geometry_data); // Ignore other solids in this element. // Please use the custom exporter for // more complex element geometry. break; } } return(Result.Succeeded); }
/// <summary> /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created /// </summary> public void SimplifyAndMergeFaces(bool ignoreMerge = false) { int eulerBefore = ignoreMerge ? 0 : CalculateEulerCharacteristic(); int noTriangle = GetTriangleCount(); IEqualityComparer <XYZ> normalComparer = new VectorCompare(); Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer); for (int ef = 0; ef < noTriangle; ++ef) { IList <int> vertIndex = new List <int>(); if (IsMesh) { MeshTriangle f = m_MeshGeom.get_Triangle(ef); vertIndex = new List <int>(3) { (int)f.get_Index(0), (int)f.get_Index(1), (int)f.get_Index(2) }; } else { TriangleInShellComponent f = m_Geom.GetTriangle(ef); vertIndex = new List <int>(3) { f.VertexIndex0, f.VertexIndex1, f.VertexIndex2 }; } IndexFace intF = new IndexFace(vertIndex, ref m_MeshVertices); m_FacesCollDict.Add(faceIdxOffset++, intF); // Keep faces in a dictionary and assigns ID List <int> fIDList; if (!faceSortedByNormal.TryGetValue(intF.Normal, out fIDList)) { fIDList = new List <int>(1) { ef }; faceSortedByNormal.Add(intF.Normal, fIDList); } else if (!fIDList.Contains(ef)) { fIDList.Add(ef); } } foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal) { List <int> mergedFaceList = null; if (fListDict.Value.Count > 1) { if (!ignoreMerge) { TryMergeFaces(fListDict.Value, out mergedFaceList); } else { // keep original face list mergedFaceList = fListDict.Value; } if (mergedFaceList != null && mergedFaceList.Count > 0) { // insert only new face indexes as the mergedlist from different vertices can be duplicated foreach (int fIdx in mergedFaceList) { if (!m_MergedFaceSet.Contains(fIdx)) { m_MergedFaceSet.Add(fIdx); } } } } else if (!m_MergedFaceSet.Contains(fListDict.Value[0])) { m_MergedFaceSet.Add(fListDict.Value[0]); // No pair face, add it into the mergedList } } // Remove unused vertices CleanVerticesAndUpdateIndexes(); int eulerAfter = ignoreMerge ? 0 : CalculateEulerCharacteristic(); if (eulerBefore != eulerAfter) { throw new InvalidOperationException(); // Coplanar merge broke the mesh in some way, so we need to fall back to exporting a triangular mesh } }
/// <summary> /// Combine coplanar triangles from the faceted body if they share the edge. From this process, polygonal faces (with or without holes) will be created /// </summary> public void SimplifyAndMergeFaces() { int noTriangle = (IsMesh)? _meshGeom.NumTriangles : _geom.TriangleCount; int noVertices = (IsMesh)? _meshGeom.Vertices.Count : _geom.VertexCount; IEqualityComparer <XYZ> normalComparer = new vectorCompare(); Dictionary <XYZ, List <int> > faceSortedByNormal = new Dictionary <XYZ, List <int> >(normalComparer); for (int ef = 0; ef < noTriangle; ++ef) { IList <int> vertIndex = new List <int>(); if (IsMesh) { MeshTriangle f = _meshGeom.get_Triangle(ef); vertIndex.Add((int)f.get_Index(0)); vertIndex.Add((int)f.get_Index(1)); vertIndex.Add((int)f.get_Index(2)); } else { TriangleInShellComponent f = _geom.GetTriangle(ef); vertIndex.Add(f.VertexIndex0); vertIndex.Add(f.VertexIndex1); vertIndex.Add(f.VertexIndex2); } IndexFace intF = new IndexFace(vertIndex); facesColl.Add(ef, intF); // Keep faces in a dictionary and assigns ID List <int> fIDList; if (!faceSortedByNormal.TryGetValue(intF.normal, out fIDList)) { fIDList = new List <int>(); fIDList.Add(ef); faceSortedByNormal.Add(intF.normal, fIDList); } else { if (!fIDList.Contains(ef)) { fIDList.Add(ef); } } } foreach (KeyValuePair <XYZ, List <int> > fListDict in faceSortedByNormal) { List <int> mergedFaceList = null; if (fListDict.Value.Count > 1) { TryMergeFaces(fListDict.Value, out mergedFaceList); if (mergedFaceList != null && mergedFaceList.Count > 0) { // insert only new face indexes as the mergedlist from different vertices can be duplicated foreach (int fIdx in mergedFaceList) { if (!_mergedFaceList.Contains(fIdx)) { _mergedFaceList.Add(fIdx); } } } } else if (!_mergedFaceList.Contains(fListDict.Value[0])) { _mergedFaceList.Add(fListDict.Value[0]); // No pair face, add it into the mergedList } } }
private byte[] GeometryToOBJ(Document doc, GeometryElement geometry) { int indexOffset = 0; int nextIndexOffset = 0; int part = 0; StringBuilder fullObjectSB = new StringBuilder(); fullObjectSB.Append($"o StreamVRExportedObject\n"); foreach (GeometryObject obj in geometry) { Solid solid = obj as Solid; if (solid == null) { continue; } if (solid.GraphicsStyleId != null) { GraphicsStyle gs = doc.GetElement(solid.GraphicsStyleId) as GraphicsStyle; bool isLightSource = gs?.GraphicsStyleCategory?.Name == "Light Source"; if (isLightSource) { XYZ centroid = solid.GetBoundingBox()?.Transform?.Origin; if (centroid != null) { fullObjectSB.Append($"ls {centroid.X} {centroid.Z} {centroid.Y}\n"); } continue; } } fullObjectSB.Append($"g GeometryPart{part}\n"); bool addedMaterial = false; IEnumerable <Face> faces = solid.Faces.Cast <Face>(); foreach (Face f in faces) { if (!addedMaterial && f.MaterialElementId != null) { fullObjectSB.Append($"svrm {f.MaterialElementId.ToString()}\n"); addedMaterial = true; } Mesh m = f.Triangulate(); foreach (XYZ v in m.Vertices) { fullObjectSB.Append($"v {v.X} {v.Z} {v.Y}\n"); } for (int i = 0; i < m.NumTriangles; i++) { MeshTriangle mt = m.get_Triangle(i); int f1 = (int)mt.get_Index(0) + indexOffset; int f2 = (int)mt.get_Index(1) + indexOffset; int f3 = (int)mt.get_Index(2) + indexOffset; fullObjectSB.Append($"f {f1 + 1} {f2 + 1} {f3 + 1}\n"); if (f1 > nextIndexOffset) { nextIndexOffset = f1; } if (f2 > nextIndexOffset) { nextIndexOffset = f2; } if (f3 > nextIndexOffset) { nextIndexOffset = f3; } } indexOffset = nextIndexOffset + 1; } part++; } byte[] file_bytes = Encoding.ASCII.GetBytes(fullObjectSB.ToString()); return(file_bytes); }
private bool GetFaceGeometry(Face face, out List <int> faceIndices, out List <int> faceVertices, out List <double> faceNormals, out XYZ centerPoint) { bool result = false; faceIndices = new List <int>(); faceVertices = new List <int>(); faceNormals = new List <double>(); centerPoint = new XYZ(); try { BoundingBoxUV bb = face.GetBoundingBox(); UV midUV = new UV((bb.Max.U - bb.Min.U) / 2, (bb.Max.V - bb.Min.V) / 2); centerPoint = face.Evaluate(midUV); Mesh mesh = m_face.Triangulate(); int nTriangles = mesh.NumTriangles; IList <XYZ> vertices = mesh.Vertices; int nVertices = vertices.Count; List <int> vertexCoordsMm = new List <int>(3 * nVertices); foreach (XYZ v in vertices) { vertexCoordsMm.Add(ConverterUtil.FootToMm(v.X)); vertexCoordsMm.Add(ConverterUtil.FootToMm(v.Y)); vertexCoordsMm.Add(ConverterUtil.FootToMm(v.Z)); } int[] triangleIndices = new int[3]; XYZ[] triangleCorners = new XYZ[3]; for (int i = 0; i < nTriangles; ++i) { faceIndices.Add(2); //triangle with material MeshTriangle triangle = mesh.get_Triangle(i); for (int j = 0; j < 3; ++j) { int k = (int)triangle.get_Index(j); triangleIndices[j] = k; triangleCorners[j] = vertices[k]; } // Calculate constant triangle facet normal. XYZ v = triangleCorners[1] - triangleCorners[0]; XYZ w = triangleCorners[2] - triangleCorners[0]; XYZ triangleNormal = v .CrossProduct(w) .Normalize(); for (int j = 0; j < 3; ++j) { int nFaceVertices = faceVertices.Count; faceIndices.Add(nFaceVertices / 3); int i3 = triangleIndices[j] * 3; // Rotate the X, Y and Z directions, // since the Z direction points upward // in Revit as opposed to sideways or // outwards or forwards in WebGL. faceVertices.Add(vertexCoordsMm[i3 + 1]); faceVertices.Add(vertexCoordsMm[i3 + 2]); faceVertices.Add(vertexCoordsMm[i3]); UV uv = m_face.Project( triangleCorners[j]).UVPoint; XYZ normal = m_face.ComputeNormal(uv); faceNormals.Add(normal.Y); faceNormals.Add(normal.Z); faceNormals.Add(normal.X); } faceIndices.Add(0); } result = true; } catch (Exception ex) { string message = "Cannot get face geometry: " + ex.Message; } return(result); }
static private bool ExportFaces(List <Face> faces, ref Va3cContainer.Va3cGeometry faceGroupGeometry, out bool bHasUvs) { bHasUvs = true; foreach (Face face in faces) { Mesh mesh = face.Triangulate(ExportEventHandler.Settings.TriangulateDetailLevel); Surface surface = face.GetSurface(); if (mesh == null || surface == null) { continue; } int currentPointNum = faceGroupGeometry.data.points; int currentTriNum = faceGroupGeometry.data.triangles; IList <XYZ> points = mesh.Vertices; int pointNum = points.Count; int triNum = mesh.NumTriangles; faceGroupGeometry.data.points += pointNum; faceGroupGeometry.data.triangles += triNum; foreach (XYZ point in points) { faceGroupGeometry.data.vertices.Add((float)point.X); faceGroupGeometry.data.vertices.Add((float)point.Y); faceGroupGeometry.data.vertices.Add((float)point.Z); try { if (bHasUvs) { UV uv; double distance; surface.Project(point, out uv, out distance); faceGroupGeometry.data.uvs.Add((float)uv.U); faceGroupGeometry.data.uvs.Add((float)uv.V); XYZ normal = face.ComputeNormal(uv); faceGroupGeometry.data.normals.Add((float)normal.X); faceGroupGeometry.data.normals.Add((float)normal.Y); faceGroupGeometry.data.normals.Add((float)normal.Z); } else { faceGroupGeometry.data.uvs.Add(0.0f); faceGroupGeometry.data.uvs.Add(0.0f); faceGroupGeometry.data.normals.Add(0.0f); faceGroupGeometry.data.normals.Add(0.0f); faceGroupGeometry.data.normals.Add(1.0f); } } catch (System.Exception ex) { bHasUvs = false; faceGroupGeometry.data.uvs.Add(0.0f); faceGroupGeometry.data.uvs.Add(0.0f); faceGroupGeometry.data.normals.Add(0.0f); faceGroupGeometry.data.normals.Add(0.0f); faceGroupGeometry.data.normals.Add(1.0f); } } for (int i = 0; i < triNum; ++i) { MeshTriangle meshTri = mesh.get_Triangle(i); int index0 = (int)meshTri.get_Index(0) + currentPointNum; int index1 = (int)meshTri.get_Index(1) + currentPointNum; int index2 = (int)meshTri.get_Index(2) + currentPointNum; faceGroupGeometry.data.indices.Add(index0); faceGroupGeometry.data.indices.Add(index1); faceGroupGeometry.data.indices.Add(index2); } } if (faceGroupGeometry.data.points > 0 && faceGroupGeometry.data.triangles > 0) { return(true); } return(false); }
// Create and populate a pair of vertex and index buffers. Also update parameters associated with the format of the vertices. private void ProcessFaces(RenderingPassBufferStorage bufferStorage) { List <MeshInfo> meshes = bufferStorage.Meshes; if (meshes.Count == 0) { return; } List <int> numVerticesInMeshesBefore = new List <int>(); bool useNormals = this.Inputs.EnableFaceNormal; // Vertex attributes are stored sequentially in vertex buffers. The attributes can include position, normal vector, and color. // All vertices within a vertex buffer must have the same format. Possible formats are enumerated by VertexFormatBits. // Vertex format also determines the type of rendering effect that can be used with the vertex buffer. In this sample, // the color is always encoded in the vertex attributes. bufferStorage.FormatBits = useNormals ? VertexFormatBits.PositionNormalColored : VertexFormatBits.PositionColored; // The format of the vertices determines the size of the vertex buffer. int vertexBufferSizeInFloats = (useNormals ? VertexPositionNormalColored.GetSizeInFloats() : VertexPositionColored.GetSizeInFloats()) * bufferStorage.VertexBufferCount; numVerticesInMeshesBefore.Add(0); bufferStorage.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); bufferStorage.VertexBuffer.Map(vertexBufferSizeInFloats); int numMeshes = meshes.Count; if (useNormals) { // A VertexStream is used to write data into a VertexBuffer. VertexStreamPositionNormalColored vertexStream = bufferStorage.VertexBuffer.GetVertexStreamPositionNormalColored(); for (int i = 0; i < numMeshes; i++) { var meshInfo = meshes[i]; Mesh mesh = meshInfo.Mesh; foreach (XYZ vertex in mesh.Vertices) { vertexStream.AddVertex(new VertexPositionNormalColored(vertex + meshInfo.Offset, meshInfo.Normal, meshInfo.ColorWithTransparency)); } numVerticesInMeshesBefore.Add(numVerticesInMeshesBefore.Last() + mesh.Vertices.Count); } } else { // A VertexStream is used to write data into a VertexBuffer. VertexStreamPositionColored vertexStream = bufferStorage.VertexBuffer.GetVertexStreamPositionColored(); for (int i = 0; i < numMeshes; i++) { var meshInfo = meshes[i]; Mesh mesh = meshInfo.Mesh; // make the color of all faces white in HLR ColorWithTransparency color = meshInfo.ColorWithTransparency; foreach (XYZ vertex in mesh.Vertices) { vertexStream.AddVertex(new VertexPositionColored(vertex + meshInfo.Offset, color)); } numVerticesInMeshesBefore.Add(numVerticesInMeshesBefore.Last() + mesh.Vertices.Count); } } bufferStorage.VertexBuffer.Unmap(); // Primitives are specified using a pair of vertex and index buffers. An index buffer contains a sequence of indices into // the associated vertex buffer, each index referencing a particular vertex. int meshNumber = 0; bufferStorage.IndexBufferCount = bufferStorage.PrimitiveCount * IndexTriangle.GetSizeInShortInts(); int indexBufferSizeInShortInts = 1 * bufferStorage.IndexBufferCount; bufferStorage.IndexBuffer = new IndexBuffer(indexBufferSizeInShortInts); bufferStorage.IndexBuffer.Map(indexBufferSizeInShortInts); { // An IndexStream is used to write data into an IndexBuffer. IndexStreamTriangle indexStream = bufferStorage.IndexBuffer.GetIndexStreamTriangle(); foreach (MeshInfo meshInfo in meshes) { Mesh mesh = meshInfo.Mesh; int startIndex = numVerticesInMeshesBefore[meshNumber]; for (int i = 0; i < mesh.NumTriangles; i++) { MeshTriangle mt = mesh.get_Triangle(i); // Add three indices that define a triangle. indexStream.AddTriangle(new IndexTriangle((int)(startIndex + mt.get_Index(0)), (int)(startIndex + mt.get_Index(1)), (int)(startIndex + mt.get_Index(2)))); } meshNumber++; } } bufferStorage.IndexBuffer.Unmap(); // VertexFormat is a specification of the data that is associated with a vertex (e.g., position). bufferStorage.VertexFormat = new VertexFormat(bufferStorage.FormatBits); // Effect instance is a specification of the appearance of geometry. For example, it may be used to specify color, if there is no color information provided with the vertices. bufferStorage.EffectInstance = new EffectInstance(bufferStorage.FormatBits); }
int getGeometry(Element e) { bool RetainCurvedSurfaceFacets = true; Options opt = new Options(); GeometryElement geo = e.get_Geometry(opt); if (geo == null) { return(0); } List <int> faceIndices = new List <int>(); List <double> faceVertices = new List <double>(); List <double> faceNormals = new List <double>(); int[] triangleIndices = new int[3]; XYZ[] triangleCorners = new XYZ[3]; foreach (GeometryObject obj in geo) { Solid solid = obj as Solid; if (solid != null && 0 < solid.Faces.Size) { faceIndices.Clear(); faceVertices.Clear(); faceNormals.Clear(); foreach (Face face in solid.Faces) { Mesh mesh = face.Triangulate(); int nTriangles = mesh.NumTriangles; IList <XYZ> vertices = mesh.Vertices; int nVertices = vertices.Count; List <double> vertexCoordsMm = new List <double>(3 * nVertices); // A vertex may be reused several times with // different normals for different faces, so // we cannot precalculate normals per vertex. //List<double> normals = new List<double>( 3 * nVertices ); foreach (XYZ v in vertices) { // Translate the entire element geometry // to the bounding box midpoint and scale // to metric millimetres. XYZ p = v; vertexCoordsMm.Add(p.X * _footToMm); vertexCoordsMm.Add(p.Y * _footToMm); vertexCoordsMm.Add(p.Z * _footToMm); } for (int i = 0; i < nTriangles; ++i) { MeshTriangle triangle = mesh.get_Triangle(i); for (int j = 0; j < 3; ++j) { int k = (int)triangle.get_Index(j); triangleIndices[j] = k; triangleCorners[j] = vertices[k]; } // Calculate constant triangle facet normal. XYZ v = triangleCorners[1] - triangleCorners[0]; XYZ w = triangleCorners[2] - triangleCorners[0]; XYZ triangleNormal = v .CrossProduct(w) .Normalize(); for (int j = 0; j < 3; ++j) { int nFaceVertices = faceVertices.Count; //Debug.Assert(nFaceVertices.Equals(faceNormals.Count), // "expected equal number of face vertex and normal coordinates"); faceIndices.Add(nFaceVertices / 3); int i3 = triangleIndices[j] * 3; // Rotate the X, Y and Z directions, // since the Z direction points upward // in Revit as opposed to sideways or // outwards or forwards in WebGL. faceVertices.Add(vertexCoordsMm[i3 + 1]); faceVertices.Add(vertexCoordsMm[i3 + 2]); faceVertices.Add(vertexCoordsMm[i3]); if (RetainCurvedSurfaceFacets) { faceNormals.Add(triangleNormal.Y); faceNormals.Add(triangleNormal.Z); faceNormals.Add(triangleNormal.X); } else { UV uv = face.Project( triangleCorners[j]).UVPoint; XYZ normal = face.ComputeNormal(uv); faceNormals.Add(normal.Y); faceNormals.Add(normal.Z); faceNormals.Add(normal.X); } } } } // Scale the vertices to a [-1,1] cube // centered around the origin. Translation // to the origin was already performed above. //double scale = 2.0 / FootToMm(MaxCoord(vsize)); //Debug.Print("position: [{0}],", // string.Join(", ", // faceVertices.ConvertAll<string>( // i => (i * scale).ToString("0.##")))); //Debug.Print("normal: [{0}],", // string.Join(", ", // faceNormals.ConvertAll<string>( // f => f.ToString("0.##")))); //Debug.Print("indices: [{0}],", // string.Join(", ", // faceIndices.ConvertAll<string>( // i => i.ToString()))); } } return(faceIndices.Count); }
public static string GetFacesAndEdges(Element e, bool startFromZero) { String xx = ""; bool RetainCurvedSurfaceFacets = true; // Get element geometry Options opt = new Options(); GeometryElement geomElem = e.get_Geometry(opt); int[] triangleIndices = new int[3]; XYZ[] triangleCorners = new XYZ[3]; List <string> faceVertices = new List <string>(); List <string> faceNormals = new List <string>(); List <string> faceElements = new List <string>(); //// First we need to get transformation //LocationCurve lc = e.Location as LocationCurve; //// Get curve starting- and endpoint //XYZ startingPoint = lc.Curve.GetEndPoint(0); //XYZ endPoint = lc.Curve.GetEndPoint(1); foreach (GeometryObject geomObj in geomElem) { Solid geomSolid = geomObj as Solid; if (null != geomSolid) { faceVertices.Clear(); faceNormals.Clear(); faceElements.Clear(); foreach (Face face in geomSolid.Faces) { // Triangulate face to get mesh Mesh mesh = face.Triangulate(); int nTriangles = mesh.NumTriangles; IList <XYZ> vertices = mesh.Vertices; int nVertices = vertices.Count; List <int> vertexCoordsMm = new List <int>(3 * nVertices); // A vertex may be reused several times with // different normals for different faces, so // we cannot precalculate normals per vertex. // List<double> normals = new List<double>( 3 * nVertices ); // Extract vertices foreach (XYZ v in vertices) { vertexCoordsMm.Add(ConvertLengthToMM(v.X)); vertexCoordsMm.Add(ConvertLengthToMM(v.Y)); vertexCoordsMm.Add(ConvertLengthToMM(v.Z)); } // Loop over triangles for (int i = 0; i < nTriangles; ++i) { MeshTriangle triangle = mesh.get_Triangle(i); for (int j = 0; j < 3; ++j) { int k = (int)triangle.get_Index(j); triangleIndices[j] = k; triangleCorners[j] = vertices[k]; } // Calculate constant triangle facet normal. XYZ v = triangleCorners[1] - triangleCorners[0]; XYZ w = triangleCorners[2] - triangleCorners[0]; XYZ triangleNormal = v .CrossProduct(w) .Normalize(); // List to store vertice indexes in the form: [v1//vn1 v2//vn2 v3//vn3] List <string> vertIndexes = new List <string>(); for (int j = 0; j < 3; ++j) { int nFaceVertices = faceVertices.Count; //if(nFaceVertices != faceNormals.Count) //{ // xx += "expected equal number of face vertex and normal coordinates\n"; //} int i3 = triangleIndices[j] * 3; // Rotate the X, Y and Z directions, // since the Z direction points upward // in Revit as opposed to sideways or // outwards or forwards in WebGL. string vStr = $"v {vertexCoordsMm[i3]} {vertexCoordsMm[i3 + 1]} {vertexCoordsMm[i3 + 2]}"; // get vertice index int vidx = faceVertices.IndexOf(vStr); // add if not exist if (vidx == -1) { faceVertices.Add(vStr); vidx = faceVertices.Count - 1; } string vnStr = ""; if (RetainCurvedSurfaceFacets) { vnStr = $"vn {Math.Round(triangleNormal.X, 2)} {Math.Round(triangleNormal.Y, 2)} {Math.Round(triangleNormal.Z, 2)}"; } else { UV uv = face.Project( triangleCorners[j]).UVPoint; XYZ normal = face.ComputeNormal(uv); vnStr = $"vn {Math.Round(normal.X, 2)} {Math.Round(normal.Y, 2)} {Math.Round(normal.Z, 2)}"; } // get face normal index int vnidx = faceNormals.IndexOf(vnStr); // add if not in list if (vnidx == -1) { faceNormals.Add(vnStr); vnidx = faceNormals.Count - 1; } // add indexes to list vertIndexes.Add($"{vidx+1+objStartIndex}/{vnidx+1 + objStartNormal}"); } // Store face elements string fStr = $"f {vertIndexes[0]} {vertIndexes[1]} {vertIndexes[2]}"; faceElements.Add(fStr); } } // Write to string xx += String.Join("\n\t", faceVertices) + "\n\t"; xx += String.Join("\n\t", faceNormals) + "\n\t"; xx += String.Join("\n\t", faceElements) + "\n\t"; if (!startFromZero) { objStartIndex += faceVertices.Count; objStartNormal += faceNormals.Count; } } Mesh geomMesh = geomObj as Mesh; Curve geomCurve = geomObj as Curve; Point geomPoint = geomObj as Point; PolyLine geomPoly = geomObj as PolyLine; GeometryInstance geomInst = geomObj as GeometryInstance; if (null != geomInst) { GeometryElement geomElement = geomInst.GetInstanceGeometry(); foreach (GeometryObject geomObj1 in geomElement) { Solid geomSolid1 = geomObj1 as Solid; if (null != geomSolid1) { Console.Out.WriteLine("got element: " + geomSolid1.Faces); } } } } return(xx); }
private void ExportSolid2(Solid solid) { foreach (Face face in solid.Faces) { if (!(face.Equals(null))) { Mesh mesh = face.Triangulate(userSetting.LevelOfDetail / 15.0); if (!(mesh.Equals(null)) && !mesh.Visibility.Equals(Visibility.Invisible)) { ModelGeometry exportedGeometry = new ModelGeometry(); // 设置坐标系 exportedGeometry.Transform = transformationStack.Peek(); exportedGeometry.Points = new List <XYZ>(mesh.Vertices); exportedGeometry.Indices = new List <int>(mesh.NumTriangles * 3); //指示此转换是否为保形的布尔值。指示此转换是否产生反射的布尔值。反射变换会更改坐标系的惯用性。 if (exportedGeometry.Transform.IsConformal && exportedGeometry.Transform.HasReflection) { for (int i = 0; i < mesh.NumTriangles; i++) { MeshTriangle meshTriangle = mesh.get_Triangle(i); exportedGeometry.Indices.Add((int)meshTriangle.get_Index(0)); exportedGeometry.Indices.Add((int)meshTriangle.get_Index(2)); exportedGeometry.Indices.Add((int)meshTriangle.get_Index(1)); } } else { for (int j = 0; j < mesh.NumTriangles; j++) { MeshTriangle meshTriangle2 = mesh.get_Triangle(j); exportedGeometry.Indices.Add((int)meshTriangle2.get_Index(0)); exportedGeometry.Indices.Add((int)meshTriangle2.get_Index(1)); exportedGeometry.Indices.Add((int)meshTriangle2.get_Index(2)); } } ModelGeometry TGeometry = exportedGeometry; //计算模型法线 TGeometry.CalculateNormals(TGeometry.Transform.IsConformal && exportedGeometry.Transform.HasReflection); exportedGeometry.CalculateUVs(true, false); //判断是不是反正双面实体 if (face.IsTwoSided) { exportedGeometry.MakeDoubleSided(); } //将当前元素面的材质ID放入元组中(文档,元素材质ID) ElementId materialElementId = face.MaterialElementId; Tuple <Document, ElementId> tuple = new Tuple <Document, ElementId>(documentStack.Peek(), materialElementId); //加入元组索引 ChangeCurrentMaterial(tuple); //用元组索引将模型加入到模型数据池 documentAndMaterialIdToGeometries[tuple].Add(exportedGeometry); } } } }