public static Mesh Subdivide(Mesh mesh, int iterations, Options options = default(Options))
        {
            var m = new MeshX(mesh);

            //m.Trace();
            m = Subdivide(m, iterations, options);
            //Debug.Log("----->"); m.Trace();
            return(m.ConvertToMesh());
        }
 //
 private static void CheckForVertexKeys(MeshX mesh)
 {
     Debug.AssertFormat(mesh.submeshes.Length <= keyMaxSubmeshes,
                        "Subdivision may not work correctly for submesh count ({0}) > {1}", mesh.submeshes.Length, keyMaxSubmeshes);
     foreach (Submesh s in mesh.submeshes)
     {
         Debug.AssertFormat(s.faces.Length <= keyMaxSubmeshFaces,
                            "Subdivision may not work correctly for submesh face count ({0}) > {1}", s.faces.Length, keyMaxSubmeshFaces);
     }
 }
        public static MeshX Subdivide(MeshX mesh, int iterations, Options options = default(Options))
        {
            string name = mesh.name;

            // subdivide
            for (int i = 0; i < iterations; ++i)
            {
                mesh = Subdivide(mesh, options);
            }
            // rename
            if (iterations > 0)
            {
                mesh.name = name + "/s" + iterations;
            }
            return(mesh);
        }
        public MeshX(Mesh mesh)
        {
            // set vertex content
            content = GetMeshVertexContent(mesh);

            // init positions
            var poses = new List <Vector>();

            posIndices = new int[mesh.vertexCount];
            for (int i = 0; i < mesh.vertexCount; ++i)
            {
                Vector pos      = mesh.vertices[i];
                int    posIndex = poses.IndexOf(pos); //!!! use threshold?
                if (posIndex == -1)
                {
                    poses.Add(pos);
                    posIndex = poses.Count - 1;
                }
                posIndices[i] = posIndex;
            }
            positions = poses.ToArray();

            // init vertex content
            if (content.HasNormal())
            {
                normals           = mesh.normals;
                normalPerPosition = false;
            }
            if (content.HasTangent())
            {
                tangents = new Vector[mesh.vertexCount];
                for (int i = 0; i < mesh.vertexCount; ++i)
                {
                    tangents[i] = (Vector)mesh.tangents[i];
                }
            }
            if (content.HasUV0())
            {
                uvs0 = mesh.uv;
            }
            if (content.HasUV1())
            {
                uvs1 = mesh.uv2;
            }
            if (content.HasUV2())
            {
                uvs2 = mesh.uv3;
            }
            if (content.HasUV3())
            {
                uvs3 = mesh.uv4;
            }

            // set faces
            int submeshCount = mesh.subMeshCount;

            submeshes = new Submesh[submeshCount];
            for (int s = 0; s < submeshCount; ++s)
            {
                int[][] faces = MeshX.TrianglesToFaces(mesh.GetTriangles(s));
                submeshes[s] = new Submesh {
                    faces = faces
                };
            }

            // copy name
            this.name = mesh.name;
        }
        public static MeshX Subdivide(MeshX mesh, Options options)
        {
            if (!mesh.helpersInited)
            {
                mesh.InitHelpers();
            }
            if (!mesh.normalPerPosition)
            {
                mesh.MakeNormalPerPosition();
            }

            MeshX newMesh = new MeshX {
                name              = mesh.name + "/s",
                content           = mesh.content,
                normalPerPosition = true,
            };

            newMesh.StartBuilding();

            CheckForVertexKeys(mesh);
            var newVertices = new MeshVertices {
                mesh = newMesh
            };
            var edgeMidPositions = new Dictionary <VertexKey, Vertex>(); // position & normal


            // reserve indices for new control-points: they keep indices and we need no special keys for them.
            // later for control-points we set position & normal only: tangent & uv stay the same.
            for (int pi = 0; pi < mesh.positions.Count; ++pi)
            {
                newMesh.AddPosition(default(Vertex));
            }
            for (int vi = 0; vi < mesh.vertexCount; ++vi)
            {
                Vertex v = mesh.GetVertex(vi, maskVertex);
                newMesh.AddVertex(v, addPosition: false);
            }

            // add face-points
            // "for each face, a face point is created which is the average of all the points of the face."
            foreach (Face f in mesh.IterAllFaces())
            {
                Vertex[] vs = mesh.GetVertices(mesh.GetFaceVertexIndices(f));

                Vertex v = mesh.Average(vs);

                VertexKey keyF = GetFacePointKey(f);
                newVertices.AddVertices(v, Pair(v, keyF));
            }

            // add edge-points
            foreach (Edge e in mesh.IterAllEdges())
            {
                EdgeType type = mesh.GetEdgeType(e);
                if (type == EdgeType.back)
                {
                    continue;
                }

                if (type == EdgeType.boundary)
                {
                    // "for the edges that are on the border of a hole, the edge point is just the middle of the edge."

                    Vertex midE = mesh.Average(
                        mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
                        );

                    VertexKey keyE = GetEdgePointKey(e);

                    edgeMidPositions[keyE] = midE;

                    newVertices.AddVertices(midE, Pair(midE, keyE));
                }
                else if (type == EdgeType.solid)
                {
                    // "for each edge, an edge point is created which is the average between the center of the edge
                    //  and the center of the segment made with the face points of the two adjacent faces."

                    Edge n = mesh.GetNeighbor(e);

                    Vertex midE = mesh.Average(
                        mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
                        );

                    VertexKey keyE = GetEdgePointKey(e);

                    edgeMidPositions[keyE] = midE;

                    Vertex v = mesh.Average(
                        new[] {
                        midE,
                        newVertices.GetVertex(GetFacePointKey(e.face)),
                        newVertices.GetVertex(GetFacePointKey(n.face)),
                    },
                        weights: new[] { 2f, 1f, 1f }
                        );

                    newVertices.AddVertices(v, Pair(v, keyE));
                }
                else     // seam edge

                {
                    Edge n = mesh.GetNeighbor(e);

                    Vertex midE = mesh.Average(
                        mesh.GetVertices(mesh.GetEdgeVertexIndices(e))
                        );
                    Vertex midN = mesh.Average(
                        mesh.GetVertices(mesh.GetEdgeVertexIndices(n)),
                        maskVertex // pos & normal already got in midE
                        );

                    VertexKey keyE = GetEdgePointKey(e);
                    VertexKey keyN = GetEdgePointKey(n);

                    edgeMidPositions[keyE] = midE;

                    Vertex p = mesh.Average( // pos & normal only
                        new[] {
                        midE,
                        newVertices.GetVertex(GetFacePointKey(e.face), maskPosition),
                        newVertices.GetVertex(GetFacePointKey(n.face), maskPosition),
                    },
                        maskPosition,
                        new[] { 2f, 1f, 1f }
                        );

                    newVertices.AddVertices(p, Pair(midE, keyE), Pair(midN, keyN));
                }
            }

            // move control-points
            for (int pi = 0; pi < mesh.positions.Count; ++pi)
            {
                // count edges
                var edges      = new List <Edge>(); // edges outcoming from the position
                var boundaries = new List <Edge>();
                var front      = new List <Edge>();
                foreach (int vi in mesh.positionVertices[pi])
                {
                    foreach (Edge e in mesh.vertexEdges[vi])
                    {
                        edges.Add(e);
                        foreach (Edge edge in new[] { e, mesh.GetNextInFace(e, -1) })
                        {
                            EdgeType type = mesh.GetEdgeType(edge);
                            if (type == EdgeType.boundary)
                            {
                                boundaries.Add(edge);
                            }
                            else if (type != EdgeType.back)
                            {
                                front.Add(edge);
                            }
                        }
                    }
                }
                Debug.AssertFormat(boundaries.Count > 0 || (edges.Count == front.Count),
                                   "Counting edges error: boundaries: {0}, edges {1}, front: {2}", boundaries.Count, edges.Count, front.Count);

                Vertex controlPoint;

                if (boundaries.Count > 0)
                {
                    bool isCorner = edges.Count == 1;
                    if (options.boundaryInterpolation == Options.BoundaryInterpolation.fixBoundaries ||
                        options.boundaryInterpolation == Options.BoundaryInterpolation.fixCorners && isCorner)
                    {
                        controlPoint = mesh.GetPosition(pi); // keep same position
                    }
                    else
                    {
                        // "for the vertex points that are on the border of a hole, the new coordinates are calculated as follows:
                        //   1. in all the edges the point belongs to, only take in account the middles of the edges that are on the border of the hole
                        //   2. calculate the average between these points (on the hole boundary) and the old coordinates (also on the hole boundary)."
                        Vertex[] vs = new Vertex[boundaries.Count + 1];
                        vs[0] = mesh.GetPosition(pi);
                        for (int e = 0; e < boundaries.Count; ++e)
                        {
                            vs[e + 1] = edgeMidPositions[GetEdgePointKey(boundaries[e])];
                        }
                        controlPoint = mesh.Average(vs, maskPosition);
                    }
                }
                else
                {
                    // "for each vertex point, its coordinates are updated from (new_coords):
                    //   the old coordinates (P),
                    //   the average of the face points of the faces the point belongs to (F),
                    //   the average of the centers of edges the point belongs to (R),
                    //   how many faces a point belongs to (n), then use this formula:
                    //   (F + 2R + (n-3)P) / n"

                    // edge-midpoints
                    Vertex[] ms = new Vertex[front.Count];
                    for (int e = 0; e < front.Count; ++e)
                    {
                        ms[e] = edgeMidPositions[GetEdgePointKey(front[e])];
                    }
                    Vertex edgeMidAverage = mesh.Average(ms, maskPosition);
                    // face-points
                    Vertex[] fs = new Vertex[edges.Count];
                    for (int e = 0; e < edges.Count; ++e)
                    {
                        fs[e] = newVertices.GetVertex(GetFacePointKey(edges[e].face), maskPosition);
                    }
                    Vertex faceAverage = mesh.Average(fs, maskPosition);
                    // new control-point
                    controlPoint = mesh.Average(
                        new[] {
                        faceAverage,
                        edgeMidAverage,
                        mesh.GetPosition(pi)
                    },
                        maskPosition,
                        new[] { 1f, 2f, edges.Count - 3f }
                        );
                }

                // set moved control-point position to reserved index
                newMesh.SetPosition(pi, controlPoint);
            }

            // add 4 new quads per face
            //           eis[i]
            //   fis[i].----.----.fis[i+1]
            //         |         |
            // eis[i-1].  ci.    .
            //         |         |
            //         .____.____.
            newMesh.submeshes = new Submesh[mesh.submeshes.Length];
            for (int si = 0; si < mesh.submeshes.Length; ++si)
            {
                int[][] faces = mesh.submeshes[si].faces;
                // get new face count
                int faceCount = 0;
                foreach (int[] face in faces)
                {
                    faceCount += face.Length;
                }
                newMesh.submeshes[si].faces = new int[faceCount][];
                // fill faces
                int faceIndex = 0;
                for (int fi = 0; fi < faces.Length; ++fi)
                {
                    int[] fis       = faces[fi];
                    int   edgeCount = fis.Length; // 3 or 4
                    //
                    Face f = new Face {
                        submesh = si, index = fi
                    };
                    int ci = newVertices.vertexIndices[GetFacePointKey(f)]; // face-point index
                    //
                    int[] eis = new int[edgeCount];                         // edge-point indices
                    for (int i = 0; i < edgeCount; ++i)
                    {
                        Edge e = new Edge {
                            face = f, index = i
                        };
                        // get neighbor for solid back edges
                        if (mesh.GetEdgeType(e) == EdgeType.back)   //!!! here should be some EdgeType.backOfSeam or EdgeType.backOfSolid
                        {
                            Edge n = mesh.GetNeighbor(e);
                            if (mesh.GetEdgeType(n) == EdgeType.solid)
                            {
                                e = n;
                            }
                        }
                        eis[i] = newVertices.vertexIndices[GetEdgePointKey(e)];
                    }
                    //
                    for (int i = 0; i < edgeCount; ++i)
                    {
                        int[] q = new int[4];             // new faces are always quads
                        int   s = edgeCount == 4 ? i : 0; // shift indices (+ i) to keep quad orientation
                        q[(0 + s) % 4] = fis[i];
                        q[(1 + s) % 4] = eis[i];
                        q[(2 + s) % 4] = ci;
                        q[(3 + s) % 4] = eis[(i - 1 + edgeCount) % edgeCount];
                        newMesh.submeshes[si].faces[faceIndex++] = q;
                    }
                }
            }

            newMesh.FinishBuilding();

            return(newMesh);
        }
 public Vertex Average(Vertex[] vs, VertexContent mask = VertexContent.full, float[] weights = null)
 {
     return(MeshX.AverageVertices(vs, content & mask, weights));
 }