// update visible triangles for this chunk void update_triangles(OrderedChunk chunk, float max_scalar) { if (chunk.mesh == null) { chunk.mesh = new MeshChunk(); } int count = 0; foreach (int idx in chunk.order_range) { if (tri_ordering[idx].scalar < max_scalar) { count++; } } // if we have the same count, we can keep it if (chunk.mesh.current_count == count) { chunk.mesh.submesh.SetVisible(true); return; } // find subset triangles int[] triangles = new int[count]; for (int k = 0; k < count; ++k) { int idx = chunk.order_range.a + k; triangles[k] = tri_ordering[idx].tid; } // find submesh // [TODO] faster variant of this? Also could be computing these in background... DSubmesh3 submesh = new DSubmesh3(Mesh, triangles); MeshTransforms.VertexNormalOffset(submesh.SubMesh, NormalOffsetDistance); fMesh umesh = UnityUtil.DMeshToUnityMesh(submesh.SubMesh, false); // create or update GO if (chunk.mesh.submesh == null) { chunk.mesh.submesh = new fMeshGameObject(umesh, true, false); if (ChunkMeshMaterial != null) { chunk.mesh.submesh.SetMaterial(ChunkMeshMaterial); } if (ChunkMeshParent != null) { ChunkMeshParent.AddChild(chunk.mesh.submesh, false); } } else { chunk.mesh.submesh.UpdateMesh(umesh, true, false); } chunk.mesh.submesh.SetVisible(true); chunk.mesh.current_count = count; }
Polygon2d make_poly(DMesh3 mesh, IEnumerable <int> triangles) { DSubmesh3 submesh = new DSubmesh3(mesh, triangles); MeshBoundaryLoops loops = new MeshBoundaryLoops(submesh.SubMesh); Util.gDevAssert(loops.Loops.Count == 1); return(make_poly(submesh.SubMesh, loops.Loops[0])); }
protected override void process_new_result(DMeshOutputStatus result) { if (result.Mesh != null) { DSubmesh3 submesh = result.Mesh.FindMetadata("removed_submesh") as DSubmesh3; if (submesh != null) { RemovedSO.ReplaceMesh(submesh.SubMesh, true); result.Mesh.RemoveMetadata("removed_submesh"); } } }
public virtual void Update() { base.begin_update(); int start_timestamp = this.CurrentInputTimestamp; if (MeshSource == null) { throw new Exception("RemoveHiddenFacesOp: must set valid MeshSource to compute!"); } try { DMesh3 meshIn = MeshSource.GetDMeshUnsafe(); if (cachedSpatial == null || spatial_cache_timstamp != meshIn.ShapeTimestamp) { cachedSpatial = new DMeshAABBTreePro(meshIn, true); cachedSpatial.FastWindingNumber(Vector3d.Zero); spatial_cache_timstamp = meshIn.ShapeTimestamp; } DMesh3 editMesh = new DMesh3(meshIn); RemoveOccludedTriangles remove = new RemoveOccludedTriangles(editMesh, cachedSpatial) { InsideMode = (RemoveOccludedTriangles.CalculationMode)(int) inside_mode, PerVertex = all_hidden_vertices }; remove.Progress = new ProgressCancel(is_invalidated); if (remove.Apply() == false) { ResultMesh = null; RemovedTris = null; RemovedSubmesh = null; } else { ResultMesh = editMesh; RemovedTris = remove.RemovedT; RemovedSubmesh = new DSubmesh3(MeshSource.GetDMeshUnsafe(), RemovedTris); } base.complete_update(); } catch (Exception e) { PostOnOperatorException(e); ResultMesh = base.make_failure_output(MeshSource.GetDMeshUnsafe()); base.complete_update(); } }
protected override void on_curve_validated() { if (previewGO != null) { RemoveGO((fGameObject)previewGO); previewGO.Destroy(); } if (EnableRegionOverlay) { if (TargetModelSO == null) { throw new InvalidOperationException("EnclosedPatchSO.on_curve_validated: curve is not connected to a Target"); } if (TransformMode != OutputCurveTransform.ToTargetSO) { throw new InvalidOperationException("EnclosedPatchSO.on_curve_validated: curve is not transformed to TargetSO"); } DCurve3 target_curve = RequestCurveCopyFromMainThread(); MeshFacesFromLoop loop = new MeshFacesFromLoop(TargetModel.SourceMesh, target_curve, TargetModel.SourceSpatial); MeshFaceSelection face_selection = loop.ToSelection(); DSubmesh3 submesh = new DSubmesh3(TargetModel.SourceMesh, face_selection, face_selection.Count); MeshNormals normals = new MeshNormals(submesh.SubMesh); normals.Compute(); foreach (int vid in submesh.SubMesh.VertexIndices()) { Vector3d n = normals.Normals[vid]; Vector3d v = submesh.SubMesh.GetVertex(vid); v += 0.1 * n; v = SceneTransforms.TransformTo(v, TargetModelSO, this); submesh.SubMesh.SetVertex(vid, v); } previewGO = GameObjectFactory.CreateMeshGO("patch", new fMesh(submesh.SubMesh), false, true); previewGO.SetMaterial(previewMaterial, true); previewGO.SetLayer(FPlatform.WidgetOverlayLayer); previewGO.SetIgnoreMaterialChanges(); AppendNewGO(previewGO, root, false); } }
public bool Insert() { Func <int, bool> is_contained_v = (vid) => { Vector3d v = Mesh.GetVertex(vid); Vector2f vf2 = ProjectFrame.ToPlaneUV((Vector3f)v, 2); return(Polygon.Contains(vf2)); }; MeshVertexSelection vertexROI = new MeshVertexSelection(Mesh); Index3i seedT = Mesh.GetTriangle(SeedTriangle); // if a seed vert of seed triangle is containd in polygon, we will // flood-fill out from there, this gives a better ROI. // If not, we will try flood-fill from the seed triangles. List <int> seed_verts = new List <int>(); for (int j = 0; j < 3; ++j) { if (is_contained_v(seedT[j])) { seed_verts.Add(seedT[j]); } } if (seed_verts.Count == 0) { seed_verts.Add(seedT.a); seed_verts.Add(seedT.b); seed_verts.Add(seedT.c); } // flood-fill out from seed vertices until we have found all vertices // contained in polygon vertexROI.FloodFill(seed_verts.ToArray(), is_contained_v); // convert vertex ROI to face ROI MeshFaceSelection faceROI = new MeshFaceSelection(Mesh, vertexROI, 1); faceROI.ExpandToOneRingNeighbours(); faceROI.FillEars(true); // this might be a good idea... // construct submesh RegionOperator regionOp = new RegionOperator(Mesh, faceROI); DSubmesh3 roiSubmesh = regionOp.Region; DMesh3 roiMesh = roiSubmesh.SubMesh; // save 3D positions of unmodified mesh Vector3d[] initialPositions = new Vector3d[roiMesh.MaxVertexID]; // map roi mesh to plane MeshTransforms.PerVertexTransform(roiMesh, roiMesh.VertexIndices(), (v, vid) => { Vector2f uv = ProjectFrame.ToPlaneUV((Vector3f)v, 2); initialPositions[vid] = v; return(new Vector3d(uv.x, uv.y, 0)); }); // save a copy of 2D mesh and construct bvtree. we will use // this later to project back to 3d // [TODO] can we use a better spatial DS here, that takes advantage of 2D? DMesh3 projectMesh = new DMesh3(roiMesh); DMeshAABBTree3 projecter = new DMeshAABBTree3(projectMesh, true); MeshInsertUVPolyCurve insertUV = new MeshInsertUVPolyCurve(roiMesh, Polygon); //insertUV.Validate() bool bOK = insertUV.Apply(); if (!bOK) { throw new Exception("insertUV.Apply() failed"); } if (SimplifyInsertion) { insertUV.Simplify(); } int[] insertedPolyVerts = insertUV.CurveVertices; // grab inserted loop, assuming it worked EdgeLoop insertedLoop = null; if (insertUV.Loops.Count == 1) { insertedLoop = insertUV.Loops[0]; } // find interior triangles List <int> interiorT = new List <int>(); foreach (int tid in roiMesh.TriangleIndices()) { Vector3d centroid = roiMesh.GetTriCentroid(tid); if (Polygon.Contains(centroid.xy)) { interiorT.Add(tid); } } if (RemovePolygonInterior) { MeshEditor editor = new MeshEditor(roiMesh); editor.RemoveTriangles(interiorT, true); InteriorTriangles = null; } else { InteriorTriangles = interiorT.ToArray(); } // map back to 3d Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero; foreach (int vid in roiMesh.VertexIndices()) { // [TODO] somehow re-use exact positions from regionOp maps? // construct new 3D pos w/ barycentric interpolation Vector3d v = roiMesh.GetVertex(vid); int tid = projecter.FindNearestTriangle(v); Index3i tri = projectMesh.GetTriangle(tid); projectMesh.GetTriVertices(tid, ref a, ref b, ref c); Vector3d bary = MathUtil.BarycentricCoords(ref v, ref a, ref b, ref c); Vector3d pos = bary.x * initialPositions[tri.a] + bary.y * initialPositions[tri.b] + bary.z * initialPositions[tri.c]; roiMesh.SetVertex(vid, pos); } bOK = BackPropagate(regionOp, insertedPolyVerts, insertedLoop); return(bOK); }
public override DMesh3 Cut(CuttingInfo info) { var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum); if (painted.Count <= 0) { return(info.mesh); } var components = FindConnectedComponents(info, painted); var subMeshes = new List <DMesh3>(); foreach (var component in components.Components) { DSubmesh3 subMesh = new DSubmesh3(info.mesh, component.Indices); var newMesh = subMesh.SubMesh; newMesh.EnableTriangleGroups(); newMesh.EnableVertexColors(ColorManager.Instance.GetColorForId(info.data.ColorNum).toVector3f()); foreach (var componentIndex in component.Indices) { info.mesh.RemoveTriangle(componentIndex); } var loops = new MeshBoundaryLoops(newMesh, true); var meshEditorNewMesh = new MeshEditor(newMesh); var meshEditorOldMesh = new MeshEditor(info.mesh); foreach (var meshBoundaryLoop in loops) { var offsettedVerticesNewMesh = new List <int>(); var offsettedVerticesOldMesh = new List <int>(); foreach (var vertex in meshBoundaryLoop.Vertices) { var normal = newMesh.GetVertexNormal(vertex); var vertextPosition = newMesh.GetVertex(vertex); var newVertex = newMesh.AppendVertex(vertextPosition - normal.toVector3d() * info.data.depth); offsettedVerticesNewMesh.Add(newVertex); var newVertexOldMesh = info.mesh.AppendVertex(vertextPosition - normal.toVector3d() * info.data.depth); offsettedVerticesOldMesh.Add(newVertexOldMesh); } var boundaryLoopOfOldMesh = meshBoundaryLoop.Vertices .Select(subv => subMesh.MapVertexToBaseMesh(subv)).ToArray(); var loopNewMesh = meshEditorNewMesh.StitchLoop(meshBoundaryLoop.Vertices, offsettedVerticesNewMesh.ToArray(), info.data.ColorNum); var loopOldMesh = meshEditorOldMesh.StitchLoop(boundaryLoopOfOldMesh, offsettedVerticesOldMesh.ToArray(), info.data.mainColorId); for (var index = 0; index < offsettedVerticesNewMesh.Count; index++) { info.PointToPoint.Add(offsettedVerticesNewMesh[index], offsettedVerticesOldMesh[index]); } var offsettedLoop = EdgeLoop.FromVertices(newMesh, offsettedVerticesNewMesh); var holeFiller = new SimpleHoleFiller(newMesh, offsettedLoop); var valid = holeFiller.Validate(); if (valid == ValidationStatus.Ok) { var res = holeFiller.Fill(info.data.ColorNum); if (res) { var newVertex = holeFiller.NewVertex; var newTriangles = holeFiller.NewTriangles; //Add the same triangles to old mesh. if (newVertex == -1) //case where it added only one tri { var vertices = newMesh.GetTriangle(newTriangles.First()); var edgeAOldMesh = info.PointToPoint[vertices.a]; var edgeBOldMesh = info.PointToPoint[vertices.b]; var edgeCOldMesh = info.PointToPoint[vertices.c]; info.mesh.AppendTriangle(edgeAOldMesh, edgeCOldMesh, edgeBOldMesh, info.data.mainColorId); } else //case where multiple tris and a middle vertex were added { var newVertexOldMesh = info.mesh.AppendVertex(newMesh.GetVertex(newVertex)); foreach (var newTriangle in newTriangles) { //the center is always the first vertex in newTriangle var edgeVertices = newMesh.GetTriangle(newTriangle); var edgeBOldMesh = info.PointToPoint[edgeVertices.b]; var edgeCOldMesh = info.PointToPoint[edgeVertices.c]; info.mesh.AppendTriangle(newVertexOldMesh, edgeCOldMesh, edgeBOldMesh, info.data.mainColorId); } if (info.PointToPoint.ContainsKey(newVertex)) { Debug.Log($"Double insertion from HF: {newVertex}, {newVertexOldMesh}"); } else { info.PointToPoint.Add(newVertex, newVertexOldMesh); } } } } } subMeshes.Add(newMesh); } InstantiateNewObjects(info, subMeshes); return(info.mesh); }
public override DMesh3 Cut(CuttingInfo info) { var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum); if (painted.Count <= 0) { return(info.mesh); } var components = new MeshConnectedComponents(info.mesh); components.FilterF = i => info.mesh.GetTriangleGroup(i) == info.data.ColorNum; components.FindConnectedT(); var subMeshes = new List <DMesh3>(); foreach (var component in components) { DSubmesh3 subMesh = new DSubmesh3(info.mesh, component.Indices); var newMesh = subMesh.SubMesh; newMesh.EnableTriangleGroups(); var normals = new List <Vector3d>(); var vertices = new List <Vector3d>(); foreach (var componentTriIndex in component.Indices) { var tri = info.mesh.GetTriangle(componentTriIndex); var normal = info.mesh.GetTriNormal(componentTriIndex); normals.Add(normal); var orgA = info.mesh.GetVertex(tri.a); vertices.Add(orgA); var orgB = info.mesh.GetVertex(tri.b); vertices.Add(orgB); var orgC = info.mesh.GetVertex(tri.c); vertices.Add(orgC); } var avgNormal = normals.Average(); var avgVertices = vertices.Average(); var newPoint = avgVertices - avgNormal * info.data.depth; if (info.data.modifier == CutSettingData.Modifier.Compute) { newPoint = MovePointInsideAndAwayFromShell(info, newPoint); } if (info.data.modifier == CutSettingData.Modifier.DepthDependant) { newPoint = MovePointDepthDependant(info, avgVertices, avgNormal); } component.Indices.ToList().ForEach(index => info.mesh.RemoveTriangle(index)); var newPointId = newMesh.AppendVertex(newPoint); var newPointIdInOldMesh = info.mesh.AppendVertex(newPoint); info.PointToPoint.Add(newPointId, newPointIdInOldMesh); var eidsNewMesh = newMesh.BoundaryEdgeIndices().ToList(); foreach (var openEdge in eidsNewMesh) { var edge = newMesh.GetOrientedBoundaryEdgeV(openEdge); newMesh.AppendTriangle(edge.b, edge.a, newPointId, info.data.ColorNum); info.mesh.AppendTriangle(subMesh.MapVertexToBaseMesh(edge.a), subMesh.MapVertexToBaseMesh(edge.b), newPointIdInOldMesh, ColorManager.Instance.MainColorId); } subMeshes.Add(newMesh); } InstantiateNewObjects(info, subMeshes); return(info.mesh); }
public override DMesh3 Cut(CuttingInfo info) { var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum); if (painted.Count <= 0) { return(info.mesh); } var components = FindConnectedComponents(info, painted); var subMeshes = new List <DMesh3>(); foreach (var component in components.Components) { DSubmesh3 subMesh = new DSubmesh3(info.mesh, new List <int>()); //component.Indices var newMesh = subMesh.SubMesh; newMesh.EnableTriangleGroups(); newMesh.EnableVertexColors(ColorManager.Instance.GetColorForId(info.data.ColorNum).toVector3f()); var stati = new Dictionary <int, PeprStatusVert>(); foreach (var triIndex in component.Indices) { var triangle = info.mesh.GetTriangle(triIndex); var vertex1 = info.mesh.GetVertex(triangle.a); var vertex2 = info.mesh.GetVertex(triangle.b); var vertex3 = info.mesh.GetVertex(triangle.c); stati.AppendIfNotExists(triangle.a); stati.AppendIfNotExists(triangle.b); stati.AppendIfNotExists(triangle.c); if (stati[triangle.a].idNewMeshOuter == null) { stati[triangle.a].idNewMeshOuter = newMesh.AppendVertex(vertex1); } if (stati[triangle.b].idNewMeshOuter == null) { stati[triangle.b].idNewMeshOuter = newMesh.AppendVertex(vertex2); } if (stati[triangle.c].idNewMeshOuter == null) { stati[triangle.c].idNewMeshOuter = newMesh.AppendVertex(vertex3); } var newTriOuter = newMesh.AppendTriangle(stati[triangle.a].idNewMeshOuter.Value, stati[triangle.b].idNewMeshOuter.Value, stati[triangle.c].idNewMeshOuter.Value, info.data.ColorNum); //normals TriAvgNormal var normal1 = info.mesh.GetVertexNormal(triangle.a).toVector3d(); var normal2 = info.mesh.GetVertexNormal(triangle.b).toVector3d(); var normal3 = info.mesh.GetVertexNormal(triangle.c).toVector3d(); if (info.data.modifier == CutSettingData.Modifier.StraightNormals) { if (info.mesh.IsGroupBoundaryVertex(triangle.a)) { normal1 = info.mesh.GetTriNormal(triIndex); } if (info.mesh.IsGroupBoundaryVertex(triangle.b)) { normal2 = info.mesh.GetTriNormal(triIndex); } if (info.mesh.IsGroupBoundaryVertex(triangle.c)) { normal3 = info.mesh.GetTriNormal(triIndex); } } if (info.data.modifier == CutSettingData.Modifier.AveragedNormals) { normal1 = GetAveragedNormal(info.mesh, triangle.a); normal2 = GetAveragedNormal(info.mesh, triangle.b); normal3 = GetAveragedNormal(info.mesh, triangle.c); } var pos1 = vertex1 - normal1 * info.data.depth; var pos2 = vertex2 - normal2 * info.data.depth; var pos3 = vertex3 - normal3 * info.data.depth; if (stati[triangle.a].idNewMeshInner == null) { stati[triangle.a].idNewMeshInner = newMesh.AppendVertex(pos1); } if (stati[triangle.b].idNewMeshInner == null) { stati[triangle.b].idNewMeshInner = newMesh.AppendVertex(pos2); } if (stati[triangle.c].idNewMeshInner == null) { stati[triangle.c].idNewMeshInner = newMesh.AppendVertex( pos3); //TODO it was flipped from here before, but we have to flip when making the triangles } if (stati[triangle.a].idOldMeshInner == null) { stati[triangle.a].idOldMeshInner = info.mesh.AppendVertex(pos1); } if (stati[triangle.b].idOldMeshInner == null) { stati[triangle.b].idOldMeshInner = info.mesh.AppendVertex(pos2); } if (stati[triangle.c].idOldMeshInner == null) { stati[triangle.c].idOldMeshInner = info.mesh.AppendVertex(pos3); } var color = ColorManager.Instance.GetColorForId(info.data.ColorNum).toVector3f(); newMesh.SetVertexColor(stati[triangle.a].idNewMeshInner.Value, color); newMesh.SetVertexColor(stati[triangle.b].idNewMeshInner.Value, color); newMesh.SetVertexColor(stati[triangle.c].idNewMeshInner.Value, color); info.mesh.SetVertexColor(stati[triangle.a].idOldMeshInner.Value, color); info.mesh.SetVertexColor(stati[triangle.b].idOldMeshInner.Value, color); info.mesh.SetVertexColor(stati[triangle.c].idOldMeshInner.Value, color); var newTriInner = newMesh.AppendTriangle(stati[triangle.a].idNewMeshInner.Value, stati[triangle.c].idNewMeshInner.Value, stati[triangle.b].idNewMeshInner.Value, info.data.ColorNum); var newTriInnerOldMesh = info.mesh.AppendTriangle(stati[triangle.a].idOldMeshInner.Value, stati[triangle.b].idOldMeshInner.Value, stati[triangle.c].idOldMeshInner.Value, info.data.mainColorId); } if (info.data.modifier == CutSettingData.Modifier.DepthDependant) { MoveAllPointsDepthDependant(info, newMesh, stati); } if (info.data.modifier == CutSettingData.Modifier.Compute) { MoveVerticesToValidPositions(info, newMesh, stati); } component.Indices.ToList().ForEach(index => info.mesh.RemoveTriangle(index)); var openEdges = newMesh.BoundaryEdgeIndices(); foreach (var openEdge in openEdges) { var edgeOriented = newMesh.GetOrientedBoundaryEdgeV(openEdge); int thirdPoint = Corresponding(stati, edgeOriented.a, true); var newTriSide = newMesh.AppendTriangle(edgeOriented.b, edgeOriented.a, thirdPoint, info.data.ColorNum); } var openEdgesOldMesh = info.mesh.BoundaryEdgeIndices(); foreach (var openEdge in openEdgesOldMesh) { var edgeOriented = info.mesh.GetOrientedBoundaryEdgeV(openEdge); int thirdPoint = Corresponding(stati, edgeOriented.a, false); var newTriSide = info.mesh.AppendTriangle(edgeOriented.b, edgeOriented.a, thirdPoint, info.data.mainColorId); } foreach (var peprStatusVert in stati) { if (info.PointToPoint.ContainsKey(peprStatusVert.Value.idNewMeshInner.Value)) { Debug.Log( $"Double insertion from BS: {peprStatusVert.Value.idNewMeshInner.Value}, {peprStatusVert.Value.idOldMeshInner.Value}"); } else { info.PointToPoint.Add(peprStatusVert.Value.idNewMeshInner.Value, peprStatusVert.Value.idOldMeshInner.Value); } } subMeshes.Add(newMesh); } InstantiateNewObjects(info, subMeshes); return(info.mesh); }
public override DMesh3 Cut(CuttingInfo info) { var painted = FindPaintedTriangles(info.mesh, info.data.ColorNum); if (painted.Count <= 0) { return(info.mesh); } var components = FindConnectedComponents(info, painted); var subMeshes = new List <DMesh3>(); foreach (var component in components.Components) { DSubmesh3 subMesh = new DSubmesh3(info.mesh, component.Indices); var newMesh = subMesh.SubMesh; newMesh.EnableTriangleGroups(); newMesh.EnableVertexColors(ColorManager.Instance.GetColorForId(info.data.ColorNum).toVector3f()); foreach (var componentIndex in component.Indices) { info.mesh.RemoveTriangle(componentIndex); } var loops = new MeshBoundaryLoops(newMesh, true); foreach (var meshBoundaryLoop in loops) { var holeFiller = new SimpleHoleFiller(newMesh, meshBoundaryLoop); var valid = holeFiller.Validate(); if (valid == ValidationStatus.Ok) { var res = holeFiller.Fill(info.data.ColorNum); if (res) { var newVertex = holeFiller.NewVertex; var newTriangles = holeFiller.NewTriangles; //Add the same triangles to old mesh. if (newVertex == -1) //case where it added only one tri { var vertices = newMesh.GetTriangle(newTriangles.First()); var edgeAOldMesh = subMesh.MapVertexToBaseMesh(vertices.a); var edgeBOldMesh = subMesh.MapVertexToBaseMesh(vertices.b); var edgeCOldMesh = subMesh.MapVertexToBaseMesh(vertices.c); info.mesh.AppendTriangle(edgeAOldMesh, edgeCOldMesh, edgeBOldMesh, info.data.mainColorId); } else //case where multiple tris and a middle vertex were added { var newVertexOldMesh = info.mesh.AppendVertex(newMesh.GetVertex(newVertex)); foreach (var newTriangle in newTriangles) { //the center is always the first vertex in newTriangle var edgeVertices = newMesh.GetTriangle(newTriangle); var edgeBOldMesh = subMesh.MapVertexToBaseMesh(edgeVertices.b); var edgeCOldMesh = subMesh.MapVertexToBaseMesh(edgeVertices.c); info.mesh.AppendTriangle(newVertexOldMesh, edgeCOldMesh, edgeBOldMesh, info.data.mainColorId); } if (info.PointToPoint.ContainsKey(newVertex)) { Debug.Log($"Double insertion from HF: {newVertex}, {newVertexOldMesh}"); } else { info.PointToPoint.Add(newVertex, newVertexOldMesh); } } } } } subMeshes.Add(newMesh); } InstantiateNewObjects(info, subMeshes); return(info.mesh); }