// --------------------------- // Intersection point of edge // --------------------------- public Vector3 Intersection(Vector3 p0, Vector3 p1) { // to avoid rounding errors, always make sure float calculations are done in this order if (IsAbove(p0)) { Vector3 tmp = p0; p0 = p1; p1 = tmp; } else if (!IsAbove(p1)) { Debug.LogException(MeshUtilsException.Internal("both below")); } if (IsAbove(p0)) { Debug.LogException(MeshUtilsException.Internal("both above")); } float dist0 = Distance(p0); float dist1 = Distance(p1); float factor = dist0 / (dist0 + dist1); return(p0 + (p1 - p0) * factor); }
public Vector3 DirectionalProject(Vector3 v, Vector3 dir) { float t = -(Vector3.Dot(v, normal) + d) / Vector3.Dot(dir, normal); if (float.IsInfinity(t) || float.IsNaN(t)) { throw MeshUtilsException.Internal("Directional projection failed: t=" + t + " v=" + v + " n=" + normal + " d=" + d + " dir=" + dir); } return(v + dir * t); }
public MUPlane(Vector3 normal, Vector3 pointInPlane) { if (normal == Vector3.zero) { throw MeshUtilsException.Internal("Zero normal"); } this.pointInPlane = pointInPlane; this.normal = normal.normalized; this.d = -Vector3.Dot(pointInPlane, this.normal); }
private void AssertMatchingCounts() { if (uvs.Count > 0 && uvs.Count != vertices.Count) { throw MeshUtilsException.Internal("UV/vertex count mismatch (" + uvs.Count + " uvs, " + vertices.Count + " verts)"); } if (normals.Count > 0 && normals.Count != vertices.Count) { throw MeshUtilsException.Internal("Normal/vertex count mismatch (" + normals.Count + " normals, " + vertices.Count + " verts)"); } }
// ------------------------ // Connect rings in strip // ------------------------ public void TemplateJoin(CuttingTemplate template, SortedDictionary <float, Vector3> map) { if (map.Count == 0) { return; } if (map.Count % 2 == 1) { throw MeshUtilsException.Internal("Odd strip entry/exit count"); } bool first_is_entry = false; Vector3 first_vec = map.First().Value; foreach (List <Vector3> part in partials) { if (part[0] != first_vec) { continue; } first_is_entry = true; break; } var x = map.Values.GetEnumerator(); for (int i = 0; i < map.Count; i += 2) { if (!x.MoveNext()) { throw MeshUtilsException.Internal("Enumerator exhausted"); } Vector3 a = x.Current; if (!x.MoveNext()) { throw MeshUtilsException.Internal("Enumerator exhausted"); } Vector3 b = x.Current; AddConnected(first_is_entry?b:a, first_is_entry?a:b); } }
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)); } }