Beispiel #1
0
 /// <summary>
 /// If two mesh points have the same (X, Y, Z) coords, then they get combined into one.
 /// </summary>
 public static Mesh JoinVertices(Mesh mesh)
 {
     Dictionary<Vector3, int> pointIxMap = new Dictionary<Vector3, int>();
     List<Vector3> points = new List<Vector3>();
     List<Vector3> norms = new List<Vector3>();
     for (int i = 0; i < mesh.points.Length; i++) {
         if (!pointIxMap.ContainsKey(mesh.points[i])) {
             points.Add(mesh.points[i]);
             norms.Add(mesh.normals[i]);
             pointIxMap.Add(mesh.points[i], points.Count-1);
         }
     }
     Logger.info("mesh has " + mesh.points.Length + " verts, "+
         "joined " + (mesh.points.Length - pointIxMap.Count) + " dupes");
     List<Mesh.Triangle> tris = new List<Mesh.Triangle>();
     for (int i = 0; i < mesh.triangles.Length; i++) {
         var oldTri = mesh.triangles[i];
         var tri = new Mesh.Triangle(
             pointIxMap[mesh.points[oldTri.vertexA]],
             pointIxMap[mesh.points[oldTri.vertexB]],
             pointIxMap[mesh.points[oldTri.vertexC]]);
         if (tri.vertexA != tri.vertexB && tri.vertexA != tri.vertexC && tri.vertexB != tri.vertexC) {
             tris.Add(tri);
         }
     }
     return new Mesh(points.ToArray(), norms.ToArray(), tris.ToArray());
 }
Beispiel #2
0
        public void Parse(string filename)
        {
            var verts = new List<Vector3>();
            var tris = new List<Mesh.Triangle>();
            var norms = new List<Vector3>();
            //deduplicate the verts...
            var vertIxs = new Dictionary<Vector3, int>();

            // parse the file with a simple state machine
            const int INIT = 1, SOLID = 2, FACET = 3, LOOP = 4, ENDFACET=5;
            var triIxs = new List<int>();
            var norm = new Vector3();
            int state = INIT;
            StreamReader reader = new StreamReader(filename);
            String line;
            for (int lineNum = 1; (line = reader.ReadLine()) != null; lineNum++) {
                string[] parts = line.Trim().ToLower().Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length == 0) {
                    // skip empty lines
                    continue;
                }
                if (parts[0] == "solid") {
                    Assert(state == INIT, filename, lineNum);
                    Logger.info("reading stl solid " + String.Join(" ", parts, 1, parts.Length - 1));
                    state = SOLID;
                } else if (parts[0] == "facet") {
                    Assert(state == SOLID, filename, lineNum);
                    Assert(parts[1] == "normal", filename, lineNum);
                    norm = ReadVector(parts, 2, filename, lineNum);
                    state = FACET;
                } else if (parts[0] == "outer") {
                    Assert(state == FACET, filename, lineNum);
                    Assert(parts[1] == "loop", filename, lineNum);
                    triIxs.Clear();
                    state = LOOP;
                } else if (parts[0] == "vertex") {
                    Assert(state == LOOP, filename, lineNum);
                    var vertex = ReadVector(parts, 1, filename, lineNum);
                    if (vertIxs.ContainsKey(vertex)) {
                        int vertIx = vertIxs[vertex];
                        triIxs.Add(vertIx);
                        norms[vertIx] += norm;
                    } else {
                        int vertIx = verts.Count;
                        vertIxs.Add(vertex, vertIx);
                        triIxs.Add(vertIx);
                        verts.Add(vertex);
                        norms.Add(norm);
                    }
                } else if (parts[0] == "endloop") {
                    Assert(state == LOOP, filename, lineNum);
                    //every face must be a triangle
                    Assert(triIxs.Count == 3, filename, lineNum);
                    var tri = new Mesh.Triangle(triIxs[0], triIxs[1], triIxs[2]);
                    tri.normal = norm;
                    tris.Add(tri);
                    state = ENDFACET;
                } else if (parts[0] == "endfacet") {
                    Assert(state == ENDFACET, filename, lineNum);
                    state = SOLID;
                } else if (parts[0] == "endsolid") {
                    Assert(state == SOLID, filename, lineNum);
                    state = INIT;
                }
            }

            // get all the averaged vertex normals and set them to unit length
            for(int i = 0; i < norms.Count; i++){
                float len = norms[i].Length;
                if (len == 0) {
                    norms[i] = new Vector3(1, 0, 0);
                } else {
                    norms[i] /= len;
                }
            }

            // finally, return a mesh
            mesh = new Mesh(verts.ToArray(), norms.ToArray(), tris.ToArray());
        }
Beispiel #3
0
        public static Mesh MirrorAndCombine(Mesh mesh, Vector3 axis)
        {
            /* if the current mesh has n points, we'll have up to 2n-1 points in the mirrored and combined one */
            var points = new List<Vector3>();
            points.AddRange(mesh.points);
            /* find the point or points we're going to mirror around--the ones closest in the 'axis' direction
             * (in other words, find the mirror plane) */
            var minDot = mesh.points.Min(point => Vector3.Dot(point, axis));
            /* if points are this close or closer to the mirror plane, they will not be duplicated--they will simply be shared by additional triangles */
            var epsilon = 0.002f;
            /* create a dictionary mapping point indices in the original mesh to the corresponding mirrored points */
            var pointIndexMap = new Dictionary<int, int>();

            for (int i = 0; i < mesh.points.Length; i++) {
                var distance = Vector3.Dot(mesh.points[i], axis) - minDot;
                if (distance < epsilon) {
                    /* point lies on mirror plane */
                    pointIndexMap.Add(i, i);
                } else {
                    /* point is not on mirror plane; create its mirror image as a new point */
                    pointIndexMap.Add(i, points.Count);
                    var mirrorPoint = mesh.points[i] - 2 * distance * axis;
                    points.Add(mirrorPoint);
                }
            }

            /* normals correspond to points, but may need to be mirrored.
             * if a point is on the mirror plane, make sure the corresponding normal is in the mirror plane
             */
            var normals = new Vector3[points.Count];
            for (int i = 0; i < mesh.points.Length; i++) {
                if (pointIndexMap[i] == i) {
                    normals[i] = mesh.normals[i] - Vector3.Dot(mesh.normals[i], axis) * axis;
                    normals[i].Normalize();
                } else {
                    normals[i] = mesh.normals[i];
                    normals[pointIndexMap[i]] = mesh.normals[i] - 2 * Vector3.Dot(mesh.normals[i], axis) * axis;
                }
            }

            /* triangles are simply duplicated--each triangle gets a mirror image */
            var triangles = new Mesh.Triangle[mesh.triangles.Length * 2];
            for (int i = 0; i < mesh.triangles.Length; i++) {
                triangles[i] = mesh.triangles[i];
            }
            for (int i = 0; i < mesh.triangles.Length; i++) {
                var triangle = mesh.triangles[i];
                triangles[mesh.triangles.Length + i] = new Mesh.Triangle(
                    pointIndexMap[triangle.vertexA],
                    pointIndexMap[triangle.vertexB],
                    pointIndexMap[triangle.vertexC]
                );
            }

            return new Mesh(points.ToArray(), normals, triangles);
        }
Beispiel #4
0
        public static void Split(Mesh input, IVolume volume, 
            out Mesh output, out bool[] trisInside)
        {
            /**
             *
             *  for triangle in input
             *      if triangle is entirely inside or outside
             *         add to corresponding mesh
             *      else split triangle into 4 pieces.
             *         three go in one mesh, one goes into the other
             *
             * Splitting a triangle, Before:
             *
             *                *    vA
             *               / \
             * boundary --- /---\ ---
             *             /     \
             *       vB1  *-------*  vB2
             *
             *
             * After:
             *
             *                * vA
             *         vMid1 / \ vMid2
             * boundary --- *---* ---
             *             / \ / \
             *       vB1  *---*---*  vB2
             *              vMidB
             *
             **/

            int np = input.points.Length, nt = input.triangles.Length;
            List<Vector3> points = input.points.ToList();
            List<Vector3> norms = input.normals.ToList();
            List<Mesh.Triangle> tris = new List<Mesh.Triangle>();
            List<bool> trisIn = new List<bool>();
            for(int i = 0; i < nt; i++){
                var tri = input.triangles[i];
                Vector3 vA = input.points[tri.vertexA];
                Vector3 vB = input.points[tri.vertexB];
                Vector3 vC = input.points[tri.vertexC];
                int ncontains =
                    (volume.Contains(vA) ? 1 : 0) +
                    (volume.Contains(vB) ? 1 : 0) +
                    (volume.Contains(vC) ? 1 : 0);
                if (ncontains==3) {
                    tris.Add(tri);
                    trisIn.Add(true);
                } else if (ncontains==0){
                    tris.Add(tri);
                    trisIn.Add(false);
                } else {
                    // see comment above for explanation
                    Debug.Assert(ncontains == 1 || ncontains == 2);
                    bool containsA = (ncontains==1);
                    int ixA, ixB1, ixB2;
                    if (volume.Contains(vA) == containsA) {
                        ixA = tri.vertexA;
                        ixB1 = tri.vertexB;
                        ixB2 = tri.vertexC;
                    } else if (volume.Contains(vB) == containsA) {
                        ixA = tri.vertexB;
                        ixB1 = tri.vertexA;
                        ixB2 = tri.vertexC;
                    } else {
                        Debug.Assert(volume.Contains(vC) == containsA);
                        ixA = tri.vertexC;
                        ixB1 = tri.vertexA;
                        ixB2 = tri.vertexB;
                    }
                    Vector3 vAO = input.points[ixA];
                    Vector3 vB1 = input.points[ixB1];
                    Vector3 vB2 = input.points[ixB2];
                    Vector3 vMid1 = FindBoundary(vAO, vB1, volume);
                    Vector3 vMid2 = FindBoundary(vAO, vB2, volume);
                    Vector3 vMidB = input.points[ixB1]*0.5f + input.points[ixB2]*0.5f;
                    points.Add(vMid1); points.Add(vMid2); points.Add(vMidB);
                    float b1 = (vMid1 - vAO).Length / ((vB1 - vAO).Length + float.Epsilon);
                    float b2 = (vMid2 - vAO).Length / ((vB2 - vAO).Length + float.Epsilon);
                    Debug.Assert(0 <= b1 && b1 <= 1 && 0 <= b2 && b2 <= 1);
                    Vector3 nMid1 = input.normals[ixA] * (1 - b1) + input.normals[ixB1] * b1;
                    Vector3 nMid2 = input.normals[ixA] * (1 - b2) + input.normals[ixB2] * b2;
                    Vector3 nMidB = input.normals[ixB1] * 0.5f + input.normals[ixB2] * 0.5f;
                    nMid1.Normalize(); nMid2.Normalize(); nMidB.Normalize();
                    norms.Add(nMid1); norms.Add(nMid2); norms.Add(nMidB);
                    //norms.Add(input.normals[ixA]); norms.Add(input.normals[ixA]); norms.Add(input.normals[ixA]);
                    var tri1 = new Mesh.Triangle(ixA, points.Count - 3, points.Count - 2);
                    var tri2 = new Mesh.Triangle(ixB1, points.Count - 3, points.Count - 1);
                    var tri3 = new Mesh.Triangle(points.Count - 3, points.Count - 2, points.Count - 1);
                    var tri4 = new Mesh.Triangle(ixB2, points.Count - 2, points.Count - 1);
                    tri1.normal = tri2.normal = tri3.normal = tri4.normal = tri.normal;
                    tris.Add(tri1); tris.Add(tri2); tris.Add(tri3); tris.Add(tri4);
                    trisIn.Add(containsA);
                    trisIn.Add(!containsA);
                    trisIn.Add(!containsA);
                    trisIn.Add(!containsA);
                }
            }

            // done
            Logger.info("split mesh, started with " + nt + " tris, added " + (tris.Count-nt));
            Debug.Assert(points.Count==norms.Count);
            output = new Mesh(points.ToArray(), norms.ToArray(), tris.ToArray());
            trisInside = trisIn.ToArray();
        }
Beispiel #5
0
        public void Parse(string filename)
        {
            /* load file */
            XmlDocument doc = new XmlDocument();
            doc.Load(filename);

            /* parse file */
            var points = new List<Vector3>();
            var normals = new List<Vector3>();
            var triangles = new List<Mesh.Triangle>();

            //TODO: disgusting hack.
            //XmlNodeList xmlSurfaces;   = doc.FirstChild.SelectNodes(
            //"//GeometricRepresentationSet/Representation/AssociatedXML/Rep");
            XmlNode rep;
            if (doc["XMLRepresentation"] != null &&
              doc["XMLRepresentation"]["Root"] != null
              && doc["XMLRepresentation"]["Root"]["Rep"] != null) {
                rep = doc["XMLRepresentation"]["Root"]["Rep"];
            } else if (doc["Model_3dxml"] != null
                && doc["Model_3dxml"]["GeometricRepresentationSet"] != null
                && doc["Model_3dxml"]["GeometricRepresentationSet"]["Representation"] != null
                && doc["Model_3dxml"]["GeometricRepresentationSet"]["Representation"]["AssociatedXML"] != null) {
                rep = doc["Model_3dxml"]["GeometricRepresentationSet"]["Representation"]["AssociatedXML"];
            } else {
                throw new ArgumentException("cannot parse " + filename);
            }

            foreach (XmlNode node in rep.ChildNodes) {
                if (node["VertexBuffer"] == null ||
                    node["VertexBuffer"]["Positions"] == null ||
                    node["VertexBuffer"]["Normals"] == null ||
                        node["Faces"]["Face"] == null) {
                    Logger.warn("skipping rep...");
                    continue;
                }

                //read verts
                var units = 0.001f; /* units in mm */
                var newPoints = Parse3DXMLVectors(node["VertexBuffer"]["Positions"].InnerText);
                for (int i = 0; i < newPoints.Length; i++)
                    newPoints[i] = newPoints[i] * units;
                var newNormals = Parse3DXMLVectors(node["VertexBuffer"]["Normals"].InnerText);
                for (int i = 0; i < newNormals.Length; i++)
                    newNormals[i] = -newNormals[i];
                int offset = points.Count;
                points.AddRange(newPoints);
                normals.AddRange(newNormals);

                //read triangles
                XmlAttributeCollection faceAttrs = node["Faces"]["Face"].Attributes;
                Logger.info("parsing face " + faceAttrs["id"].Value);
                if (faceAttrs["triangles"] != null) {
                    int[] vertIxs = Parse3DXMLInts(faceAttrs["triangles"].Value);
                    var triangleIxs = new HashSet<int>(vertIxs);
                    /*for (int i = 0; i < newPoints.Length - 2; i++)
                    {
                        if (triangleIxs.Contains(i))
                        {
                            triangles.Add(new MeshSprite.Triangle()
                            {
                                VertexA = offset + i,
                                VertexB = offset + i + 1,
                                VertexC = offset + i + 2
                            });
                        }
                    }*/
                    for (int i = 0; i < vertIxs.Length; i += 3) {
                        var tri = new Mesh.Triangle(
                            offset + vertIxs[i],
                            offset + vertIxs[i + 1],
                            offset + vertIxs[i + 2]
                        );
                        tri.normal = newNormals[vertIxs[i]];
                        triangles.Add(tri);
                    }
                } else if (faceAttrs["strips"] != null) {
                    String[] strips = faceAttrs["strips"].Value.Split(',');
                    foreach (String strip in strips) {
                        int[] vertIxs = Parse3DXMLInts(strip);
                        for (int i = 2; i < vertIxs.Length; i++) {
                            var tri = new Mesh.Triangle(
                                offset + vertIxs[i - ((i % 2) == 0 ? 1 : 2)],
                                offset + vertIxs[i - ((i % 2) == 0 ? 2 : 1)],
                                offset + vertIxs[i]
                            );
                            tri.normal = newNormals[vertIxs[i]];
                            triangles.Add(tri);
                        }
                    }
                } else {
                    throw new ArgumentException("Found an unsupported 3dxml mesh surface.\n" +
                        "Clean up your mesh in MeshLab. Triangles only.");
                }
            }

            /* create mesh sprite */
            mesh = new Mesh(points.ToArray(), normals.ToArray(), triangles.ToArray());
        }