/* ** SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) ** Copyright (C) 2011 Silicon Graphics, Inc. ** All Rights Reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal ** in the Software without restriction, including without limitation the rights ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ** of the Software, and to permit persons to whom the Software is furnished to do so, ** subject to the following conditions: ** ** The above copyright notice including the dates of first publication and either this ** permission notice or a reference to http://oss.sgi.com/projects/FreeB/ shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, ** INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A ** PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC. ** BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE ** OR OTHER DEALINGS IN THE SOFTWARE. ** ** Except as contained in this notice, the name of Silicon Graphics, Inc. shall not ** be used in advertising or otherwise to promote the sale, use or other dealings in ** this Software without prior written authorization from Silicon Graphics, Inc. */ /* ** Original Author: Eric Veach, July 1994. ** libtess2: Mikko Mononen, http://code.google.com/p/libtess2/. ** LibTessDotNet: Remi Gillig, https://github.com/speps/LibTessDotNet */ using System; using System.Diagnostics; namespace UnityEngine.Rendering.Universal { namespace LibTessDotNet { internal class Mesh : MeshUtils.Pooled { internal MeshUtils.Vertex _vHead; internal MeshUtils.Face _fHead; internal MeshUtils.Edge _eHead, _eHeadSym; public Mesh() { var v = _vHead = MeshUtils.Vertex.Create(); var f = _fHead = MeshUtils.Face.Create(); var pair = MeshUtils.EdgePair.Create(); var e = _eHead = pair._e; var eSym = _eHeadSym = pair._eSym; v._next = v._prev = v; v._anEdge = null; f._next = f._prev = f; f._anEdge = null; f._trail = null; f._marked = false; f._inside = false; e._next = e; e._Sym = eSym; e._Onext = null; e._Lnext = null; e._Org = null; e._Lface = null; e._winding = 0; e._activeRegion = null; eSym._next = eSym; eSym._Sym = e; eSym._Onext = null; eSym._Lnext = null; eSym._Org = null; eSym._Lface = null; eSym._winding = 0; eSym._activeRegion = null; } public override void Reset() { _vHead = null; _fHead = null; _eHead = _eHeadSym = null; } public override void OnFree() { for (MeshUtils.Face f = _fHead._next, fNext = _fHead; f != _fHead; f = fNext) { fNext = f._next; f.Free(); } for (MeshUtils.Vertex v = _vHead._next, vNext = _vHead; v != _vHead; v = vNext) { vNext = v._next; v.Free(); } for (MeshUtils.Edge e = _eHead._next, eNext = _eHead; e != _eHead; e = eNext) { eNext = e._next; e.Free(); } } /// /// Creates one edge, two vertices and a loop (face). /// The loop consists of the two new half-edges. /// public MeshUtils.Edge MakeEdge() { var e = MeshUtils.MakeEdge(_eHead); MeshUtils.MakeVertex(e, _vHead); MeshUtils.MakeVertex(e._Sym, _vHead); MeshUtils.MakeFace(e, _fHead); return e; } /// /// Splice is the basic operation for changing the /// mesh connectivity and topology. It changes the mesh so that /// eOrg->Onext = OLD( eDst->Onext ) /// eDst->Onext = OLD( eOrg->Onext ) /// where OLD(...) means the value before the meshSplice operation. /// /// This can have two effects on the vertex structure: /// - if eOrg->Org != eDst->Org, the two vertices are merged together /// - if eOrg->Org == eDst->Org, the origin is split into two vertices /// In both cases, eDst->Org is changed and eOrg->Org is untouched. /// /// Similarly (and independently) for the face structure, /// - if eOrg->Lface == eDst->Lface, one loop is split into two /// - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one /// In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. /// /// Some special cases: /// If eDst == eOrg, the operation has no effect. /// If eDst == eOrg->Lnext, the new face will have a single edge. /// If eDst == eOrg->Lprev, the old face will have a single edge. /// If eDst == eOrg->Onext, the new vertex will have a single edge. /// If eDst == eOrg->Oprev, the old vertex will have a single edge. /// public void Splice(MeshUtils.Edge eOrg, MeshUtils.Edge eDst) { if (eOrg == eDst) { return; } bool joiningVertices = false; if (eDst._Org != eOrg._Org) { // We are merging two disjoint vertices -- destroy eDst->Org joiningVertices = true; MeshUtils.KillVertex(eDst._Org, eOrg._Org); } bool joiningLoops = false; if (eDst._Lface != eOrg._Lface) { // We are connecting two disjoint loops -- destroy eDst->Lface joiningLoops = true; MeshUtils.KillFace(eDst._Lface, eOrg._Lface); } // Change the edge structure MeshUtils.Splice(eDst, eOrg); if (!joiningVertices) { // We split one vertex into two -- the new vertex is eDst->Org. // Make sure the old vertex points to a valid half-edge. MeshUtils.MakeVertex(eDst, eOrg._Org); eOrg._Org._anEdge = eOrg; } if (!joiningLoops) { // We split one loop into two -- the new loop is eDst->Lface. // Make sure the old face points to a valid half-edge. MeshUtils.MakeFace(eDst, eOrg._Lface); eOrg._Lface._anEdge = eOrg; } } /// /// Removes the edge eDel. There are several cases: /// if (eDel->Lface != eDel->Rface), we join two loops into one; the loop /// eDel->Lface is deleted. Otherwise, we are splitting one loop into two; /// the newly created loop will contain eDel->Dst. If the deletion of eDel /// would create isolated vertices, those are deleted as well. /// public void Delete(MeshUtils.Edge eDel) { var eDelSym = eDel._Sym; // First step: disconnect the origin vertex eDel->Org. We make all // changes to get a consistent mesh in this "intermediate" state. bool joiningLoops = false; if (eDel._Lface != eDel._Rface) { // We are joining two loops into one -- remove the left face joiningLoops = true; MeshUtils.KillFace(eDel._Lface, eDel._Rface); } if (eDel._Onext == eDel) { MeshUtils.KillVertex(eDel._Org, null); } else { // Make sure that eDel->Org and eDel->Rface point to valid half-edges eDel._Rface._anEdge = eDel._Oprev; eDel._Org._anEdge = eDel._Onext; MeshUtils.Splice(eDel, eDel._Oprev); if (!joiningLoops) { // We are splitting one loop into two -- create a new loop for eDel. MeshUtils.MakeFace(eDel, eDel._Lface); } } // Claim: the mesh is now in a consistent state, except that eDel->Org // may have been deleted. Now we disconnect eDel->Dst. if (eDelSym._Onext == eDelSym) { MeshUtils.KillVertex(eDelSym._Org, null); MeshUtils.KillFace(eDelSym._Lface, null); } else { // Make sure that eDel->Dst and eDel->Lface point to valid half-edges eDel._Lface._anEdge = eDelSym._Oprev; eDelSym._Org._anEdge = eDelSym._Onext; MeshUtils.Splice(eDelSym, eDelSym._Oprev); } // Any isolated vertices or faces have already been freed. MeshUtils.KillEdge(eDel); } /// /// Creates a new edge such that eNew == eOrg.Lnext and eNew.Dst is a newly created vertex. /// eOrg and eNew will have the same left face. /// public MeshUtils.Edge AddEdgeVertex(MeshUtils.Edge eOrg) { var eNew = MeshUtils.MakeEdge(eOrg); var eNewSym = eNew._Sym; // Connect the new edge appropriately MeshUtils.Splice(eNew, eOrg._Lnext); // Set vertex and face information eNew._Org = eOrg._Dst; MeshUtils.MakeVertex(eNewSym, eNew._Org); eNew._Lface = eNewSym._Lface = eOrg._Lface; return eNew; } /// /// Splits eOrg into two edges eOrg and eNew such that eNew == eOrg.Lnext. /// The new vertex is eOrg.Dst == eNew.Org. /// eOrg and eNew will have the same left face. /// public MeshUtils.Edge SplitEdge(MeshUtils.Edge eOrg) { var eTmp = AddEdgeVertex(eOrg); var eNew = eTmp._Sym; // Disconnect eOrg from eOrg->Dst and connect it to eNew->Org MeshUtils.Splice(eOrg._Sym, eOrg._Sym._Oprev); MeshUtils.Splice(eOrg._Sym, eNew); // Set the vertex and face information eOrg._Dst = eNew._Org; eNew._Dst._anEdge = eNew._Sym; // may have pointed to eOrg->Sym eNew._Rface = eOrg._Rface; eNew._winding = eOrg._winding; // copy old winding information eNew._Sym._winding = eOrg._Sym._winding; return eNew; } /// /// Creates a new edge from eOrg->Dst to eDst->Org, and returns the corresponding half-edge eNew. /// If eOrg->Lface == eDst->Lface, this splits one loop into two, /// and the newly created loop is eNew->Lface. Otherwise, two disjoint /// loops are merged into one, and the loop eDst->Lface is destroyed. /// /// If (eOrg == eDst), the new face will have only two edges. /// If (eOrg->Lnext == eDst), the old face is reduced to a single edge. /// If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges. /// public MeshUtils.Edge Connect(MeshUtils.Edge eOrg, MeshUtils.Edge eDst) { var eNew = MeshUtils.MakeEdge(eOrg); var eNewSym = eNew._Sym; bool joiningLoops = false; if (eDst._Lface != eOrg._Lface) { // We are connecting two disjoint loops -- destroy eDst->Lface joiningLoops = true; MeshUtils.KillFace(eDst._Lface, eOrg._Lface); } // Connect the new edge appropriately MeshUtils.Splice(eNew, eOrg._Lnext); MeshUtils.Splice(eNewSym, eDst); // Set the vertex and face information eNew._Org = eOrg._Dst; eNewSym._Org = eDst._Org; eNew._Lface = eNewSym._Lface = eOrg._Lface; // Make sure the old face points to a valid half-edge eOrg._Lface._anEdge = eNewSym; if (!joiningLoops) { MeshUtils.MakeFace(eNew, eOrg._Lface); } return eNew; } /// /// Destroys a face and removes it from the global face list. All edges of /// fZap will have a NULL pointer as their left face. Any edges which /// also have a NULL pointer as their right face are deleted entirely /// (along with any isolated vertices this produces). /// An entire mesh can be deleted by zapping its faces, one at a time, /// in any order. Zapped faces cannot be used in further mesh operations! /// public void ZapFace(MeshUtils.Face fZap) { var eStart = fZap._anEdge; // walk around face, deleting edges whose right face is also NULL var eNext = eStart._Lnext; MeshUtils.Edge e, eSym; do { e = eNext; eNext = e._Lnext; e._Lface = null; if (e._Rface == null) { // delete the edge -- see TESSmeshDelete above if (e._Onext == e) { MeshUtils.KillVertex(e._Org, null); } else { // Make sure that e._Org points to a valid half-edge e._Org._anEdge = e._Onext; MeshUtils.Splice(e, e._Oprev); } eSym = e._Sym; if (eSym._Onext == eSym) { MeshUtils.KillVertex(eSym._Org, null); } else { // Make sure that eSym._Org points to a valid half-edge eSym._Org._anEdge = eSym._Onext; MeshUtils.Splice(eSym, eSym._Oprev); } MeshUtils.KillEdge(e); } } while (e != eStart); /* delete from circular doubly-linked list */ var fPrev = fZap._prev; var fNext = fZap._next; fNext._prev = fPrev; fPrev._next = fNext; fZap.Free(); } public void MergeConvexFaces(int maxVertsPerFace) { for (var f = _fHead._next; f != _fHead; f = f._next) { // Skip faces which are outside the result if (!f._inside) { continue; } var eCur = f._anEdge; var vStart = eCur._Org; while (true) { var eNext = eCur._Lnext; var eSym = eCur._Sym; if (eSym != null && eSym._Lface != null && eSym._Lface._inside) { // Try to merge the neighbour faces if the resulting polygons // does not exceed maximum number of vertices. int curNv = f.VertsCount; int symNv = eSym._Lface.VertsCount; if ((curNv + symNv - 2) <= maxVertsPerFace) { // Merge if the resulting poly is convex. if (Geom.VertCCW(eCur._Lprev._Org, eCur._Org, eSym._Lnext._Lnext._Org) && Geom.VertCCW(eSym._Lprev._Org, eSym._Org, eCur._Lnext._Lnext._Org)) { eNext = eSym._Lnext; Delete(eSym); eCur = null; } } } if (eCur != null && eCur._Lnext._Org == vStart) break; // Continue to next edge. eCur = eNext; } } } [Conditional("DEBUG")] public void Check() { MeshUtils.Edge e; MeshUtils.Face fPrev = _fHead, f; for (fPrev = _fHead; (f = fPrev._next) != _fHead; fPrev = f) { e = f._anEdge; do { Debug.Assert(e._Sym != e); Debug.Assert(e._Sym._Sym == e); Debug.Assert(e._Lnext._Onext._Sym == e); Debug.Assert(e._Onext._Sym._Lnext == e); Debug.Assert(e._Lface == f); e = e._Lnext; } while (e != f._anEdge); } Debug.Assert(f._prev == fPrev && f._anEdge == null); MeshUtils.Vertex vPrev = _vHead, v; for (vPrev = _vHead; (v = vPrev._next) != _vHead; vPrev = v) { Debug.Assert(v._prev == vPrev); e = v._anEdge; do { Debug.Assert(e._Sym != e); Debug.Assert(e._Sym._Sym == e); Debug.Assert(e._Lnext._Onext._Sym == e); Debug.Assert(e._Onext._Sym._Lnext == e); Debug.Assert(e._Org == v); e = e._Onext; } while (e != v._anEdge); } Debug.Assert(v._prev == vPrev && v._anEdge == null); MeshUtils.Edge ePrev = _eHead; for (ePrev = _eHead; (e = ePrev._next) != _eHead; ePrev = e) { Debug.Assert(e._Sym._next == ePrev._Sym); Debug.Assert(e._Sym != e); Debug.Assert(e._Sym._Sym == e); Debug.Assert(e._Org != null); Debug.Assert(e._Dst != null); Debug.Assert(e._Lnext._Onext._Sym == e); Debug.Assert(e._Onext._Sym._Lnext == e); } Debug.Assert(e._Sym._next == ePrev._Sym && e._Sym == _eHeadSym && e._Sym._Sym == e && e._Org == null && e._Dst == null && e._Lface == null && e._Rface == null); } } } }