123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750 |
- /*
- ** 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.Experimental.Rendering.Universal
- {
- using Real = System.Single;
- namespace LibTessDotNet
- {
- internal enum WindingRule
- {
- EvenOdd,
- NonZero,
- Positive,
- Negative,
- AbsGeqTwo
- }
- internal enum ElementType
- {
- Polygons,
- ConnectedPolygons,
- BoundaryContours
- }
- internal enum ContourOrientation
- {
- Original,
- Clockwise,
- CounterClockwise
- }
- internal struct ContourVertex
- {
- public Vec3 Position;
- public object Data;
- public override string ToString()
- {
- return string.Format("{0}, {1}", Position, Data);
- }
- }
- internal delegate object CombineCallback(Vec3 position, object[] data, Real[] weights);
- internal partial class Tess
- {
- private Mesh _mesh;
- private Vec3 _normal;
- private Vec3 _sUnit;
- private Vec3 _tUnit;
- private Real _bminX, _bminY, _bmaxX, _bmaxY;
- private WindingRule _windingRule;
- private Dict<ActiveRegion> _dict;
- private PriorityQueue<MeshUtils.Vertex> _pq;
- private MeshUtils.Vertex _event;
- private CombineCallback _combineCallback;
- private ContourVertex[] _vertices;
- private int _vertexCount;
- private int[] _elements;
- private int _elementCount;
- public Vec3 Normal { get { return _normal; } set { _normal = value; } }
- public Real SUnitX = 1;
- public Real SUnitY = 0;
- #if DOUBLE
- public Real SentinelCoord = 4e150;
- #else
- public Real SentinelCoord = 4e30f;
- #endif
- /// <summary>
- /// If true, will remove empty (zero area) polygons.
- /// </summary>
- public bool NoEmptyPolygons = false;
- /// <summary>
- /// If true, will use pooling to reduce GC (compare performance with/without, can vary wildly).
- /// </summary>
- public bool UsePooling = false;
- public ContourVertex[] Vertices { get { return _vertices; } }
- public int VertexCount { get { return _vertexCount; } }
- public int[] Elements { get { return _elements; } }
- public int ElementCount { get { return _elementCount; } }
- public Tess()
- {
- _normal = Vec3.Zero;
- _bminX = _bminY = _bmaxX = _bmaxY = 0;
- _windingRule = WindingRule.EvenOdd;
- _mesh = null;
- _vertices = null;
- _vertexCount = 0;
- _elements = null;
- _elementCount = 0;
- }
- private void ComputeNormal(ref Vec3 norm)
- {
- var v = _mesh._vHead._next;
- var minVal = new Real[3] { v._coords.X, v._coords.Y, v._coords.Z };
- var minVert = new MeshUtils.Vertex[3] { v, v, v };
- var maxVal = new Real[3] { v._coords.X, v._coords.Y, v._coords.Z };
- var maxVert = new MeshUtils.Vertex[3] { v, v, v };
- for (; v != _mesh._vHead; v = v._next)
- {
- if (v._coords.X < minVal[0]) { minVal[0] = v._coords.X; minVert[0] = v; }
- if (v._coords.Y < minVal[1]) { minVal[1] = v._coords.Y; minVert[1] = v; }
- if (v._coords.Z < minVal[2]) { minVal[2] = v._coords.Z; minVert[2] = v; }
- if (v._coords.X > maxVal[0]) { maxVal[0] = v._coords.X; maxVert[0] = v; }
- if (v._coords.Y > maxVal[1]) { maxVal[1] = v._coords.Y; maxVert[1] = v; }
- if (v._coords.Z > maxVal[2]) { maxVal[2] = v._coords.Z; maxVert[2] = v; }
- }
- // Find two vertices separated by at least 1/sqrt(3) of the maximum
- // distance between any two vertices
- int i = 0;
- if (maxVal[1] - minVal[1] > maxVal[0] - minVal[0]) { i = 1; }
- if (maxVal[2] - minVal[2] > maxVal[i] - minVal[i]) { i = 2; }
- if (minVal[i] >= maxVal[i])
- {
- // All vertices are the same -- normal doesn't matter
- norm = new Vec3 { X = 0, Y = 0, Z = 1 };
- return;
- }
- // Look for a third vertex which forms the triangle with maximum area
- // (Length of normal == twice the triangle area)
- Real maxLen2 = 0, tLen2;
- var v1 = minVert[i];
- var v2 = maxVert[i];
- Vec3 d1, d2, tNorm;
- Vec3.Sub(ref v1._coords, ref v2._coords, out d1);
- for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
- {
- Vec3.Sub(ref v._coords, ref v2._coords, out d2);
- tNorm.X = d1.Y * d2.Z - d1.Z * d2.Y;
- tNorm.Y = d1.Z * d2.X - d1.X * d2.Z;
- tNorm.Z = d1.X * d2.Y - d1.Y * d2.X;
- tLen2 = tNorm.X*tNorm.X + tNorm.Y*tNorm.Y + tNorm.Z*tNorm.Z;
- if (tLen2 > maxLen2)
- {
- maxLen2 = tLen2;
- norm = tNorm;
- }
- }
- if (maxLen2 <= 0.0f)
- {
- // All points lie on a single line -- any decent normal will do
- norm = Vec3.Zero;
- i = Vec3.LongAxis(ref d1);
- norm[i] = 1;
- }
- }
- private void CheckOrientation()
- {
- // When we compute the normal automatically, we choose the orientation
- // so that the the sum of the signed areas of all contours is non-negative.
- Real area = 0.0f;
- for (var f = _mesh._fHead._next; f != _mesh._fHead; f = f._next)
- {
- if (f._anEdge._winding <= 0)
- {
- continue;
- }
- area += MeshUtils.FaceArea(f);
- }
- if (area < 0.0f)
- {
- // Reverse the orientation by flipping all the t-coordinates
- for (var v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
- {
- v._t = -v._t;
- }
- Vec3.Neg(ref _tUnit);
- }
- }
- private void ProjectPolygon()
- {
- var norm = _normal;
- bool computedNormal = false;
- if (norm.X == 0.0f && norm.Y == 0.0f && norm.Z == 0.0f)
- {
- ComputeNormal(ref norm);
- _normal = norm;
- computedNormal = true;
- }
- int i = Vec3.LongAxis(ref norm);
- _sUnit[i] = 0;
- _sUnit[(i + 1) % 3] = SUnitX;
- _sUnit[(i + 2) % 3] = SUnitY;
- _tUnit[i] = 0;
- _tUnit[(i + 1) % 3] = norm[i] > 0.0f ? -SUnitY : SUnitY;
- _tUnit[(i + 2) % 3] = norm[i] > 0.0f ? SUnitX : -SUnitX;
- // Project the vertices onto the sweep plane
- for (var v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
- {
- Vec3.Dot(ref v._coords, ref _sUnit, out v._s);
- Vec3.Dot(ref v._coords, ref _tUnit, out v._t);
- }
- if (computedNormal)
- {
- CheckOrientation();
- }
- // Compute ST bounds.
- bool first = true;
- for (var v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
- {
- if (first)
- {
- _bminX = _bmaxX = v._s;
- _bminY = _bmaxY = v._t;
- first = false;
- }
- else
- {
- if (v._s < _bminX) _bminX = v._s;
- if (v._s > _bmaxX) _bmaxX = v._s;
- if (v._t < _bminY) _bminY = v._t;
- if (v._t > _bmaxY) _bmaxY = v._t;
- }
- }
- }
- /// <summary>
- /// TessellateMonoRegion( face ) tessellates a monotone region
- /// (what else would it do??) The region must consist of a single
- /// loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this
- /// case means that any vertical line intersects the interior of the
- /// region in a single interval.
- ///
- /// Tessellation consists of adding interior edges (actually pairs of
- /// half-edges), to split the region into non-overlapping triangles.
- ///
- /// The basic idea is explained in Preparata and Shamos (which I don't
- /// have handy right now), although their implementation is more
- /// complicated than this one. The are two edge chains, an upper chain
- /// and a lower chain. We process all vertices from both chains in order,
- /// from right to left.
- ///
- /// The algorithm ensures that the following invariant holds after each
- /// vertex is processed: the untessellated region consists of two
- /// chains, where one chain (say the upper) is a single edge, and
- /// the other chain is concave. The left vertex of the single edge
- /// is always to the left of all vertices in the concave chain.
- ///
- /// Each step consists of adding the rightmost unprocessed vertex to one
- /// of the two chains, and forming a fan of triangles from the rightmost
- /// of two chain endpoints. Determining whether we can add each triangle
- /// to the fan is a simple orientation test. By making the fan as large
- /// as possible, we restore the invariant (check it yourself).
- /// </summary>
- private void TessellateMonoRegion(MeshUtils.Face face)
- {
- // All edges are oriented CCW around the boundary of the region.
- // First, find the half-edge whose origin vertex is rightmost.
- // Since the sweep goes from left to right, face->anEdge should
- // be close to the edge we want.
- var up = face._anEdge;
- Debug.Assert(up._Lnext != up && up._Lnext._Lnext != up);
- while (Geom.VertLeq(up._Dst, up._Org)) up = up._Lprev;
- while (Geom.VertLeq(up._Org, up._Dst)) up = up._Lnext;
- var lo = up._Lprev;
- while (up._Lnext != lo)
- {
- if (Geom.VertLeq(up._Dst, lo._Org))
- {
- // up.Dst is on the left. It is safe to form triangles from lo.Org.
- // The EdgeGoesLeft test guarantees progress even when some triangles
- // are CW, given that the upper and lower chains are truly monotone.
- while (lo._Lnext != up && (Geom.EdgeGoesLeft(lo._Lnext)
- || Geom.EdgeSign(lo._Org, lo._Dst, lo._Lnext._Dst) <= 0.0f))
- {
- lo = _mesh.Connect(lo._Lnext, lo)._Sym;
- }
- lo = lo._Lprev;
- }
- else
- {
- // lo.Org is on the left. We can make CCW triangles from up.Dst.
- while (lo._Lnext != up && (Geom.EdgeGoesRight(up._Lprev)
- || Geom.EdgeSign(up._Dst, up._Org, up._Lprev._Org) >= 0.0f))
- {
- up = _mesh.Connect(up, up._Lprev)._Sym;
- }
- up = up._Lnext;
- }
- }
- // Now lo.Org == up.Dst == the leftmost vertex. The remaining region
- // can be tessellated in a fan from this leftmost vertex.
- Debug.Assert(lo._Lnext != up);
- while (lo._Lnext._Lnext != up)
- {
- lo = _mesh.Connect(lo._Lnext, lo)._Sym;
- }
- }
- /// <summary>
- /// TessellateInterior( mesh ) tessellates each region of
- /// the mesh which is marked "inside" the polygon. Each such region
- /// must be monotone.
- /// </summary>
- private void TessellateInterior()
- {
- MeshUtils.Face f, next;
- for (f = _mesh._fHead._next; f != _mesh._fHead; f = next)
- {
- // Make sure we don't try to tessellate the new triangles.
- next = f._next;
- if (f._inside)
- {
- TessellateMonoRegion(f);
- }
- }
- }
- /// <summary>
- /// DiscardExterior zaps (ie. sets to null) all faces
- /// which are not marked "inside" the polygon. Since further mesh operations
- /// on NULL faces are not allowed, the main purpose is to clean up the
- /// mesh so that exterior loops are not represented in the data structure.
- /// </summary>
- private void DiscardExterior()
- {
- MeshUtils.Face f, next;
- for (f = _mesh._fHead._next; f != _mesh._fHead; f = next)
- {
- // Since f will be destroyed, save its next pointer.
- next = f._next;
- if( ! f._inside ) {
- _mesh.ZapFace(f);
- }
- }
- }
- /// <summary>
- /// SetWindingNumber( value, keepOnlyBoundary ) resets the
- /// winding numbers on all edges so that regions marked "inside" the
- /// polygon have a winding number of "value", and regions outside
- /// have a winding number of 0.
- ///
- /// If keepOnlyBoundary is TRUE, it also deletes all edges which do not
- /// separate an interior region from an exterior one.
- /// </summary>
- private void SetWindingNumber(int value, bool keepOnlyBoundary)
- {
- MeshUtils.Edge e, eNext;
- for (e = _mesh._eHead._next; e != _mesh._eHead; e = eNext)
- {
- eNext = e._next;
- if (e._Rface._inside != e._Lface._inside)
- {
- /* This is a boundary edge (one side is interior, one is exterior). */
- e._winding = (e._Lface._inside) ? value : -value;
- }
- else
- {
- /* Both regions are interior, or both are exterior. */
- if (!keepOnlyBoundary)
- {
- e._winding = 0;
- }
- else
- {
- _mesh.Delete(e);
- }
- }
- }
- }
- private int GetNeighbourFace(MeshUtils.Edge edge)
- {
- if (edge._Rface == null)
- return MeshUtils.Undef;
- if (!edge._Rface._inside)
- return MeshUtils.Undef;
- return edge._Rface._n;
- }
- private void OutputPolymesh(ElementType elementType, int polySize)
- {
- MeshUtils.Vertex v;
- MeshUtils.Face f;
- MeshUtils.Edge edge;
- int maxFaceCount = 0;
- int maxVertexCount = 0;
- int faceVerts, i;
- if (polySize < 3)
- {
- polySize = 3;
- }
- // Assume that the input data is triangles now.
- // Try to merge as many polygons as possible
- if (polySize > 3)
- {
- _mesh.MergeConvexFaces(polySize);
- }
- // Mark unused
- for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
- v._n = MeshUtils.Undef;
- // Create unique IDs for all vertices and faces.
- for (f = _mesh._fHead._next; f != _mesh._fHead; f = f._next)
- {
- f._n = MeshUtils.Undef;
- if (!f._inside) continue;
- if (NoEmptyPolygons)
- {
- var area = MeshUtils.FaceArea(f);
- if (Math.Abs(area) < Real.Epsilon)
- {
- continue;
- }
- }
- edge = f._anEdge;
- faceVerts = 0;
- do {
- v = edge._Org;
- if (v._n == MeshUtils.Undef)
- {
- v._n = maxVertexCount;
- maxVertexCount++;
- }
- faceVerts++;
- edge = edge._Lnext;
- }
- while (edge != f._anEdge);
- Debug.Assert(faceVerts <= polySize);
- f._n = maxFaceCount;
- ++maxFaceCount;
- }
- _elementCount = maxFaceCount;
- if (elementType == ElementType.ConnectedPolygons)
- maxFaceCount *= 2;
- _elements = new int[maxFaceCount * polySize];
- _vertexCount = maxVertexCount;
- _vertices = new ContourVertex[_vertexCount];
- // Output vertices.
- for (v = _mesh._vHead._next; v != _mesh._vHead; v = v._next)
- {
- if (v._n != MeshUtils.Undef)
- {
- // Store coordinate
- _vertices[v._n].Position = v._coords;
- _vertices[v._n].Data = v._data;
- }
- }
- // Output indices.
- int elementIndex = 0;
- for (f = _mesh._fHead._next; f != _mesh._fHead; f = f._next)
- {
- if (!f._inside) continue;
- if (NoEmptyPolygons)
- {
- var area = MeshUtils.FaceArea(f);
- if (Math.Abs(area) < Real.Epsilon)
- {
- continue;
- }
- }
- // Store polygon
- edge = f._anEdge;
- faceVerts = 0;
- do {
- v = edge._Org;
- _elements[elementIndex++] = v._n;
- faceVerts++;
- edge = edge._Lnext;
- } while (edge != f._anEdge);
- // Fill unused.
- for (i = faceVerts; i < polySize; ++i)
- {
- _elements[elementIndex++] = MeshUtils.Undef;
- }
- // Store polygon connectivity
- if (elementType == ElementType.ConnectedPolygons)
- {
- edge = f._anEdge;
- do
- {
- _elements[elementIndex++] = GetNeighbourFace(edge);
- edge = edge._Lnext;
- } while (edge != f._anEdge);
- // Fill unused.
- for (i = faceVerts; i < polySize; ++i)
- {
- _elements[elementIndex++] = MeshUtils.Undef;
- }
- }
- }
- }
- private void OutputContours()
- {
- MeshUtils.Face f;
- MeshUtils.Edge edge, start;
- int startVert = 0;
- int vertCount = 0;
- _vertexCount = 0;
- _elementCount = 0;
- for (f = _mesh._fHead._next; f != _mesh._fHead; f = f._next)
- {
- if (!f._inside) continue;
- start = edge = f._anEdge;
- do
- {
- ++_vertexCount;
- edge = edge._Lnext;
- }
- while (edge != start);
- ++_elementCount;
- }
- _elements = new int[_elementCount * 2];
- _vertices = new ContourVertex[_vertexCount];
- int vertIndex = 0;
- int elementIndex = 0;
- startVert = 0;
- for (f = _mesh._fHead._next; f != _mesh._fHead; f = f._next)
- {
- if (!f._inside) continue;
- vertCount = 0;
- start = edge = f._anEdge;
- do {
- _vertices[vertIndex].Position = edge._Org._coords;
- _vertices[vertIndex].Data = edge._Org._data;
- ++vertIndex;
- ++vertCount;
- edge = edge._Lnext;
- } while (edge != start);
- _elements[elementIndex++] = startVert;
- _elements[elementIndex++] = vertCount;
- startVert += vertCount;
- }
- }
- private Real SignedArea(ContourVertex[] vertices)
- {
- Real area = 0.0f;
- for (int i = 0; i < vertices.Length; i++)
- {
- var v0 = vertices[i];
- var v1 = vertices[(i + 1) % vertices.Length];
- area += v0.Position.X * v1.Position.Y;
- area -= v0.Position.Y * v1.Position.X;
- }
- return 0.5f * area;
- }
- public void AddContour(ContourVertex[] vertices)
- {
- AddContour(vertices, ContourOrientation.Original);
- }
- public void AddContour(ContourVertex[] vertices, ContourOrientation forceOrientation)
- {
- if (_mesh == null)
- {
- _mesh = new Mesh();
- }
- bool reverse = false;
- if (forceOrientation != ContourOrientation.Original)
- {
- var area = SignedArea(vertices);
- reverse = (forceOrientation == ContourOrientation.Clockwise && area < 0.0f) || (forceOrientation == ContourOrientation.CounterClockwise && area > 0.0f);
- }
- MeshUtils.Edge e = null;
- for (int i = 0; i < vertices.Length; ++i)
- {
- if (e == null)
- {
- e = _mesh.MakeEdge();
- _mesh.Splice(e, e._Sym);
- }
- else
- {
- // Create a new vertex and edge which immediately follow e
- // in the ordering around the left face.
- _mesh.SplitEdge(e);
- e = e._Lnext;
- }
- int index = reverse ? vertices.Length - 1 - i : i;
- // The new vertex is now e._Org.
- e._Org._coords = vertices[index].Position;
- e._Org._data = vertices[index].Data;
- // The winding of an edge says how the winding number changes as we
- // cross from the edge's right face to its left face. We add the
- // vertices in such an order that a CCW contour will add +1 to
- // the winding number of the region inside the contour.
- e._winding = 1;
- e._Sym._winding = -1;
- }
- }
- public void Tessellate(WindingRule windingRule, ElementType elementType, int polySize)
- {
- Tessellate(windingRule, elementType, polySize, null);
- }
- public void Tessellate(WindingRule windingRule, ElementType elementType, int polySize, CombineCallback combineCallback)
- {
- _normal = Vec3.Zero;
- _vertices = null;
- _elements = null;
- _windingRule = windingRule;
- _combineCallback = combineCallback;
- if (_mesh == null)
- {
- return;
- }
- // Determine the polygon normal and project vertices onto the plane
- // of the polygon.
- ProjectPolygon();
- // ComputeInterior computes the planar arrangement specified
- // by the given contours, and further subdivides this arrangement
- // into regions. Each region is marked "inside" if it belongs
- // to the polygon, according to the rule given by windingRule.
- // Each interior region is guaranteed be monotone.
- ComputeInterior();
- // If the user wants only the boundary contours, we throw away all edges
- // except those which separate the interior from the exterior.
- // Otherwise we tessellate all the regions marked "inside".
- if (elementType == ElementType.BoundaryContours)
- {
- SetWindingNumber(1, true);
- }
- else
- {
- TessellateInterior();
- }
- _mesh.Check();
- if (elementType == ElementType.BoundaryContours)
- {
- OutputContours();
- }
- else
- {
- OutputPolymesh(elementType, polySize);
- }
- if (UsePooling)
- {
- _mesh.Free();
- }
- _mesh = null;
- }
- }
- }
- }
|