public List <MeshPart> PolySeparate() { List <Tuple <HashSet <int>, HashSet <Vector3>, List <int> > > list = new List <Tuple <HashSet <int>, HashSet <Vector3>, List <int> > >(); AssertMatchingCounts(); // create index groups for (int i = 0; i < indices.Count; i += 3) { int i0 = indices[i], i1 = indices[i + 1], i2 = indices[i + 2]; Vector3 v0 = vertices[i0], v1 = vertices[i1], v2 = vertices[i2]; AddIndices(list, vertices, v0, v1, v2, i0, i1, i2, false, false, false); } return(list.ConvertAll(set => { MeshPart part = new MeshPart(side); foreach (int ind in set.Item3) { if (part.indexMap.ContainsKey(ind)) { part.indices.Add(part.indexMap[ind]); } else { part.indexMap.Add(ind, part.vertices.Count); part.indices.Add(part.vertices.Count); part.vertices.Add(vertices[ind]); if (uvs.Count > 0) { part.uvs.Add(uvs[ind]); } if (normals.Count > 0) { part.normals.Add(normals[ind]); } } } return part; })); }
public static CutResult Run( GameObject target, CuttingTemplate template ) { Mesh mesh = target.GetComponent <MeshFilter>().mesh; MeshPart pos = new MeshPart(true), neg = new MeshPart(false); RingGenerator intersection_ring = new RingGenerator(); Vector3[] vertices = mesh.vertices; Vector3[] normals = mesh.normals; int[] triangles = mesh.triangles; bool addNormals = normals.Length > 0; // Debug.Log(vertices.Length + " verts, " + triangles.Length / 3 + " tris, " + template.points.Count + "tpl. points"); // divide mesh in half by vertices int i = 0; foreach (Vector3 vertex in vertices) { if (template.IsAbove(vertex)) { pos.indexMap.Add(i, pos.vertices.Count); pos.vertices.Add(vertex); if (addNormals) { pos.normals.Add(normals[i]); } } else { neg.indexMap.Add(i, neg.vertices.Count); neg.vertices.Add(vertex); if (addNormals) { neg.normals.Add(normals[i]); } } i++; } // RingGen's are associated to first template point // these define the rings used for generating strip-wise inner geometry // SortedDictionary are for connecting edges of strip-wise inner geometry // Sorted by signed distance to template plane, so they get connected in the right order Dictionary <Vector3, Tuple <SortedDictionary <float, Vector3>, RingGen> > point_data = new Dictionary <Vector3, Tuple <SortedDictionary <float, Vector3>, RingGen> >(); foreach (Vector3 point in template.points) { point_data.Add(point, new Tuple <SortedDictionary <float, Vector3>, RingGen>(new SortedDictionary <float, Vector3>(), new RingGen())); } IvSaver ivSaver = new IvSaver(); // put triangles in correct mesh for (i = 0; i < triangles.Length; i += 3) { // find orignal indices int i_a = triangles[i], i_b = triangles[i + 1], i_c = triangles[i + 2]; // find original verticies Vector3 a = vertices[i_a], b = vertices[i_b], c = vertices[i_c]; // seperation check if (!ProcessTriangle(template, a, b, c, point_data, pos, neg, intersection_ring, ivSaver, addNormals)) { if (template.IsAbove(a)) { // triangle above plane pos.indices.Add(pos.indexMap[i_a]); pos.indices.Add(pos.indexMap[i_b]); pos.indices.Add(pos.indexMap[i_c]); } else { // triangle below plane neg.indices.Add(neg.indexMap[i_a]); neg.indices.Add(neg.indexMap[i_b]); neg.indices.Add(neg.indexMap[i_c]); } } } // Generate strip (inner) geometry SortedDictionary <float, Vector3> next_dist_map = point_data[template.points[template.isClosed ? 0 : template.points.Count - 1]].Item1; Vector3 next_point = template.isClosed ? template.points.First() : template.points.Last(); for (i = template.points.Count - (template.isClosed ? 1 : 2); i >= 0; i--) { SortedDictionary <float, Vector3> dist_map = point_data[template.points[i]].Item1; Vector3 point = template.points[i]; //Debugging.LogLine(template.points[i-1],template.normal); //Debugging.LogLine(template.points[i],template.normal); //Debugging.LogList(point_data[template.points[i-1]].Item1.Keys); //Debugging.LogList(point_data[template.points[i]].Item1.Keys); RingGen rg = point_data[template.points[i]].Item2; // Debug.Log("points: "+point+" -> "+next_point); //rg.MyDebugLog(); rg.TemplateJoin(template, dist_map); rg.TemplateJoin(template, next_dist_map); //rg.MyDebugLog(); foreach (Ring ring in rg.GetRings(false, false)) { //Debugging.DebugRing(ring.verts); Vector3 normal = Vector3.Cross(template.normal, next_point - point); GenerateRingMesh(ring.verts, pos, normal, true, null, addNormals); //TmpGen(ring.verts,pos,normal); GenerateRingMesh(ring.verts, neg, normal, false, null, addNormals); //TmpGen(ring.verts,neg,normal,true); } // Debug.Log("---------------------------"); next_dist_map = dist_map; next_point = point; } // Debug.Log("template: "+template); return(new CutResult(target, Vector3.zero, intersection_ring.GetRings(false, false), pos, neg)); }
public List <MeshPart> PartialPolySeparate( CuttingPlane plane, HashSet <Vector3> allow_cut ) { //Debug.Log("allow_cut"); //foreach (Vector3 v in allow_cut) Debug.Log(VecStr(v)); //Debug.Log("missing"); List <Tuple <HashSet <int>, HashSet <Vector3>, List <int> > > list = new List <Tuple <HashSet <int>, HashSet <Vector3>, List <int> > >(); AssertMatchingCounts(); // create index groups for (int i = 0; i < indices.Count; i += 3) { int i0 = indices[i], i1 = indices[i + 1], i2 = indices[i + 2]; Vector3 v0 = vertices[i0], v1 = vertices[i1], v2 = vertices[i2]; bool ci0 = allow_cut.Contains(v0), ci1 = allow_cut.Contains(v1), ci2 = allow_cut.Contains(v2); AddIndices(list, vertices, v0, v1, v2, i0, i1, i2, ci0, ci1, ci2); } return(list.ConvertAll(set => { MeshPart part = new MeshPart(false); int doSwap = 0, doStay = 0; // temporary solution (?) foreach (int ind in set.Item3) { Vector3 point = vertices[ind]; if (!allow_cut.Contains(point)) { if (plane.IsAbove(point)) { doSwap++; } else { doStay++; } } ; if (part.indexMap.ContainsKey(ind)) { part.indices.Add(part.indexMap[ind]); } else { part.indexMap.Add(ind, part.vertices.Count); part.indices.Add(part.vertices.Count); part.vertices.Add(point); if (uvs.Count > 0) { part.uvs.Add(uvs[ind]); } } } if (doSwap > doStay) { part.SwapSide(); } part.AssertMatchingCounts(); return part; })); }
public CutObj(MeshPart part, CutResult res, bool isPolySeperated) : base(part, res, isPolySeperated) { }
private static bool ProcessTriangle( CuttingTemplate template, Vector3 a, Vector3 b, Vector3 c, Dictionary <Vector3, Tuple <SortedDictionary <float, Vector3>, RingGen> > point_data, MeshPart pos, MeshPart neg, RingGenerator intersection_ring, IvSaver ivSaver, bool addNormals ) { List <Vector3> points = template.points; Vector3 normal = template.normal; RingGenerator tri_rings = new RingGenerator(); Vector3 tri_nor = -Vector3.Cross(c - a, c - b).normalized; if (tri_nor == Vector3.zero) { // Debug.LogWarning("zero area tri"); return(true); } bool partToUse = Vector3.Dot(tri_nor, normal) > 0; bool ring_dir = partToUse; MUPlane tri_plane = new MUPlane(tri_nor, a); Vector3 ab_nor = Vector3.Cross(tri_nor, (b - a).normalized), // vector perpendicular to edge and pointing away from triangle bc_nor = Vector3.Cross(tri_nor, (c - b).normalized), ca_nor = Vector3.Cross(tri_nor, (a - c).normalized); #if EPSILON_TEST if (Math.Abs(Vector3.Dot(tri_nor, normal)) < 0.01f) { Debug.LogWarning("epsilon ignore"); return(false); } #endif // Debug.Log("tri verts: "+a+" "+b+" "+c); // Debug.Log((b-a).magnitude+" "+(c-b).magnitude+" "+(a-c).magnitude); // Debug.Log("edge norms: "+ab_nor+" "+bc_nor+" "+ca_nor); Dictionary <float, Vector3> map_ab = new Dictionary <float, Vector3>(), // distance (from first vertex in edge) -> vertex in edge map_bc = new Dictionary <float, Vector3>(), // used for sorting... TODO: change to SortedDictionary map_ca = new Dictionary <float, Vector3>(); HashSet <Vector3> exiting_ivs = new HashSet <Vector3>(); MUPlane ab = new MUPlane(ab_nor, a), // planes passing through edges, perpendicular to triangle bc = new MUPlane(bc_nor, b), ca = new MUPlane(ca_nor, c); // Debug.Log("tri: "+tri_plane+" "+normal); //Debugging.DebugRing(points); Vector3 opi, old_point; bool oaab, oabc, oaca; SortedDictionary <float, Vector3> dist_map_old; RingGen strip_rings; if (template.isClosed) { opi = tri_plane.DirectionalProject(points.Last(), normal); old_point = points.Last(); oaab = ab.IsAbove(opi); oabc = bc.IsAbove(opi); oaca = ca.IsAbove(opi); dist_map_old = point_data[points.Last()].Item1; strip_rings = point_data[points.Last()].Item2; } else { opi = tri_plane.DirectionalProject(points.First(), normal); old_point = points.First(); oaab = ab.IsAbove(opi); oabc = bc.IsAbove(opi); oaca = ca.IsAbove(opi); dist_map_old = point_data[points.First()].Item1; strip_rings = point_data[points.First()].Item2; } bool oldInside = !(oaab || oabc || oaca); for (int i = template.isClosed ? 0 : 1; i < points.Count; i++) { Vector3 cur_point = points[i]; SortedDictionary <float, Vector3> dist_map = point_data[cur_point].Item1; Vector3 pi = tri_plane.DirectionalProject(cur_point, normal); // Debug.Log(opi+" => "+pi); bool aab = ab.IsAbove(pi), // above means outside edge abc = bc.IsAbove(pi), aca = ca.IsAbove(pi); bool inside = !(aab || abc || aca); // Debug.Log(aab+" "+abc+" "+aca+" | "+oaab+" "+oabc+" "+oaca); if (inside != oldInside) { // Debug.Log("EDGE PAIR: "+a+" "+b+" "+c+" pi-opi: "+pi+" "+opi+" hc: "+pi.GetHashCode()+" "+opi.GetHashCode()); // add edge pair MUPlane edge_plane; Dictionary <float, Vector3> map; Vector3 ep1, ep2, iv; if (inside) { if (oaab) { edge_plane = ab; map = map_ab; ep1 = a; ep2 = b; iv = ivSaver.Intersect(edge_plane, ep1, ep2, opi, pi, old_point, cur_point); if (Vector3.Dot(ep1 - iv, ep2 - iv) < 0) { goto connect; } } if (oabc) { edge_plane = bc; map = map_bc; ep1 = b; ep2 = c; iv = ivSaver.Intersect(edge_plane, ep1, ep2, opi, pi, old_point, cur_point); if (Vector3.Dot(ep1 - iv, ep2 - iv) < 0) { goto connect; } } if (oaca) { edge_plane = ca; map = map_ca; ep1 = c; ep2 = a; iv = ivSaver.Intersect(edge_plane, ep1, ep2, opi, pi, old_point, cur_point); if (Vector3.Dot(ep1 - iv, ep2 - iv) < 0) { goto connect; } } throw MeshUtilsException.Internal("Point on neither side of triangle"); connect: // Debug.Log("EDGE PAIR: "+iv+" "+pi+" hc: "+iv.GetHashCode()+" "+pi.GetHashCode()); float dist_key = template.plane.SignedDistance(pi); if (!dist_map.ContainsKey(dist_key)) { dist_map.Add(dist_key, pi); } map.Add((ep1 - iv).magnitude, iv); strip_rings.AddConnected(ring_dir?iv:pi, ring_dir?pi:iv); intersection_ring.AddConnected(ring_dir?iv:pi, ring_dir?pi:iv); tri_rings.AddConnected(iv, pi); } else { // Debug.Log(a+" "+b+" "+c+" "+template_plane); // Debug.Log(pa+" "+pb+" "+pc+" "+points[i-1]+" "+points[i]); if (aab) { edge_plane = ab; map = map_ab; ep1 = a; ep2 = b; iv = ivSaver.Intersect(edge_plane, ep1, ep2, opi, pi, old_point, cur_point); if (Vector3.Dot(ep1 - iv, ep2 - iv) < 0) { goto connect; } } if (abc) { edge_plane = bc; map = map_bc; ep1 = b; ep2 = c; iv = ivSaver.Intersect(edge_plane, ep1, ep2, opi, pi, old_point, cur_point); if (Vector3.Dot(ep1 - iv, ep2 - iv) < 0) { goto connect; } } if (aca) { edge_plane = ca; map = map_ca; ep1 = c; ep2 = a; iv = ivSaver.Intersect(edge_plane, ep1, ep2, opi, pi, old_point, cur_point); if (Vector3.Dot(ep1 - iv, ep2 - iv) < 0) { goto connect; } } throw MeshUtilsException.Internal("Point on neither side of triangle"); connect: // Debug.Log("EDGE PAIR: "+iv+" "+opi+" hc: "+iv.GetHashCode()+" "+opi.GetHashCode()); float dist_key = template.plane.SignedDistance(opi); if (!dist_map_old.ContainsKey(dist_key)) { dist_map_old.Add(dist_key, opi); } map.Add((ep1 - iv).magnitude, iv); strip_rings.AddConnected(ring_dir?opi:iv, ring_dir?iv:opi); intersection_ring.AddConnected(ring_dir?opi:iv, ring_dir?iv:opi); tri_rings.AddConnected(opi, iv); exiting_ivs.Add(iv); } } else if (inside) { // add inner pair // Debug.Log("INNER PAIR: "+a+" "+b+" "+c+" pi-opi: "+pi+" "+opi+" hc: "+pi.GetHashCode()+" "+opi.GetHashCode()); // Debug.Log("INNER PAIR: "+pi+" "+opi+" hc: "+pi.GetHashCode()+" "+opi.GetHashCode()); strip_rings.AddConnected(ring_dir?opi:pi, ring_dir?pi:opi); intersection_ring.AddConnected(ring_dir?opi:pi, ring_dir?pi:opi); tri_rings.AddConnected(opi, pi); float dist_key = template.plane.SignedDistance(pi); if (!dist_map.ContainsKey(dist_key)) { dist_map.Add(dist_key, pi); } dist_key = template.plane.SignedDistance(opi); if (!dist_map_old.ContainsKey(dist_key)) { dist_map_old.Add(dist_key, opi); } } else { // add outer pair if ( // test to check if edge does not cross triangle (aab && oaab) || (abc && oabc) || (aca && oaca) ) { goto continue_for; } // crosses triangle if (!aab && !oaab) // not ab { Vector3 iv0 = ivSaver.Intersect(bc, b, c, opi, pi, old_point, cur_point), iv1 = ivSaver.Intersect(ca, c, a, opi, pi, old_point, cur_point); if ( Vector3.Dot(b - iv0, c - iv0) > 0 || Vector3.Dot(c - iv1, a - iv1) > 0 ) { goto continue_for; } // Debug.Log("OUTER PAIR: "+iv0+" "+iv1+" hc: "+iv0.GetHashCode()+" "+iv1.GetHashCode()); map_bc.Add((b - iv0).magnitude, iv0); map_ca.Add((c - iv1).magnitude, iv1); bool iv0_first = (iv0 - opi).magnitude < (iv1 - opi).magnitude; exiting_ivs.Add(iv0_first?iv1:iv0); tri_rings.AddConnected(iv0_first?iv0:iv1, iv0_first?iv1:iv0); strip_rings.AddConnected((!ring_dir ^ iv0_first)?iv0:iv1, (!ring_dir ^ iv0_first)?iv1:iv0); intersection_ring.AddConnected((!ring_dir ^ iv0_first)?iv0:iv1, (!ring_dir ^ iv0_first)?iv1:iv0); } else if (!abc && !oabc) // not bc { Vector3 iv0 = ivSaver.Intersect(ca, c, a, opi, pi, old_point, cur_point), iv1 = ivSaver.Intersect(ab, a, b, opi, pi, old_point, cur_point); if ( Vector3.Dot(c - iv0, a - iv0) > 0 || Vector3.Dot(a - iv1, b - iv1) > 0 ) { goto continue_for; } // Debug.Log("OUTER PAIR: "+iv0+" "+iv1+" hc: "+iv0.GetHashCode()+" "+iv1.GetHashCode()); map_ca.Add((c - iv0).magnitude, iv0); map_ab.Add((a - iv1).magnitude, iv1); bool iv0_first = (iv0 - opi).magnitude < (iv1 - opi).magnitude; exiting_ivs.Add(iv0_first?iv1:iv0); tri_rings.AddConnected(iv0_first?iv0:iv1, iv0_first?iv1:iv0); strip_rings.AddConnected((!ring_dir ^ iv0_first)?iv0:iv1, (!ring_dir ^ iv0_first)?iv1:iv0); intersection_ring.AddConnected((!ring_dir ^ iv0_first)?iv0:iv1, (!ring_dir ^ iv0_first)?iv1:iv0); } else if (!aca && !oaca) // not ca { Vector3 iv0 = ivSaver.Intersect(ab, a, b, opi, pi, old_point, cur_point), iv1 = ivSaver.Intersect(bc, b, c, opi, pi, old_point, cur_point); if ( Vector3.Dot(a - iv0, b - iv0) > 0 || Vector3.Dot(b - iv1, c - iv1) > 0 ) { goto continue_for; } // Debug.Log("OUTER PAIR: "+iv0+" "+iv1+" hc: "+iv0.GetHashCode()+" "+iv1.GetHashCode()); map_ab.Add((a - iv0).magnitude, iv0); map_bc.Add((b - iv1).magnitude, iv1); bool iv0_first = (iv0 - opi).magnitude < (iv1 - opi).magnitude; exiting_ivs.Add(iv0_first?iv1:iv0); tri_rings.AddConnected(iv0_first?iv0:iv1, iv0_first?iv1:iv0); strip_rings.AddConnected((!ring_dir ^ iv0_first)?iv0:iv1, (!ring_dir ^ iv0_first)?iv1:iv0); intersection_ring.AddConnected((!ring_dir ^ iv0_first)?iv0:iv1, (!ring_dir ^ iv0_first)?iv1:iv0); } else // opposite sides { MUPlane edge_plane, edge_plane1; Dictionary <float, Vector3> map, map1; Vector3 iv, iv1; // iv is entry, must be one side; iv1 is exit, can be either two other sides float iv_mag, iv1_mag; bool swap_ring_dir = false; if (aab && !abc && !aca || oaab && !oabc && !oaca) { // Debug.Log("1 swap "+oaab); if (oaab) { swap_ring_dir = true; } edge_plane = ab; map = map_ab; iv = ivSaver.Intersect(ab, a, b, opi, pi, old_point, cur_point); iv_mag = (a - iv).magnitude; if (Vector3.Dot(a - iv, b - iv) > 0) { goto continue_for; } iv1 = ivSaver.Intersect(bc, b, c, opi, pi, old_point, cur_point); if (Vector3.Dot(b - iv1, c - iv1) > 0) { iv1 = ivSaver.Intersect(ca, c, a, opi, pi, old_point, cur_point); iv1_mag = (c - iv1).magnitude; edge_plane1 = ca; map1 = map_ca; } else { iv1_mag = (b - iv1).magnitude; edge_plane1 = bc; map1 = map_bc; } goto connect_opposite; } else if (!aab && abc && !aca || !oaab && oabc && !oaca) { // Debug.Log("2 swap "+oabc); if (oabc) { swap_ring_dir = true; } edge_plane = bc; map = map_bc; iv = ivSaver.Intersect(bc, b, c, opi, pi, old_point, cur_point); iv_mag = (b - iv).magnitude; if (Vector3.Dot(b - iv, c - iv) > 0) { goto continue_for; } iv1 = ivSaver.Intersect(ca, c, a, opi, pi, old_point, cur_point); if (Vector3.Dot(c - iv1, a - iv1) > 0) { iv1 = ivSaver.Intersect(ab, a, b, opi, pi, old_point, cur_point); iv1_mag = (a - iv1).magnitude; edge_plane1 = ab; map1 = map_ab; } else { iv1_mag = (c - iv1).magnitude; edge_plane1 = ca; map1 = map_ca; } goto connect_opposite; } else if (!aab && !abc && aca || !oaab && !oabc && oaca) { // Debug.Log("3 swap "+oaca); if (oaca) { swap_ring_dir = true; } edge_plane = ca; map = map_ca; iv = ivSaver.Intersect(ca, c, a, opi, pi, old_point, cur_point); iv_mag = (c - iv).magnitude; if (Vector3.Dot(c - iv, a - iv) > 0) { goto continue_for; } iv1 = ivSaver.Intersect(ab, a, b, opi, pi, old_point, cur_point); if (Vector3.Dot(a - iv1, b - iv1) > 0) { iv1 = ivSaver.Intersect(bc, b, c, opi, pi, old_point, cur_point); iv1_mag = (b - iv1).magnitude; edge_plane1 = bc; map1 = map_bc; } else { iv1_mag = (a - iv1).magnitude; edge_plane1 = ab; map1 = map_ab; } goto connect_opposite; } throw MeshUtilsException.Internal("Case exhaustion failed"); connect_opposite: // Debug.Log("CROSS PAIR: "+iv+" "+iv1+" hc: "+iv.GetHashCode()+" "+iv1.GetHashCode()); // Debug.Log("yay "+opi+" "+pi+" "+a+" "+b+" "+c+" "+aab+" "+abc+" "+aca+" "+oaab+" "+oabc+" "+oaca); map.Add(iv_mag, iv); map1.Add(iv1_mag, iv1); exiting_ivs.Add(swap_ring_dir?iv1:iv); // Debug.Log("exiting is "+(swap_ring_dir?iv1:iv)); strip_rings.AddConnected((!ring_dir ^ swap_ring_dir)?iv:iv1, (!ring_dir ^ swap_ring_dir)?iv1:iv); intersection_ring.AddConnected((!ring_dir ^ swap_ring_dir)?iv:iv1, (!ring_dir ^ swap_ring_dir)?iv1:iv); tri_rings.AddConnected(swap_ring_dir?iv:iv1, swap_ring_dir?iv1:iv); } } continue_for: old_point = cur_point; oldInside = inside; oaab = aab; oabc = abc; oaca = aca; opi = pi; dist_map_old = dist_map; strip_rings = point_data[points[i]].Item2; } if (tri_rings.HasComplete()) { #if !HANDLE_CLOSED throw MeshUtilsException.Internal("Template closed and within triangle not handled"); #else // Debug.Log("inside"); if (tri_rings.HasPartials()) { throw MeshUtilsException.Internal("Unexpected condition"); } List <Ring> ring = tri_rings.GetRings(false, false); if (ring.Count != 1) { throw MeshUtilsException.Internal("Multiple rings in single triangle"); } Debugging.DebugRing(ring[0].verts); GenerateRingMesh(ring[0].verts, partToUse?neg:pos, tri_nor, true, null, addNormals); var r0 = new Ring(new List <Vector3>() { a, b, c }); var r1 = ring[0]; r1.verts.Reverse(); // join indices int i0 = 0, i1 = 0; r0.RingDist(r1, ref i0, ref i1); // the inner ring should have the opposite direction // join by inserting r1 at i0 in r0 List <Vector3> res = new List <Vector3>(); res.AddRange(r0.verts.GetRange(0, i0 + 1)); // r0 from start to split res.AddRange(r1.verts.GetRange(i1, r1.verts.Count - i1)); // r1 from split to end res.AddRange(r1.verts.GetRange(0, i1 + 1)); // r1 from start to split res.AddRange(r0.verts.GetRange(i0, r0.verts.Count - i0)); // r0 from split to end // Debugging.DebugRing(res); GenerateRingMesh(res, partToUse?pos:neg, tri_nor, true, null, addNormals); return(true); #endif } if (!tri_rings.HasPartials()) { return(false); } // Debugging.DebugRing(points.ConvertAll(p=>tri_plane.DirectionalProject(p,normal))); // Debug.Log("tri verts:"+a+" "+b+" "+c); // tri_rings.MyDebugLog(); // Debug.Log("connecting ivs..."); RingGenerator inv_tri_rings = tri_rings.Duplicate(); // seperate rings needed for other MeshPart ConnectIVs(exiting_ivs, a, b, c, map_ab, map_ca, map_bc, tri_rings, inv_tri_rings); // connect edges around in triangle rings ConnectIVs(exiting_ivs, b, c, a, map_bc, map_ab, map_ca, tri_rings, inv_tri_rings); // one call per edge ConnectIVs(exiting_ivs, c, a, b, map_ca, map_bc, map_ab, tri_rings, inv_tri_rings); // Debug.Log("gen:"); //tri_rings.MyDebugLog(); foreach (var ring in tri_rings.GetRings(false, false)) { // Debugging.DebugRing(ring.verts); GenerateRingMesh(ring.verts, partToUse?neg:pos, tri_nor, true, null, addNormals); } // Debug.Log("gen inv:"); //inv_tri_rings.MyDebugLog(); foreach (var ring in inv_tri_rings.GetRings(false, false)) { //Debugging.DebugRing(ring.verts); ring.verts.Reverse(); GenerateRingMesh(ring.verts, partToUse?pos:neg, tri_nor, true, null, addNormals); } //Debug.Log("--------------- END PROCESS TRIANGLE"); return(true); }
public static void GenerateRingMesh( List <Vector3> ring, MeshPart part, Vector3 normal, bool side, Vector2?innerUV, bool addNormals = false ) { bool addUVs = innerUV != null; // List<List<Vector3>> reduceHist = new List<List<Vector3>>(); // reduceHist.Add(ring); int indStart = part.vertices.Count; part.vertices.AddRange(ring); if (addUVs || addNormals) { foreach (var _ in ring) { if (addUVs) { part.uvs.Add(innerUV.Value); } if (addNormals) { part.normals.Add(side ? -normal : normal); } } } List <Tuple <Vector3, int> > set = ring.ConvertAll(v => new Tuple <Vector3, int>(v, indStart++)); int i = -1, lsc = set.Count; bool didInf = false; while (set.Count > 1) { i++; if (i >= set.Count) { if (lsc == set.Count) { if (didInf) { // Debug.LogError("ear clipping failed"); // DebugRings(reduceHist); // Debug.Log("normal "+(normal*1000)); // DebugRing(set.ConvertAll(s=>s.Item1)); throw MeshUtilsException.Internal("Ear clipping failed"); } didInf = true; } else { didInf = false; } lsc = set.Count; } i %= set.Count; Tuple <Vector3, int> vi0 = set[i], vi1 = set[(i + 1) % set.Count], vi2 = set[(i + 2) % set.Count]; Vector3 d1 = vi1.Item1 - vi0.Item1; // i0 -> i1 Vector3 d2 = vi2.Item1 - vi0.Item1; // i0 -> i2 // we assume points do not lie in a line (see SimplifyRing) // if they do however, it's just a waste of vertices // check convex ear float conv = Vector3.Dot(Vector3.Cross(d1.normalized, d2.normalized), normal); if (conv > 0) { continue; } // check that there are no other vertices inside this triangle if (set.Exists(v => { // note: we must compare the vectors, otherwise coinciding vectors do not // register as being the same if (v.Item1 == vi0.Item1 || v.Item1 == vi1.Item1 || v.Item1 == vi2.Item1) { return(false); } return(VectorUtil.CheckIsInside(d1, d2, v.Item1 - vi0.Item1)); })) { continue; } // generate indices if (side) { part.indices.Add(vi0.Item2); part.indices.Add(vi1.Item2); part.indices.Add(vi2.Item2); } else { part.indices.Add(vi0.Item2); part.indices.Add(vi2.Item2); part.indices.Add(vi1.Item2); } // eliminate vertex set.RemoveAt((++i) % set.Count); i++; // reduceHist.Add(set.ConvertAll(s=>s.Item1)); } }
public FriendlyCutObj(MeshPart part, CutResult result, bool isPolySeperated) { this.part = part; this.cutResult = result; this.isPolySeperated = isPolySeperated; }
// ----------------------------------------------------- // Generate triangle mesh within possibly concave ring // ----------------------------------------------------- public static void GenerateRingMesh( Ring ring, MeshPart part, Vector3 normal, Vector2?innerUV, bool addNormals = false ) { GenerateRingMesh(ring.verts, part, normal, innerUV, addNormals); }
public static void GenerateRingMesh( List <Vector3> ring, MeshPart part, Vector3 normal, Vector2?innerUV, bool addNormals = false ) { GenerateRingMesh(ring, part, normal, part.side, innerUV, addNormals); }
public static CutResult Run( GameObject target, CuttingPlane plane, CutParams param ) { CuttingPlane cutting_plane = plane.ToLocalSpace(target.transform); Mesh mesh = target.GetComponent <MeshFilter>().mesh; MeshPart pos = new MeshPart(true), neg = new MeshPart(false); RingGenerator rings = new RingGenerator(); //DateTime start = DateTime.Now; Vector2[] uvs = mesh.uv; Vector3[] vertices = mesh.vertices; Vector3[] normals = mesh.normals; int[] triangles = mesh.triangles; bool addUVs = uvs.Length > 0 && param.innerTextureCoord != null, addNormals = normals.Length > 0; // divide mesh in half by vertices int i = 0; foreach (Vector3 vertex in vertices) { if (cutting_plane.IsAbove(vertex)) { pos.indexMap.Add(i, pos.vertices.Count); pos.vertices.Add(vertex); if (addUVs) { pos.uvs.Add(uvs[i]); } if (addNormals) { pos.normals.Add(normals[i]); } } else { neg.indexMap.Add(i, neg.vertices.Count); neg.vertices.Add(vertex); if (addUVs) { neg.uvs.Add(uvs[i]); } if (addNormals) { neg.normals.Add(normals[i]); } } i++; } //Debug.Log((DateTime.Now-start).TotalMilliseconds+" elapsed (1)"); //start = DateTime.Now; // if either vertex list is empty the knife plane didn't collide if (pos.vertices.Count == 0 || neg.vertices.Count == 0) { if (!param.allowSingleResult) { return(null); } } // put triangles in correct mesh for (i = 0; i < triangles.Length; i += 3) { // find orignal indices int i_a = triangles[i], i_b = triangles[i + 1], i_c = triangles[i + 2]; // find original verticies Vector3 a = vertices[i_a], b = vertices[i_b], c = vertices[i_c]; // find original uvs Vector2 txa, txb, txc; if (addUVs) { txa = uvs[i_a]; txb = uvs[i_b]; txc = uvs[i_c]; } else { txa = txb = txc = Vector3.zero; } // find original normals Vector3 na, nb, nc; if (addNormals) { na = normals[i_a]; nb = normals[i_b]; nc = normals[i_c]; } else { na = nb = nc = Vector3.zero; } // seperation check bool aAbove = cutting_plane.IsAbove(a), bAbove = cutting_plane.IsAbove(b), cAbove = cutting_plane.IsAbove(c); if (aAbove && bAbove && cAbove) { // triangle above plane pos.indices.Add(pos.indexMap[i_a]); pos.indices.Add(pos.indexMap[i_b]); pos.indices.Add(pos.indexMap[i_c]); } else if (!aAbove && !bAbove && !cAbove) { // triangle below plane neg.indices.Add(neg.indexMap[i_a]); neg.indices.Add(neg.indexMap[i_b]); neg.indices.Add(neg.indexMap[i_c]); } else { // triangle crosses plane // call Util.GenTriangles if (aAbove == bAbove) { // a, b, c GenTriangles( cutting_plane, aAbove ? pos : neg, !aAbove ? pos : neg, a, b, c, txa, txb, txc, na, nb, nc, i_a, i_b, i_c, rings, addUVs, addNormals ); } else if (aAbove == cAbove) { // c, a, b GenTriangles( cutting_plane, aAbove ? pos : neg, !aAbove ? pos : neg, c, a, b, txc, txa, txb, na, nb, nc, i_c, i_a, i_b, rings, addUVs, addNormals ); } else if (bAbove == cAbove) { // b, c, a (use bAbove) GenTriangles( cutting_plane, bAbove ? pos : neg, !bAbove ? pos : neg, b, c, a, txb, txc, txa, na, nb, nc, i_b, i_c, i_a, rings, addUVs, addNormals ); } } } //Debug.Log((DateTime.Now-start).TotalMilliseconds+" elapsed (2)"); //start = DateTime.Now; List <Ring> ringOut = rings.GetRings(param.selfConnectRings, param.ignorePartialRings); List <Ring> analysis = param.hiearchyAnalysis ? Hierarchy.Analyse(ringOut, cutting_plane.normal) : ringOut; Vector2?innerUV = addUVs ? param.innerTextureCoord : null; // generate seperation meshing foreach (var ring in analysis) { GenerateRingMesh(ring, pos, cutting_plane.normal, innerUV, addNormals); GenerateRingMesh(ring, neg, cutting_plane.normal, innerUV, addNormals); } //Debug.Log((DateTime.Now-start).TotalMilliseconds+" elapsed (3)"); //start = DateTime.Now; return(new CutResult(target, cutting_plane.ToWorldSpace().normal, ringOut, pos, neg)); }
public static CutResult Run( GameObject target, CuttingPlane plane, CutParams param ) { CuttingPlane cutting_plane = plane.ToLocalSpace(target.transform); Mesh mesh = target.GetComponent <MeshFilter>().mesh; MeshPart pos = new MeshPart(true), neg = new MeshPart(false); RingGenerator pos_rings = new RingGenerator(), neg_rings = new RingGenerator(); Vector2[] uvs = mesh.uv; Vector3[] vertices = mesh.vertices; Vector3[] normals = mesh.normals; int[] triangles = mesh.triangles; bool addUVs = uvs.Length > 0 && param.innerTextureCoord != null, addNormals = normals.Length > 0; // removed indices HashSet <int> removed = new HashSet <int>(); // divide mesh in half by vertices int i = -1; foreach (Vector3 vertex in vertices) { i++; float dist = cutting_plane.Distance(vertex); if (dist < param.seperationDistance) { removed.Add(i); continue; } if (cutting_plane.IsAbove(vertex)) { pos.indexMap.Add(i, pos.vertices.Count); pos.vertices.Add(vertex); if (addUVs) { pos.uvs.Add(uvs[i]); } if (addNormals) { pos.normals.Add(normals[i]); } } else { neg.indexMap.Add(i, neg.vertices.Count); neg.vertices.Add(vertex); if (addUVs) { neg.uvs.Add(uvs[i]); } if (addNormals) { pos.normals.Add(normals[i]); } } } // if either vertex list is empty and no vertices were removed, the knife plane didn't collide if (pos.vertices.Count == 0 || neg.vertices.Count == 0) { if (!param.allowSingleResult) { return(null); } } // put triangles in correct mesh for (i = 0; i < triangles.Length; i += 3) { // find orignal indices int i_a = triangles[i], i_b = triangles[i + 1], i_c = triangles[i + 2]; // find if these were removed in cut bool r_a = removed.Contains(i_a), r_b = removed.Contains(i_b), r_c = removed.Contains(i_c); // if all are removed, ignore triangle if (r_a && r_b && r_c) { continue; } // find original verticies Vector3 a = vertices[i_a], b = vertices[i_b], c = vertices[i_c]; Vector2 txa, txb, txc; // find original uvs if (uvs.Length > 0) { txa = uvs[i_a]; txb = uvs[i_b]; txc = uvs[i_c]; } else { txa = txb = txc = Vector2.zero; } // find original normals Vector3 na, nb, nc; if (addNormals) { na = normals[i_a]; nb = normals[i_b]; nc = normals[i_c]; } else { na = nb = nc = Vector3.zero; } // seperation check bool aAbove = cutting_plane.IsAbove(a), bAbove = cutting_plane.IsAbove(b), cAbove = cutting_plane.IsAbove(c); if (!r_a && !r_b && !r_c) { // all available if (aAbove && bAbove && cAbove) { // triangle above plane pos.indices.Add(pos.indexMap[i_a]); pos.indices.Add(pos.indexMap[i_b]); pos.indices.Add(pos.indexMap[i_c]); } else if (!aAbove && !bAbove && !cAbove) { // triangle below plane neg.indices.Add(neg.indexMap[i_a]); neg.indices.Add(neg.indexMap[i_b]); neg.indices.Add(neg.indexMap[i_c]); } else { // triangle crosses plane if (aAbove == bAbove) { // a, b, c GenTwoTriangles( cutting_plane, aAbove ? pos : neg, a, b, c, txa, txb, txc, na, nb, nc, i_a, i_b, i_c, aAbove ? pos_rings : neg_rings, addUVs, addNormals, -param.seperationDistance ); GenTriangle( cutting_plane, cAbove ? pos : neg, c, a, b, txc, txa, txb, nc, na, nb, i_c, i_a, i_b, cAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } else if (aAbove == cAbove) { // c, a, b GenTwoTriangles( cutting_plane, aAbove ? pos : neg, c, a, b, txc, txa, txb, nc, na, nb, i_c, i_a, i_b, aAbove ? pos_rings : neg_rings, addUVs, addNormals, -param.seperationDistance ); GenTriangle( cutting_plane, bAbove ? pos : neg, b, c, a, txb, txc, txa, nb, nc, na, i_b, i_c, i_a, bAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } else if (bAbove == cAbove) { // b, c, a GenTwoTriangles( cutting_plane, bAbove ? pos : neg, b, c, a, txb, txc, txa, nb, nc, na, i_b, i_c, i_a, bAbove ? pos_rings : neg_rings, addUVs, addNormals, -param.seperationDistance ); GenTriangle( cutting_plane, aAbove ? pos : neg, a, b, c, txa, txb, txc, na, nb, nc, i_a, i_b, i_c, aAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } } } else if (!r_a && !r_b) { // a and b available if (aAbove != bAbove) { GenTriangle( cutting_plane, aAbove ? pos : neg, a, b, c, txa, txb, txc, na, nb, nc, i_a, i_b, i_c, aAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); GenTriangle( cutting_plane, bAbove ? pos : neg, b, c, a, txb, txc, txa, nb, nc, na, i_b, i_c, i_a, bAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } else { GenTwoTriangles( cutting_plane, aAbove ? pos : neg, a, b, c, txa, txb, txc, na, nb, nc, i_a, i_b, i_c, aAbove ? pos_rings : neg_rings, addUVs, addNormals, -param.seperationDistance ); } } else if (!r_a && !r_c) { // a and c available if (aAbove != cAbove) { GenTriangle( cutting_plane, aAbove ? pos : neg, a, b, c, txa, txb, txc, na, nb, nc, i_a, i_b, i_c, aAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); GenTriangle( cutting_plane, cAbove ? pos : neg, c, a, b, txc, txa, txb, nc, na, nb, i_c, i_a, i_b, cAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } else { GenTwoTriangles( cutting_plane, aAbove ? pos : neg, c, a, b, txc, txa, txb, nc, na, nb, i_c, i_a, i_b, aAbove ? pos_rings : neg_rings, addUVs, addNormals, -param.seperationDistance ); } } else if (!r_b && !r_c) { // b and c available if (bAbove != cAbove) { GenTriangle( cutting_plane, bAbove ? pos : neg, b, c, a, txb, txc, txa, nb, nc, na, i_b, i_c, i_a, bAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); GenTriangle( cutting_plane, cAbove ? pos : neg, c, a, b, txc, txa, txb, nc, na, nb, i_c, i_a, i_b, cAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } else { GenTwoTriangles( cutting_plane, bAbove ? pos : neg, b, c, a, txb, txc, txa, nb, nc, na, i_b, i_c, i_a, bAbove ? pos_rings : neg_rings, addUVs, addNormals, -param.seperationDistance ); } } else if (!r_a) { // a available GenTriangle( cutting_plane, aAbove ? pos : neg, a, b, c, txa, txb, txc, na, nb, nc, i_a, i_b, i_c, aAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } else if (!r_b) { // b available GenTriangle( cutting_plane, bAbove ? pos : neg, b, c, a, txb, txc, txa, nb, nc, na, i_b, i_c, i_a, bAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } else { // c available GenTriangle( cutting_plane, cAbove ? pos : neg, c, a, b, txc, txa, txb, nc, na, nb, i_c, i_a, i_b, cAbove ? pos_rings : neg_rings, addUVs, addNormals, param.seperationDistance ); } } List <Ring> pos_ring_res = pos_rings.GetRings(param.selfConnectRings, param.ignorePartialRings); List <Ring> neg_ring_res = neg_rings.GetRings(param.selfConnectRings, param.ignorePartialRings); List <Ring> pos_analysis = param.hiearchyAnalysis ? Hierarchy.Analyse(pos_ring_res, cutting_plane.normal) : pos_ring_res; List <Ring> neg_analysis = param.hiearchyAnalysis ? Hierarchy.Analyse(neg_ring_res, cutting_plane.normal) : neg_ring_res; Vector2?innerUV = addUVs ? param.innerTextureCoord : null; // generate seperation meshing foreach (var ring in pos_analysis) { GenerateRingMesh(ring, pos, cutting_plane.normal, innerUV); } foreach (var ring in neg_analysis) { GenerateRingMesh(ring, neg, cutting_plane.normal, innerUV); } List <MeshPart> resParts = new List <MeshPart>(); if (pos.vertices.Count > 0) { resParts.Add(pos); } if (neg.vertices.Count > 0) { resParts.Add(neg); } if (resParts.Count < 2 && !param.allowSingleResult) { return(null); } return(new CutResult(target, resParts, cutting_plane.ToWorldSpace().normal, new List <Ring>(), false)); }
public static void GenPartialTriangles( CuttingPlane plane, MeshPart part, Vector3 a, Vector3 b, Vector3 c, Vector2 txa, Vector2 txb, Vector2 txc, int i_a, int i_b, int i_c, HashSet <Vector3> allow_cut, bool addUVs ) { // find intersection vertices / uvs var es = plane.Intersection(a, c, txa, txc, 0); var ds = plane.Intersection(b, c, txb, txc, 0); Vector3 e = es.Item1, d = ds.Item1; Vector2 txe = es.Item2, txd = ds.Item2; // if e == d, the three vertices lie in a line, // and thus do not make up a triangle if (e == d) { return; // not sure if this is nescessary throw MeshUtilsException.ZeroAreaTriangle(); } if ( !allow_cut.Contains(e) && !allow_cut.Contains(d) ) { // triangle must not be cut part.indices.Add(part.indexMap[i_a]); part.indices.Add(part.indexMap[i_b]); part.indices.Add(part.indexMap[i_c]); return; } // new indices int i0 = part.vertices.Count, i1 = part.vertices.Count + 2; // add new vertices and uvs part.vertices.Add(d); part.vertices.Add(e); part.vertices.Add(d); part.vertices.Add(e); if (addUVs) { part.uvs.Add(txd); part.uvs.Add(txe); part.uvs.Add(txd); part.uvs.Add(txe); } // generate triangles ... // add a,d,e part.indices.Add(part.indexMap[i_a]); part.indices.Add(i0); part.indices.Add(i0 + 1); // add a,b,d part.indices.Add(part.indexMap[i_a]); part.indices.Add(part.indexMap[i_b]); part.indices.Add(i0); // add e,d,c part.indices.Add(i1 + 1); part.indices.Add(i1); part.indices.Add(part.indexMap[i_c]); }
// ------------------------------------------------------------ // Generate single triangle for an intersecting triangle a,b,c // It is assumed that a is on the positive half plane // ------------------------------------------------------------ public static void GenTriangle( CuttingPlane plane, MeshPart pos, Vector3 a, Vector3 b, Vector3 c, Vector2 txa, Vector2 txb, Vector2 txc, Vector3 na, Vector3 nb, Vector3 nc, int i_a, int i_b, int i_c, RingGenerator rings, bool addUVs, bool addNormals, float shift ) { // find intersection vertices / uvs var es = plane.Intersection(c, a, txa, txc, nc, na, shift); var ds = plane.Intersection(b, a, txb, txc, nb, na, shift); Vector3 e = es.Item1, d = ds.Item1; Vector2 txe = es.Item2, txd = ds.Item2; Vector3 ne = es.Item3, nd = ds.Item3; // if e == d, the three vertices lie in a line, // and thus do not make up a triangle if (e == d) { return; // not sure if this is nescessary throw MeshUtilsException.ZeroAreaTriangle(); } // new indices int i0 = pos.vertices.Count; // add connected pair in ring generator // find proper direction for ring Vector3 tri_nor = Vector3.Cross(c - a, c - b); bool dir = Vector3.Dot(e - d, Vector3.Cross(plane.normal, tri_nor)) > 0; rings.AddConnected(dir?e:d, dir?d:e); // add new vertices and uvs pos.vertices.Add(d); pos.vertices.Add(e); if (addUVs) { pos.uvs.Add(txd); pos.uvs.Add(txe); } if (addNormals) { pos.normals.Add(nd); pos.normals.Add(ne); } // add a,d,e to positive indicies pos.indices.Add(pos.indexMap[i_a]); pos.indices.Add(i0); pos.indices.Add(i0 + 1); }
public static CutResult Run( GameObject target, CuttingPlane plane, CutParams param ) { CuttingPlane cutting_plane = plane.ToLocalSpace(target.transform); Mesh mesh = target.GetComponent <MeshFilter>().mesh; MeshPart part = new MeshPart(false); Vector2[] uvs = mesh.uv; Vector3[] vertices = mesh.vertices; int[] triangles = mesh.triangles; // // First we find the rings that are eligable for cutting // RingGenerator rings = new RingGenerator(); int i; for (i = 0; i < triangles.Length; i += 3) { // find orignal indices int i_a = triangles[i], i_b = triangles[i + 1], i_c = triangles[i + 2]; // find original verticies Vector3 a = vertices[i_a], b = vertices[i_b], c = vertices[i_c]; // seperation check bool aAbove = cutting_plane.IsAbove(a), bAbove = cutting_plane.IsAbove(b), cAbove = cutting_plane.IsAbove(c); if (aAbove == bAbove && aAbove == cAbove) { continue; } if (aAbove == bAbove) { // a, b, c GenIntersection( cutting_plane, a, b, c, rings ); } else if (aAbove == cAbove) { // c, a, b GenIntersection( cutting_plane, c, a, b, rings ); } else if (bAbove == cAbove) { // b, c, a GenIntersection( cutting_plane, b, c, a, rings ); } } Vector3 point = target.transform.InverseTransformPoint(param.originPoint); List <Ring> resulting_rings = new List <Ring>(); foreach (Ring ring in rings.GetRings(param.selfConnectRings, param.ignorePartialRings)) { Vector3 vec = ring.FurthestVectorToRingPerimeter(point); vec = target.transform.TransformVector(vec); // Debug.DrawRay(param.originPoint,vec,Color.blue,10); float mag = vec.magnitude; Debug.Log(mag); if (mag < param.maxCutDistance) { resulting_rings.Add(ring); } } if (resulting_rings.Count == 0) { return(null); } // Debug.Log(resulting_rings.Count); HashSet <Vector3> allow_cut = new HashSet <Vector3>(); foreach (Ring ring in resulting_rings) { foreach (Vector3 v in ring.verts) { allow_cut.Add(v); } } // // Start of cutting // bool addUVs = uvs.Length > 0 && param.innerTextureCoord != null; // transfer vertices into MeshPart i = 0; foreach (Vector3 vertex in vertices) { part.indexMap.Add(i, part.vertices.Count); part.vertices.Add(vertex); if (addUVs) { part.uvs.Add(uvs[i]); } i++; } // process triangles for (i = 0; i < triangles.Length; i += 3) { // find orignal indices int i_a = triangles[i], i_b = triangles[i + 1], i_c = triangles[i + 2]; // find original verticies Vector3 a = vertices[i_a], b = vertices[i_b], c = vertices[i_c]; Vector2 txa, txb, txc; // find original uvs if (uvs.Length > 0) { txa = uvs[i_a]; txb = uvs[i_b]; txc = uvs[i_c]; } else { txa = txb = txc = Vector2.zero; } // seperation check bool aAbove = cutting_plane.IsAbove(a), bAbove = cutting_plane.IsAbove(b), cAbove = cutting_plane.IsAbove(c); if (aAbove == bAbove && aAbove == cAbove) { // triangle on one side of plane part.indices.Add(part.indexMap[i_a]); part.indices.Add(part.indexMap[i_b]); part.indices.Add(part.indexMap[i_c]); } else { // triangle crosses plane if (aAbove == bAbove) { // a, b, c GenPartialTriangles( cutting_plane, part, a, b, c, txa, txb, txc, i_a, i_b, i_c, allow_cut, addUVs ); } else if (aAbove == cAbove) { // c, a, b GenPartialTriangles( cutting_plane, part, c, a, b, txc, txa, txb, i_c, i_a, i_b, allow_cut, addUVs ); } else if (bAbove == cAbove) { // b, c, a GenPartialTriangles( cutting_plane, part, b, c, a, txb, txc, txa, i_b, i_c, i_a, allow_cut, addUVs ); } } } List <Ring> analysis = param.hiearchyAnalysis ? Hierarchy.Analyse(resulting_rings, cutting_plane.normal) : resulting_rings; List <MeshPart> parts = part.PartialPolySeparate(cutting_plane, allow_cut); if (parts.Count < 2 && !param.allowSingleResult) { return(null); } // generate seperation meshing if (parts.Count > 0) { foreach (var ring in analysis) { foreach (var resPart in parts) { GenerateRingMesh(ring, resPart, cutting_plane.normal, param.innerTextureCoord); } } } return(new CutResult(target, parts, cutting_plane.ToWorldSpace().normal, resulting_rings, true)); }