/** * Inserts a vertex at the center of each edge, then connects the new vertices to another new * vertex placed at the center of the face. */ private static bool SubdivideFace_Internal(pb_Object pb, pb_EdgeConnection pb_edgeConnection, out DanglingVertex?[] appendedVertices, out pb_Face[] splitFaces, out Vector3[][] splitVertices, out Color[][] splitColors, out Vector2[][] splitUVs, out int[][] splitSharedIndices) { splitFaces = null; splitVertices = null; splitColors = null; splitUVs = null; splitSharedIndices = null; appendedVertices = new DanglingVertex?[pb_edgeConnection.edges.Count]; // cache all the things pb_Face face = pb_edgeConnection.face; Dictionary <int, int> sharedIndices = pb.sharedIndices.ToDictionary(); Vector3[] vertices = pb.vertices; Vector2[] uvs = pb.uv; List <Vector2> edgeCentersUV = new List <Vector2>(); List <Vector3> edgeCenters3d = new List <Vector3>(); List <Color> edgeCentersCol = new List <Color>(); // filter duplicate edges int u = 0; List <int> usedEdgeIndices = new List <int>(); foreach (pb_Edge edge in pb_edgeConnection.edges) { int ind = face.edges.IndexOf(edge, sharedIndices); if (!usedEdgeIndices.Contains(ind)) { Vector3 cen = (vertices[edge.x] + vertices[edge.y]) / 2f; appendedVertices[u] = new DanglingVertex(cen, (pb.colors[edge.x] + pb.colors[edge.y]) / 2f); edgeCenters3d.Add(cen); edgeCentersUV.Add((uvs[edge.x] + uvs[edge.y]) / 2f); edgeCentersCol.Add((pb.colors[edge.x] + pb.colors[edge.y]) / 2f); usedEdgeIndices.Add(ind); } else { appendedVertices[u] = null; } u++; } // now we have all the vertices of the old face, plus the new edge center vertices Vector3 nrm = pb_Math.Normal(pb.GetVertices(face.indices)); Vector3[] verts3d = pb.GetVertices(face.distinctIndices); Vector2[] faceUVs = pb.GetUVs(face.distinctIndices); Color[] colors = pbUtil.ValuesWithIndices(pb.colors, face.distinctIndices); Vector2[] verts2d = pb_Math.PlanarProject(verts3d, nrm); Vector2[] edgeCenters2d = pb_Math.PlanarProject(edgeCenters3d.ToArray(), nrm); Vector3 cen3d = pb_Math.Average(verts3d); Vector2 cenUV = pb_Bounds2D.Center(faceUVs); Vector2 cen2d = pb_Math.PlanarProject(new Vector3[1] { cen3d }, nrm)[0]; // Get the directions from which to segment this face Vector2[] dividers = new Vector2[edgeCenters2d.Length]; for (int i = 0; i < edgeCenters2d.Length; i++) { dividers[i] = (edgeCenters2d[i] - cen2d).normalized; } List <Vector2>[] quadrants2d = new List <Vector2> [edgeCenters2d.Length]; List <Vector3>[] quadrants3d = new List <Vector3> [edgeCenters2d.Length]; List <Vector2>[] quadrantsUV = new List <Vector2> [edgeCenters2d.Length]; List <Color>[] quadrantsCol = new List <Color> [edgeCenters2d.Length]; List <int>[] sharedIndex = new List <int> [edgeCenters2d.Length]; for (int i = 0; i < quadrants2d.Length; i++) { quadrants2d[i] = new List <Vector2>(1) { cen2d }; quadrants3d[i] = new List <Vector3>(1) { cen3d }; quadrantsUV[i] = new List <Vector2>(1) { cenUV }; quadrantsCol[i] = new List <Color>(1) { pb_Math.Average(pbUtil.ValuesWithIndices(pb.colors, face.distinctIndices)) }; sharedIndex[i] = new List <int>(1) { -2 }; // any negative value less than -1 will be treated as a new group } // add the divisors for (int i = 0; i < edgeCenters2d.Length; i++) { quadrants2d[i].Add(edgeCenters2d[i]); quadrants3d[i].Add(edgeCenters3d[i]); quadrantsUV[i].Add(edgeCentersUV[i]); quadrantsCol[i].Add(edgeCentersCol[i]); sharedIndex[i].Add(-1); // and add closest in the counterclockwise direction Vector2 dir = (edgeCenters2d[i] - cen2d).normalized; float largestClockwiseDistance = 0f; int quad = -1; for (int j = 0; j < dividers.Length; j++) { if (j == i) { continue; // this is a dividing vertex - ignore } float dist = Vector2.Angle(dividers[j], dir); if (Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f) { dist = 360f - dist; } if (dist > largestClockwiseDistance) { largestClockwiseDistance = dist; quad = j; } } quadrants2d[quad].Add(edgeCenters2d[i]); quadrants3d[quad].Add(edgeCenters3d[i]); quadrantsUV[quad].Add(edgeCentersUV[i]); quadrantsCol[quad].Add(edgeCentersCol[i]); sharedIndex[quad].Add(-1); } // distribute the existing vertices for (int i = 0; i < face.distinctIndices.Length; i++) { Vector2 dir = (verts2d[i] - cen2d).normalized; // plane corresponds to distinctIndices float largestClockwiseDistance = 0f; int quad = -1; for (int j = 0; j < dividers.Length; j++) { float dist = Vector2.Angle(dividers[j], dir); if (Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f) { dist = 360f - dist; } if (dist > largestClockwiseDistance) { largestClockwiseDistance = dist; quad = j; } } quadrants2d[quad].Add(verts2d[i]); quadrants3d[quad].Add(verts3d[i]); quadrantsUV[quad].Add(faceUVs[i]); quadrantsCol[quad].Add(colors[i]); sharedIndex[quad].Add(sharedIndices[face.distinctIndices[i]]); } int len = quadrants2d.Length; // Triangulate int[][] tris = new int[len][]; for (int i = 0; i < len; i++) { if (quadrants2d[i].Count < 3) { Debug.LogError("Insufficient points to triangulate. Exit subdivide operation. This is probably due to a concave face."); return(false); } tris[i] = Delaunay.Triangulate(quadrants2d[i]).ToIntArray(); if (tris[i].Length < 3) ///< #521 { return(false); } if (Vector3.Dot(nrm, pb_Math.Normal(quadrants3d[i][tris[i][0]], quadrants3d[i][tris[i][1]], quadrants3d[i][tris[i][2]])) < 0) { System.Array.Reverse(tris[i]); } } splitFaces = new pb_Face[len]; splitVertices = new Vector3[len][]; splitColors = new Color[len][]; splitUVs = new Vector2[len][]; splitSharedIndices = new int[len][]; for (int i = 0; i < len; i++) { // triangles, material, pb_UV, smoothing group, shared index splitFaces[i] = new pb_Face(tris[i], face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.manualUV); splitVertices[i] = quadrants3d[i].ToArray(); splitColors[i] = quadrantsCol[i].ToArray(); splitUVs[i] = quadrantsUV[i].ToArray(); splitSharedIndices[i] = sharedIndex[i].ToArray(); } return(true); }
/** * Inserts a vertex at the center of each edge, then connects the new vertices to another new * vertex placed at the center of the face. */ private static bool SubdivideFace_Internal(pb_Object pb, pb_EdgeConnection pb_edgeConnection, out DanglingVertex?[] appendedVertices, out pb_Face[] splitFaces, out Vector3[][] splitVertices, out Color[][] splitColors, out Vector2[][] splitUVs, out int[][] splitSharedIndices) { splitFaces = null; splitVertices = null; splitColors = null; splitUVs = null; splitSharedIndices = null; appendedVertices = new DanglingVertex?[pb_edgeConnection.edges.Count]; // cache all the things pb_Face face = pb_edgeConnection.face; Dictionary<int, int> sharedIndices = pb.sharedIndices.ToDictionary(); Vector3[] vertices = pb.vertices; Vector2[] uvs = pb.uv; List<Vector2> edgeCentersUV = new List<Vector2>(); List<Vector3> edgeCenters3d = new List<Vector3>(); List<Color> edgeCentersCol = new List<Color>(); // filter duplicate edges int u = 0; List<int> usedEdgeIndices = new List<int>(); foreach(pb_Edge edge in pb_edgeConnection.edges) { int ind = face.edges.IndexOf(edge, sharedIndices); if(!usedEdgeIndices.Contains(ind)) { Vector3 cen = (vertices[edge.x] + vertices[edge.y]) / 2f; appendedVertices[u] = new DanglingVertex(cen, (pb.colors[edge.x] + pb.colors[edge.y]) / 2f); edgeCenters3d.Add(cen); edgeCentersUV.Add( (uvs[edge.x] + uvs[edge.y])/2f ); edgeCentersCol.Add( (pb.colors[edge.x] + pb.colors[edge.y])/2f ); usedEdgeIndices.Add(ind); } else { appendedVertices[u] = null; } u++; } // now we have all the vertices of the old face, plus the new edge center vertices Vector3 nrm = pb_Math.Normal(pb.GetVertices(face.indices)); Vector3[] verts3d = pb.GetVertices(face.distinctIndices); Vector2[] faceUVs = pb.GetUVs(face.distinctIndices); Color[] colors = pbUtil.ValuesWithIndices(pb.colors, face.distinctIndices); Vector2[] verts2d = pb_Math.PlanarProject(verts3d, nrm); Vector2[] edgeCenters2d = pb_Math.PlanarProject(edgeCenters3d.ToArray(), nrm); Vector3 cen3d = pb_Math.Average(verts3d); Vector2 cenUV = pb_Bounds2D.Center(faceUVs); Vector2 cen2d = pb_Math.PlanarProject( new Vector3[1] { cen3d }, nrm)[0]; // Get the directions from which to segment this face Vector2[] dividers = new Vector2[edgeCenters2d.Length]; for(int i = 0; i < edgeCenters2d.Length; i++) dividers[i] = (edgeCenters2d[i] - cen2d).normalized; List<Vector2>[] quadrants2d = new List<Vector2>[edgeCenters2d.Length]; List<Vector3>[] quadrants3d = new List<Vector3>[edgeCenters2d.Length]; List<Vector2>[] quadrantsUV = new List<Vector2>[edgeCenters2d.Length]; List<Color>[] quadrantsCol = new List<Color>[edgeCenters2d.Length]; List<int>[] sharedIndex = new List<int>[edgeCenters2d.Length]; for(int i = 0; i < quadrants2d.Length; i++) { quadrants2d[i] = new List<Vector2>(1) { cen2d }; quadrants3d[i] = new List<Vector3>(1) { cen3d }; quadrantsUV[i] = new List<Vector2>(1) { cenUV }; quadrantsCol[i] = new List<Color>(1) { pb_Math.Average(pbUtil.ValuesWithIndices(pb.colors, face.distinctIndices)) }; sharedIndex[i] = new List<int>(1) { -2 }; // any negative value less than -1 will be treated as a new group } // add the divisors for(int i = 0; i < edgeCenters2d.Length; i++) { quadrants2d[i].Add(edgeCenters2d[i]); quadrants3d[i].Add(edgeCenters3d[i]); quadrantsUV[i].Add(edgeCentersUV[i]); quadrantsCol[i].Add(edgeCentersCol[i]); sharedIndex[i].Add(-1); // and add closest in the counterclockwise direction Vector2 dir = (edgeCenters2d[i]-cen2d).normalized; float largestClockwiseDistance = 0f; int quad = -1; for(int j = 0; j < dividers.Length; j++) { if(j == i) continue; // this is a dividing vertex - ignore float dist = Vector2.Angle(dividers[j], dir); if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f ) dist = 360f - dist; if(dist > largestClockwiseDistance) { largestClockwiseDistance = dist; quad = j; } } quadrants2d[quad].Add(edgeCenters2d[i]); quadrants3d[quad].Add(edgeCenters3d[i]); quadrantsUV[quad].Add(edgeCentersUV[i]); quadrantsCol[quad].Add(edgeCentersCol[i]); sharedIndex[quad].Add(-1); } // distribute the existing vertices for(int i = 0; i < face.distinctIndices.Length; i++) { Vector2 dir = (verts2d[i]-cen2d).normalized; // plane corresponds to distinctIndices float largestClockwiseDistance = 0f; int quad = -1; for(int j = 0; j < dividers.Length; j++) { float dist = Vector2.Angle(dividers[j], dir); if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f ) dist = 360f - dist; if(dist > largestClockwiseDistance) { largestClockwiseDistance = dist; quad = j; } } quadrants2d[quad].Add(verts2d[i]); quadrants3d[quad].Add(verts3d[i]); quadrantsUV[quad].Add(faceUVs[i]); quadrantsCol[quad].Add(colors[i]); sharedIndex[quad].Add( sharedIndices[face.distinctIndices[i]] ); } int len = quadrants2d.Length; // Triangulate int[][] tris = new int[len][]; for(int i = 0; i < len; i++) { if(quadrants2d[i].Count < 3) { Debug.LogError("Insufficient points to triangulate. Exit subdivide operation. This is probably due to a concave face."); return false; } tris[i] = Delaunay.Triangulate(quadrants2d[i]).ToIntArray(); if(tris[i].Length < 3) ///< #521 return false; if( Vector3.Dot(nrm, pb_Math.Normal(quadrants3d[i][tris[i][0]], quadrants3d[i][tris[i][1]], quadrants3d[i][tris[i][2]])) < 0 ) System.Array.Reverse(tris[i]); } splitFaces = new pb_Face[len]; splitVertices = new Vector3[len][]; splitColors = new Color[len][]; splitUVs = new Vector2[len][]; splitSharedIndices = new int[len][]; for(int i = 0; i < len; i++) { // triangles, material, pb_UV, smoothing group, shared index splitFaces[i] = new pb_Face(tris[i], face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.manualUV); splitVertices[i] = quadrants3d[i].ToArray(); splitColors[i] = quadrantsCol[i].ToArray(); splitUVs[i] = quadrantsUV[i].ToArray(); splitSharedIndices[i] = sharedIndex[i].ToArray(); } return true; }