Example #1
0
        int _replaceCornerUV2InFaceGroup(Vector2 oldUV2, Vector2 newUV2, IndexList faceList, IndexList corners, int filterTag)
        {
            int count = 0;

            for (int i = 0; i < faceList.Count; ++i)
            {
                Face f = faces[faceList[i]];
                if (f.mark == filterTag)
                {
                    continue;
                }
                if (UnityUtils.Vector2CompareWithTolerance(f.uv2[corners[i]], oldUV2, equalityTolerance) == 0)
                {
                    f.uv2[corners[i]] = newUV2;
                    f.mark            = filterTag;
                    count++;
                }
            }
            return(count);
        }
Example #2
0
        public void RebuildMesh(ref int[] vertTable, ref int[] faceTable, bool verbose = false)
        {
            InvalidateUnconnectedVertices();
            InvalidateDegenerateFaces();
            int numVerts = vertCount();
            int numFaces = faceCount();

            vertTable = new int[numVerts];
            faceTable = new int[numFaces];
            List <Vertex> newVerts = new List <Vertex>();
            List <Face>   newFaces = new List <Face>();

            for (int i = 0; i < numVerts; ++i)
            {
                if (vertices[i].valid)
                {
                    vertTable[i] = newVerts.Count;
                    newVerts.Add(vertices[i]);
                }
                else
                {
                    vertTable[i] = -1;
                }
            }
            for (int i = 0; i < numFaces; ++i)
            {
                if (faces[i].valid)
                {
                    faceTable[i] = newFaces.Count;
                    newFaces.Add(faces[i]);
                }
                else
                {
                    faceTable[i] = -1;
                }
            }
            vertices = newVerts;
            faces    = newFaces;
            // Update index
            if (verbose)
            {
                Debug.Log("Rebuild Vertex Count " + numVerts + " -> " + vertCount());
                Debug.Log("Rebuild Face Count " + numFaces + " -> " + faceCount());
            }
            numVerts = vertCount();
            numFaces = faceCount();
            for (int i = 0; i < numVerts; ++i)
            {
                IndexList lFaces    = vertices[i].linkedFaces;
                IndexList lFacesNew = new IndexList(18);
                for (int j = 0; j < lFaces.Count; ++j)
                {
                    int l = faceTable[lFaces[j]];
                    if (l != -1)
                    {
                        lFacesNew.Add(l);
                    }
                    else
                    {
                        Debug.LogError("!!!!!");
                    }
                }
                vertices[i].linkedFaces = lFacesNew;
            }
            for (int i = 0; i < numFaces; ++i)
            {
                int[] v = faces[i].v;
                for (int j = 0; j < faces[i].cornerCount; ++j)
                {
                    v[j] = vertTable[v[j]];
                }
            }
            numValidVerts = numVerts;
            numValidFaces = numFaces;
        }
Example #3
0
        // REQUIRES triangulated mesh!
        public int CollapseVertexPair(CollapseInfo info)
        {
            if (topology != MeshTopology.Triangles)
            {
                Debug.LogError("KrablMesh: Collapsing a vertex pair requires a triangle mesh");
                return(0);
            }

            VertexPair pair = info.vp;
            int        vindex0 = pair.v[0];
            int        vindex1 = pair.v[1];
            Vertex     vertex0 = vertices[vindex0];
            Vertex     vertex1 = vertices[vindex1];
            int        i, j;

            changeFaces0.Clear();
            changeFaces1.Clear();
            removeFaces.Clear();
            CollectCollapseFacesForVertexPair(pair, changeFaces0, changeFaces1, removeFaces);

            // Adjust parameters of vertex0 to the new position
            float ratio1 = info.Ratio(this);

            // try baricentric projection on all the removeFaces (usually 2)
            int     projFaceIndex = -1;
            Face    projFace = null;
            int     projCorner0 = 0, projCorner1 = 0;
            Vector3 bari = Vector3.zero;

            int[] v = null;
            for (i = 0; i < removeFaces.Count; ++i)
            {
                Face f = faces[removeFaces[i]];
                v    = f.v;
                bari = UnityUtils.BaricentricProjection(info.targetPosition, vertices[v[0]].coords, vertices[v[1]].coords, vertices[v[2]].coords);
                if (UnityUtils.AreBaricentricCoordsInsideTriangle(bari))
                {
                    projFaceIndex = removeFaces[i];
                    projFace      = f;
                    projCorner0   = projFace.CornerIndexTriangle(vindex0);
                    projCorner1   = projFace.CornerIndexTriangle(vindex1);
                    break;
                }
            }
            // There must not be invalid faces in changeFaces0 or changeFaces1 !!!

            /*	for (i = 0; i < changeFaces0.Count; ++i) if (faces[changeFaces0[i]].valid == false) Debug.LogError("NOOO!");
             *      for (i = 0; i < changeFaces1.Count; ++i) if (faces[changeFaces1[i]].valid == false) Debug.LogError("NOOO!");
             *      for (i = 0; i < removeFaces.Count; ++i) if (faces[removeFaces[i]].valid == false) Debug.LogError("NOOO!");
             */
            // Deal with vertex colors and boneweights. these are per vertex.
            if (projFace != null)
            {
                if (hasVertexColors)
                {
                    vertex0.color = bari.x * vertices[v[0]].color + bari.y * vertices[v[1]].color + bari.z * vertices[v[2]].color;
                }
                if (hasBoneWeights)
                {
                    vertex0.boneWeight = UnityUtils.BoneWeightBaricentricInterpolation(vertices[v[0]].boneWeight, vertices[v[1]].boneWeight, vertices[v[2]].boneWeight, bari.x, bari.y, bari.z);
                }
            }
            else
            {
                if (hasVertexColors)
                {
                    vertex0.color = Color.Lerp(vertex0.color, vertex1.color, ratio1);
                }
                if (hasBoneWeights)
                {
                    vertex0.boneWeight = UnityUtils.BoneWeightLerp(vertex0.boneWeight, vertex1.boneWeight, ratio1);
                }
            }

            // Determine corner numbers for v0 in changefaces0 and v1 in changefaces1
            IndexList corners0 = new IndexList(changeFaces0.Count);

            for (i = 0; i < changeFaces0.Count; ++i)
            {
                corners0[i] = faces[changeFaces0[i]].CornerIndexTriangle(vindex0);
            }
            IndexList corners1 = new IndexList(changeFaces1.Count);

            for (i = 0; i < changeFaces1.Count; ++i)
            {
                corners1[i] = faces[changeFaces1[i]].CornerIndexTriangle(vindex1);
            }

            #region Face-Dependent Attributes (Vertex normals, uv1, uv2)

            // NORMALS
            int     count = 0, filterTag = GetUniqueTag();
            Vector3 projNormalNew = Vector3.zero;
            if (projFace != null)
            {
                projNormalNew = bari.x * projFace.vertexNormal[0] + bari.y * projFace.vertexNormal[1] + bari.z * projFace.vertexNormal[2];
                count         = _replaceCornerNormalInFaceGroup(projFace.vertexNormal[projCorner0], projNormalNew, changeFaces0, corners0, filterTag);
            }
            if (count < changeFaces0.Count)
            {
                // there are faces which cannot use baricentric projection
                for (j = 0; j < removeFaces.Count; ++j)
                {
                    if (removeFaces[j] != projFaceIndex)
                    {
                        Face    f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1);
                        Vector3 oldNormal = f2.vertexNormal[c0];
                        _replaceCornerNormalInFaceGroup(oldNormal, Vector3.Lerp(oldNormal, f2.vertexNormal[c1], ratio1), changeFaces0, corners0, filterTag);
                    }
                }
            }

            count = 0; filterTag = GetUniqueTag();
            if (projFace != null)
            {
                count = _replaceCornerNormalInFaceGroup(projFace.vertexNormal[projCorner1], projNormalNew, changeFaces1, corners1, filterTag);
            }
            if (count < changeFaces1.Count)
            {
                // there are faces which cannot use baricentric projection
                for (j = 0; j < removeFaces.Count; ++j)
                {
                    if (removeFaces[j] != projFaceIndex)
                    {
                        Face    f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1);
                        Vector3 oldNormal = f2.vertexNormal[c1];
                        _replaceCornerNormalInFaceGroup(oldNormal, Vector3.Lerp(f2.vertexNormal[c0], oldNormal, ratio1), changeFaces1, corners1, filterTag);
                    }
                }
            }

            if (hasUV1)
            {
                count = 0; filterTag = GetUniqueTag();
                Vector2 projUV1New = Vector2.zero;
                if (projFace != null)
                {
                    projUV1New = bari.x * projFace.uv1[0] + bari.y * projFace.uv1[1] + bari.z * projFace.uv1[2];
                    count      = _replaceCornerUV1InFaceGroup(projFace.uv1[projCorner0], projUV1New, changeFaces0, corners0, filterTag);
                }
                if (count < changeFaces0.Count)
                {
                    // there are faces which cannot use baricentric projection
                    for (j = 0; j < removeFaces.Count; ++j)
                    {
                        if (removeFaces[j] != projFaceIndex)
                        {
                            Face    f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1);
                            Vector2 oldUV1 = f2.uv1[c0];
                            _replaceCornerUV1InFaceGroup(oldUV1, Vector2.Lerp(oldUV1, f2.uv1[c1], ratio1), changeFaces0, corners0, filterTag);
                        }
                    }
                }

                count = 0; filterTag = GetUniqueTag();
                if (projFace != null)
                {
                    count = _replaceCornerUV1InFaceGroup(projFace.uv1[projCorner1], projUV1New, changeFaces1, corners1, filterTag);
                }
                if (count < changeFaces1.Count)
                {
                    // there are faces which cannot use baricentric projection
                    for (j = 0; j < removeFaces.Count; ++j)
                    {
                        if (removeFaces[j] != projFaceIndex)
                        {
                            Face    f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1);
                            Vector2 oldUV1 = f2.uv1[c1];
                            _replaceCornerUV1InFaceGroup(oldUV1, Vector2.Lerp(f2.uv1[c0], oldUV1, ratio1), changeFaces1, corners1, filterTag);
                        }
                    }
                }
            }

            if (hasUV2)
            {
                count = 0; filterTag = GetUniqueTag();
                Vector2 projUV2New = Vector2.zero;
                if (projFace != null)
                {
                    projUV2New = bari.x * projFace.uv2[0] + bari.y * projFace.uv2[1] + bari.z * projFace.uv2[2];
                    count      = _replaceCornerUV2InFaceGroup(projFace.uv2[projCorner0], projUV2New, changeFaces0, corners0, filterTag);
                }
                if (count < changeFaces0.Count)
                {
                    // there are faces which cannot use baricentric projection
                    for (j = 0; j < removeFaces.Count; ++j)
                    {
                        if (removeFaces[j] != projFaceIndex)
                        {
                            Face    f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1);
                            Vector2 oldUV2 = f2.uv2[c0];
                            _replaceCornerUV2InFaceGroup(oldUV2, Vector2.Lerp(oldUV2, f2.uv2[c1], ratio1), changeFaces0, corners0, filterTag);
                        }
                    }
                }

                count = 0; filterTag = GetUniqueTag();
                if (projFace != null)
                {
                    count = _replaceCornerUV2InFaceGroup(projFace.uv2[projCorner1], projUV2New, changeFaces1, corners1, filterTag);
                }
                if (count < changeFaces1.Count)
                {
                    // there are faces which cannot use baricentric projection
                    for (j = 0; j < removeFaces.Count; ++j)
                    {
                        if (removeFaces[j] != projFaceIndex)
                        {
                            Face    f2 = faces[removeFaces[j]]; int c0 = f2.CornerIndexTriangle(vindex0), c1 = f2.CornerIndexTriangle(vindex1);
                            Vector2 oldUV2 = f2.uv2[c1];
                            _replaceCornerUV2InFaceGroup(oldUV2, Vector2.Lerp(f2.uv2[c0], oldUV2, ratio1), changeFaces1, corners1, filterTag);
                        }
                    }
                }
            }
            #endregion
            // Move vertex to goal position
            vertex0.coords = info.targetPosition;

            // remove faces
            //	Debug.Log("Change faces 1 num: " + changeFaces0.Count);
            //	Debug.Log("Change faces 2 num: " + changeFaces1.Count);
            //	Debug.Log("Remove faces num: " + removeFaces.Count);
            for (i = 0; i < removeFaces.Count; ++i)
            {
                UnlinkFace(removeFaces[i]);
            }

            // change vertex on vindex1 faces, update surrounding faces on vindex0
            for (i = 0; i < changeFaces1.Count; ++i)
            {
                int  faceIndex = changeFaces1[i];
                Face f         = faces[faceIndex];
                if (f.valid)
                {
                    f.ReplaceVertex(vindex1, vindex0);
                    vertex0.linkedFaces.Add(faceIndex);
                }
            }

            // mark vindex1 as invalid
            vertex1.linkedFaces.Clear();
            if (vertex1.valid == true)
            {
                numValidVerts--;
                vertex1.valid = false;
            }
            else
            {
                Debug.LogError("vindex1 was already invalid");
            }
            return(vindex0);
        }
Example #4
0
        int _replaceCornerNormalInFaceGroup(Vector3 oldNormal, Vector3 newNormal, IndexList faceList, IndexList corners, int filterTag)
        {
            int count = 0;

            for (int i = 0; i < faceList.Count; ++i)
            {
                Face f = faces[faceList[i]];
                if (f.mark == filterTag)
                {
                    continue;
                }
                if (UnityUtils.Vector3CompareWithTolerance(f.vertexNormal[corners[i]], oldNormal, equalityTolerance) == 0)
                {
                    f.vertexNormal[corners[i]] = newNormal;
                    f.mark = filterTag;
                    count++;
                }
            }
            return(count);
        }
Example #5
0
        public void CollectCollapseFacesForVertexPair(VertexPair pair, IndexList changeFaces0, IndexList changeFaces1, IndexList commonFaces)
        {
            IndexList il0 = vertices[pair.v[0]].linkedFaces;
            IndexList il1 = vertices[pair.v[1]].linkedFaces;

            int[] v0Faces = il0.array;
            int[] v1Faces = il1.array;
            int   v0Count = il0.Count;
            int   v1Count = il1.Count;
            int   tag     = GetUniqueTag();

            // Grow target lists to save on checks later
            changeFaces0.GrowToCapacity(v0Count);
            changeFaces1.GrowToCapacity(v1Count);
            commonFaces.GrowToCapacity(v0Count);             // could be min(v0count, v1count), but that's probably slower

            for (int i = 0; i < v1Count; ++i)
            {
                faces[v1Faces[i]].mark = tag;
            }
            for (int i = 0; i < v0Count; ++i)
            {
                int  faceIndex = v0Faces[i];
                Face f         = faces[faceIndex];
                //	if (f.valid) {
                if (f.mark == tag)
                {
                    commonFaces.AddUnsafe(faceIndex);
                    f.mark = 0;
                }
                else
                {
                    changeFaces0.AddUnsafe(faceIndex);
                }
                //	}
            }
            for (int i = 0; i < v1Count; ++i)
            {
                int  faceIndex = v1Faces[i];
                Face f         = faces[faceIndex];
                if (/*f.valid &&*/ f.mark == tag)
                {
                    changeFaces1.AddUnsafe(faceIndex);
                }
            }
        }
Example #6
0
        void _updateEdgePenalties(int edgeIndex, CollapseInfo cinfo, int movedVertexIndex = -1)
        {
            Edge   edge    = mesh.edges[edgeIndex];
            int    vindex0 = edge.v[0];
            int    vindex1 = edge.v[1];
            Vertex v0      = mesh.vertices[vindex0];
            Vertex v1      = mesh.vertices[vindex1];

            faces0.Clear();
            faces1.Clear();
            commonFaces.Clear();

            bool hadPenalty = (cinfo.cost >= kMeshPenaltyMaxEdgesPerVertex);

            cinfo.cost = cinfo.positionCost;             // reset cost
            // determine the faces involved in the collapse ..
            mesh.CollectCollapseFacesForVertexPair(mesh.edges[edgeIndex], faces0, faces1, commonFaces);

            // Penalties

            int filterTag = 0;

            if (_parameters.preventNonManifoldEdges && _producesNonManifold(mesh, cinfo))
            {
                cinfo.cost += kMeshPenaltyNonManifold;                 // largest penalty first
            }
            else
            {
                if (movedVertexIndex != -1 && hadPenalty == false)
                {
                    // For cinfos that are not new and had no penalty before, all faces besides the onces connected
                    // to the moved vertex can be skipped.
                    filterTag = mesh.GetUniqueTag();
                    IndexList linkedFaces = mesh.vertices[movedVertexIndex].linkedFaces;
                    int       count       = linkedFaces.Count;
                    for (int i = 0; i < count; ++i)
                    {
                        mesh.faces[linkedFaces[i]].mark = filterTag;
                    }
                }
                if (_parameters.checkTopology && _producesBadTopology(mesh, cinfo, filterTag))
                {
                    // Apply penalties for bad collapses
                    cinfo.cost += kMeshPenaltyBadTopology;
                }
                else if (_parameters.maxEdgesPerVertex > 0 && _vertexDegreeAfterCollapse(mesh, cinfo) > _parameters.maxEdgesPerVertex)                     // Hard coded at 18 for now.. rarely reached, but always check!
                // Avoid collapses leading to excessive stars (many verts connected to one)
                {
                    cinfo.cost += kMeshPenaltyMaxEdgesPerVertex;
                }
            }

            // Additional penalties:
            float val = 0.0f;

            if (_parameters.boneWeightProtection > 0.0f)
            {
                val += UnityUtils.BoneWeightDeltaSqr(v0.boneWeight, v1.boneWeight) * _parameters.boneWeightProtection;
            }
            if (_parameters.vertexColorProtection > 0.0f)
            {
                val += UnityUtils.ColorDeltaSqr(v0.color, v1.color) * _parameters.vertexColorProtection;
            }
            if (val != 0.0f)
            {
                cinfo.cost += 0.1f * val * mesh.EdgeLengthSqr(edgeIndex);
            }
        }
Example #7
0
        public void CalculateFaceVertexNormalsFromEdgeCreasesForVertex(int vertexIndex, ref List <int>[] faceLinkedEdges)
        {
            int        i, j, grp;
            List <int> grpCornerIndex = new List <int>();

            Vertex    v               = vertices[vertexIndex];
            IndexList vertexFaces     = v.linkedFaces;
            int       vertexFaceCount = vertexFaces.Count;

            //	List<int> vertexEdges = linkedEdges[vertexIndex];
            // Clear face marks around vertex
            for (j = 0; j < vertexFaceCount; ++j)
            {
                faces[vertexFaces[j]].mark = -1;                 // TODO: could be faster with uniqueTag
            }
            // This will add each facemark to a groupIndex
            int groupIndex = 0;

            for (j = 0; j < vertexFaceCount; ++j)
            {
                int faceIndex = vertexFaces[j];
                if (faces[faceIndex].mark == -1)                   // face still available
                {
                    _markGroupFaceNeightbours(vertexIndex, vertexFaces[j], ref faceLinkedEdges, groupIndex);
                    groupIndex++;
                }
            }
            // Build group arrays
            List <int>[] groups = new List <int> [groupIndex];          // are these too many allocations?
            for (i = 0; i < groupIndex; ++i)
            {
                groups[i] = new List <int>();
            }
            for (i = 0; i < vertexFaceCount; ++i)
            {
                int faceIndex = vertexFaces[i];
                int mark      = faces[faceIndex].mark;
                groups[mark].Add(faceIndex);
            }

            // Calculate and set normal for each face on the vertex based on the groups
            Vector3 normal;

            for (grp = 0; grp < groupIndex; ++grp)
            {
                normal = Vector3.zero;
                List <int> grpFaces = groups[grp];
                int        cnt      = grpFaces.Count;
                grpCornerIndex.Clear();
                for (i = 0; i < cnt; ++i)
                {
                    Face f = faces[grpFaces[i]];
                    if (f.normal == Vector3.zero)
                    {
                        //	Debug.Log("face has zero normal .. valid " + f.valid);
                    }
                    // Multiply with corner angle (=SLOW?)
                    int   corner = f.CornerIndex(vertexIndex);
                    float fact   = CornerAngle(grpFaces[i], corner);
                    normal += f.normal * fact;
                    grpCornerIndex.Add(corner);
                }
                UnityUtils.NormalizeSmallVector(ref normal);
                if (normal == Vector3.zero)
                {
                    //	Debug.Log("NORMAL == ZERO facecount " + cnt);
                }
                // Now set the normal to all group faces
                for (i = 0; i < cnt; ++i)
                {
                    faces[grpFaces[i]].vertexNormal[grpCornerIndex[i]] = normal;
                }
            }
        }
Example #8
0
        public static bool CheckMeshIntegrity(MeshEdges mesh)
        {
            // Vertex linked faces
            int numVerts = mesh.vertCount();
            int numFaces = mesh.faceCount();
            int numEdges = mesh.edgeCount();

            List <int>[] mVertexLinkedFaces = new List <int> [numVerts];
            for (int i = 0; i < numVerts; ++i)
            {
                mVertexLinkedFaces[i] = new List <int>();
            }
            for (int i = 0; i < numFaces; ++i)
            {
                Face f = mesh.faces[i];
                if (f.valid)
                {
                    for (int j = 0; j < f.cornerCount; ++j)
                    {
                        int vertIndex = f.v[j];
                        if (mesh.IsVertexValid(vertIndex))
                        {
                            mVertexLinkedFaces[vertIndex].Add(i);
                        }
                    }
                }
            }
            for (int i = 0; i < numVerts; ++i)
            {
                if (mesh.IsVertexValid(i))
                {
                    IndexList test = new IndexList(4);
                    for (int j = 0; j < mesh.vertices[i].linkedFaces.Count; ++j)
                    {
                        if (mesh.faces[mesh.vertices[i].linkedFaces[j]].valid)
                        {
                            test.Add(mesh.vertices[i].linkedFaces[j]);
                        }
                    }

                    /*if (_areIntListsTheSame(mesh.vertices[i].linkedFaces, mVertexLinkedFaces[i]) == false) {
                     *      Debug.LogError("INVALID MESH vertexLinkedFaces not correct for vertex " + i);
                     *      return false;
                     * }*/
                }
            }

            // Vertex linked edges
            for (int i = 0; i < numVerts; ++i)
            {
                if (mesh.IsVertexValid(i))
                {
                    List <int> li1 = new List <int>();
                    mesh.CollectVerticesAroundVertex(i, ref li1);
                    List <int> li2 = new List <int>();
                    List <int> le  = mesh.linkedEdgesForVert(i);
                    for (int j = 0; j < le.Count; ++j)
                    {
                        if (mesh.IsEdgeValid(le[j]))
                        {
                            li2.Add(mesh.edges[le[j]].OtherVertex(i));
                        }
                    }
                    if (_areIntListsTheSame(li1, li2) == false)
                    {
                        Debug.LogError("INVALID MESH vertexLinkedEdges not correct for vertex" + i);
                        return(false);
                    }
                }
            }

            // Edge linked faces
            for (int i = 0; i < numEdges; ++i)
            {
                if (mesh.IsEdgeValid(i))
                {
                    IndexList test = new IndexList(18);
                    mesh.CollectVertexPairFaces(mesh.edges[i], test);

                    /*	if (_areIntListsTheSame(test, mesh.edges[i).linkedFaces) == false) {
                     *              Debug.LogError("INVALID MESH edgeLinkedFaces not correct for edge" + i);
                     *              return false;
                     *      }*/
                }
            }

            Debug.Log("Mesh integrity is good.");
            return(true);
        }
Example #9
0
        void _calculateVertexPositions(MeshEdges mesh)
        {
            int numVerts = mesh.vertCount();
            int numFaces = mesh.faceCount();
            int numEdges = mesh.edgeCount();

            vertPoints = new Vector3[numVerts];
            facePoints = new Vector3[numFaces];
            edgePoints = new Vector3[numEdges];

            // In any case the new face points are the center of the faces
            for (int i = 0; i < numFaces; ++i)
            {
                facePoints[i] = mesh.CalculateFaceCenter(i);
            }

            if (smooth)
            {
                // First count the number of creases connected to each vertex
                int[] vertexNumCreases = new int[numVerts];
                for (int i = 0; i < numVerts; ++i)
                {
                    List <int> linkedEdges = mesh.linkedEdgesForVert(i);
                    for (int j = 0; j < linkedEdges.Count; ++j)
                    {
                        int edgeIndex = linkedEdges[j];
                        if (mesh.IsEdgeValid(edgeIndex) && mesh.edges[edgeIndex].crease > 0.0)
                        {
                            vertexNumCreases[i]++;
                        }
                    }
                }

                // Edge is the average of the two ends and the connected face centers
                for (int i = 0; i < numEdges; ++i)
                {
                    Edge      e           = mesh.edges[i];
                    IndexList linkedFaces = e.linkedFaces;
                    edgePoints[i] = mesh.CalculateVertexPairCenter(e);

                    // If an edge connects two verts that do not move, use the center to prevent overlaps
                    //bool betweenNonMovableVertices = (vertexNumCreases[e.v[0]] > 2 && vertexNumCreases[e.v[1]] > 2);
                    //if (betweenNonMovableVertices) Debug.Log("Between NONmovable verts");
                    if (/*betweenNonMovableVertices == false &&*/ e.crease == 0.0f && linkedFaces.Count >= 2)
                    {
                        // creases and borders stay at edge centers
                        // other edges use the edge center + the center of all attached face centers
                        Vector3 faceCenterCenter = facePoints[linkedFaces[0]];
                        int     numLinked        = e.linkedFaces.Count;
                        for (int j = 1; j < numLinked; ++j)
                        {
                            faceCenterCenter += facePoints[linkedFaces[j]];
                        }
                        edgePoints[i] = 0.5f * (edgePoints[i] + faceCenterCenter * (1.0f / ((float)numLinked)));
                    }
                }
                // Vert
                for (int i = 0; i < numVerts; ++i)
                {
                    Vector3    oldPosition = mesh.vertices[i].coords;
                    List <int> linkedEdges = mesh.linkedEdgesForVert(i);
                    if (_isVertexBorder(mesh, i) == false)
                    {
                        // First deal with edges. the number of creases needs to be known!


                        Vector3 edgesCenter  = Vector3.zero;
                        float   numCreases   = 0.0f;
                        Vector3 creaseCenter = Vector3.zero;
                        float   nEdges       = 0.0f;
                        for (int j = 0; j < linkedEdges.Count; ++j)
                        {
                            int edgeIndex = linkedEdges[j];
                            if (mesh.IsEdgeValid(edgeIndex))
                            {
                                Edge    e      = mesh.edges[edgeIndex];
                                Vector3 center = mesh.CalculateVertexPairCenter(e);
                                if (e.crease > 0.0f)
                                {
                                    numCreases   += 1.0f;
                                    creaseCenter += center;
                                }
                                nEdges      += 1.0f;
                                edgesCenter += center;
                            }
                        }
                        edgesCenter *= 1.0f / nEdges;

                        // For points without crease edges or just one -> do nothing
                        if (numCreases == 2.0f)
                        {
                            // like a border connection
                            vertPoints[i] = 0.5f * oldPosition + creaseCenter * (0.5f / numCreases);
                        }
                        else if (numCreases > 2.0f)
                        {
                            // A sharp corner
                            vertPoints[i] = oldPosition;
                        }
                        else
                        {
                            // Full formula including faces center which needs to be calculated
                            IndexList linkedFaces = mesh.vertices[i].linkedFaces;
                            Vector3   facesCenter = Vector3.zero;
                            float     n           = 0.0f;
                            for (int j = 0; j < linkedFaces.Count; ++j)
                            {
                                int faceIndex = linkedFaces[j];
                                if (mesh.faces[faceIndex].valid)
                                {
                                    facesCenter += facePoints[faceIndex];
                                    n           += 1.0f;
                                }
                            }
                            float invN = 1.0f / n;
                            vertPoints[i] = (facesCenter * invN + 2.0f * edgesCenter + (n - 3.0f) * oldPosition) * invN;                     // useless for 2 creases!!!
                        }
                    }
                    else
                    {
                        // Border Vertex. get center of all connected border centers.
                        if (vertexNumCreases[i] > 2)
                        {
                            vertPoints[i] = oldPosition;
                        }
                        else
                        {
                            float   nBorders     = 0.0f;
                            Vector3 borderCenter = Vector3.zero;
                            for (int j = 0; j < linkedEdges.Count; ++j)
                            {
                                int edgeIndex = linkedEdges[j];
                                if (mesh.IsEdgeValid(edgeIndex))
                                {
                                    Edge e = mesh.edges[edgeIndex];
                                    if (mesh.IsEdgeBorder(edgeIndex))
                                    {
                                        borderCenter += mesh.CalculateVertexPairCenter(e);
                                        nBorders     += 1.0f;
                                    }
                                }
                            }
                            vertPoints[i] = 0.5f * oldPosition + borderCenter * (0.5f / nBorders);
                        }
                    }
                }
            }
            else
            {
                // Subdivision without smoothing
                for (int i = 0; i < numVerts; ++i)
                {
                    vertPoints[i] = mesh.vertices[i].coords;
                }
                for (int i = 0; i < numEdges; ++i)
                {
                    edgePoints[i] = mesh.CalculateVertexPairCenter(mesh.edges[i]);
                }
            }
        }