Singularity/Library/PackageCache/com.unity.render-pipelines..../Runtime/External/LibTessDotNet/Mesh.cs

504 lines
20 KiB
C#
Raw Normal View History

2024-05-06 14:45:45 -04:00
/*
** 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);
}
}
}
}