You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
503 lines
20 KiB
503 lines
20 KiB
/* |
|
** 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<Mesh> |
|
{ |
|
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(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Creates one edge, two vertices and a loop (face). |
|
/// The loop consists of the two new half-edges. |
|
/// </summary> |
|
public MeshUtils.Edge MakeEdge() |
|
{ |
|
var e = MeshUtils.MakeEdge(_eHead); |
|
|
|
MeshUtils.MakeVertex(e, _vHead); |
|
MeshUtils.MakeVertex(e._Sym, _vHead); |
|
MeshUtils.MakeFace(e, _fHead); |
|
|
|
return e; |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
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; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
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); |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// 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. |
|
/// </summary> |
|
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; |
|
} |
|
|
|
/// <summary> |
|
/// 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! |
|
/// </summary> |
|
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); |
|
} |
|
} |
|
} |
|
}
|
|
|