public static void GenIntersection( CuttingPlane plane, Vector3 a, Vector3 b, Vector3 c, RingGenerator rings ) { // find intersection vertices Vector3 e = plane.Intersection(a, c, Vector2.zero, Vector2.zero, 0).Item1, d = plane.Intersection(b, c, Vector2.zero, Vector2.zero, 0).Item1; // 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(); } // 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; // add connected pair in ring generator rings.AddConnected(dir?e:d, dir?d:e); }
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); }
// ---------------------------- // Complete rings in triangle // ---------------------------- private static void ConnectIVs( HashSet <Vector3> exiting_ivs, Vector3 ep1, Vector3 ep2, Vector3 oep, Dictionary <float, Vector3> map, Dictionary <float, Vector3> prev_map, Dictionary <float, Vector3> next_map, RingGenerator rings, RingGenerator rings2 ) { if (map.Count == 0) { return; } List <float> keyList = new List <float>(map.Keys); keyList.Sort(); int oneIfFirstIsEntry = 0; // Debugging.LogList(keyList); // check first is exit or entry // connect in relevant ring generator if (!exiting_ivs.Contains(map[keyList[0]])) { // Debug.Log("First is entry"); // connect first edge point to first entry rings.AddConnected(ep1, map[keyList[0]]); oneIfFirstIsEntry = 1; // if previous are empty, connect around if (prev_map.Count == 0) { rings.AddConnected(oep, ep1); } } else { // Debug.Log("First is exit"); rings2.AddConnected(map[keyList[0]], ep1); if (prev_map.Count == 0) { rings2.AddConnected(ep1, oep); } } // connect inner int i; for (i = 1; i < keyList.Count; i++) { // Debug.Log(map[keyList[i-1]]+" "+map[keyList[i]]); if (i % 2 != oneIfFirstIsEntry) { rings.AddConnected(map[keyList[i - 1]], map[keyList[i]]); } else { rings2.AddConnected(map[keyList[i]], map[keyList[i - 1]]); } } // check exit or entrance // connect to edge point in relevant ring generator i = keyList.Count - 1; if (exiting_ivs.Contains(map[keyList[i]])) { //Debug.Log("last was exit"); rings.AddConnected(map[keyList[i]], ep2); if (next_map.Count == 0 && prev_map.Count == 0) { rings.AddConnected(ep2, oep); } } else { // Debug.Log("last was entry"); rings2.AddConnected(ep2, map[keyList[i]]); if (next_map.Count == 0 && prev_map.Count == 0) { rings2.AddConnected(oep, ep2); } } }
// ------------------------------------------------------------ // 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); }