private void OnMesh(Mesh mesh) { //Log.Debug(" mesh!"); }
static bool RemoveDegenerateFaces(Mesh mesh) /* * Delete any degenerate faces with only two edges. WalkDirtyRegions() * will catch almost all of these, but it won't catch degenerate faces * produced by splice operations on already-processed edges. * The two places this can happen are in FinishLeftRegions(), when * we splice in a "temporary" edge produced by ConnectRightVertex(), * and in CheckForLeftSplice(), where we splice already-processed * edges to ensure that our dictionary invariants are not violated * by numerical errors. * * In both these cases it is *very* dangerous to delete the offending * edge at the time, since one of the routines further up the stack * will sometimes be keeping a pointer to that edge. */ { Face f, fNext; HalfEdge e; for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = fNext) { fNext = f.nextFace; e = f.halfEdgeThisIsLeftFaceOf; if (e.nextEdgeCCWAroundLeftFace == e) { throw new Exception(); } if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == e) { /* A face with only two edges */ AddWinding(e.nextEdgeCCWAroundOrigin, e); Mesh.DeleteHalfEdge(e); } } return true; }
void EmptyCache() { VertexAndIndex[] v = this.simpleVertexCache; this.mesh = new Mesh(); for (int i = 0; i < this.cacheCount; i++) { this.AddVertex(v[i].x, v[i].y, v[i].vertexIndex); } this.cacheCount = 0; this.emptyCache = false; }
/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in * both meshes, and returns the new mesh (the old meshes are destroyed). */ private Mesh meshUnion(Mesh mesh1, Mesh mesh2) { Face f1 = mesh1.faceHead; ContourVertex v1 = mesh1.vertexHead; HalfEdge e1 = mesh1.halfEdgeHead; Face f2 = mesh2.faceHead; ContourVertex v2 = mesh2.vertexHead; HalfEdge e2 = mesh2.halfEdgeHead; /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ if (f2.nextFace != f2) { f1.prevFace.nextFace = f2.nextFace; f2.nextFace.prevFace = f1.prevFace; f2.prevFace.nextFace = f1; f1.prevFace = f2.prevFace; } if (v2.nextVertex != v2) { v1.prevVertex.nextVertex = v2.nextVertex; v2.nextVertex.prevVertex = v1.prevVertex; v2.prevVertex.nextVertex = v1; v1.prevVertex = v2.prevVertex; } if (e2.nextHalfEdge != e2) { e1.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e2.nextHalfEdge; e2.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1.otherHalfOfThisEdge.nextHalfEdge; e2.otherHalfOfThisEdge.nextHalfEdge.otherHalfOfThisEdge.nextHalfEdge = e1; e1.otherHalfOfThisEdge.nextHalfEdge = e2.otherHalfOfThisEdge.nextHalfEdge; } mesh2 = null; return mesh1; }
/************************ Boundary contour decomposition ******************/ /* Takes a mesh, and outputs one * contour for each face marked "inside". The rendering output is * provided as callbacks. */ public void RenderBoundary(Mesh mesh) { for (Face curFace = mesh.faceHead.nextFace; curFace != mesh.faceHead; curFace = curFace.nextFace) { if (curFace.isInterior) { this.CallBegin(Tesselator.TriangleListType.LineLoop); HalfEdge curHalfEdge = curFace.halfEdgeThisIsLeftFaceOf; do { this.CallVertex(curHalfEdge.originVertex.clientIndex); curHalfEdge = curHalfEdge.nextEdgeCCWAroundLeftFace; } while (curHalfEdge != curFace.halfEdgeThisIsLeftFaceOf); this.CallEnd(); } } }
/************************ Strips and Fans decomposition ******************/ /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle * fans, strips, and separate triangles. A substantial effort is made * to use as few rendering primitives as possible (ie. to make the fans * and strips as large as possible). * * The rendering output is provided as callbacks (see the api). */ public void RenderMesh(Mesh mesh) { Face f; /* Make a list of separate triangles so we can render them all at once */ this.lonelyTriList = null; for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = f.nextFace) { f.marked = false; } for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = f.nextFace) { /* We examine all faces in an arbitrary order. Whenever we find * an unprocessed face F, we output a group of faces including F * whose size is maximum. */ if (f.isInterior && !f.marked) { RenderMaximumFaceGroup(f); if (!f.marked) { throw new System.Exception(); } } } if (this.lonelyTriList != null) { RenderLonelyTriangles(this.lonelyTriList); this.lonelyTriList = null; } }
public void EndPolygon() { RequireState(ProcessingState.InPolygon); processingState = ProcessingState.Dormant; if (this.mesh == null) { if (!this.EdgeCallBackSet && this.callMesh == null) { /* Try some special code to make the easy cases go quickly * (eg. convex polygons). This code does NOT handle multiple contours, * intersections, edge flags, and of course it does not generate * an explicit mesh either. */ if (RenderCache()) { return; } } EmptyCache(); /* could've used a label*/ } /* Determine the polygon normal and project vertices onto the plane * of the polygon. */ ProjectPolygon(); /* __gl_computeInterior( this ) 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 this.windingRule. * Each interior region is guaranteed to be monotone. */ ActiveRegion.ComputeInterior(this); bool rc = true; /* 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 (this.boundaryOnly) { rc = this.mesh.SetWindingNumber(1, true); } else { rc = this.mesh.TessellateInterior(); } this.mesh.CheckMesh(); if (this.callBegin != null || this.callEnd != null || this.callVertex != null || this.callEdgeFlag != null) { if (this.boundaryOnly) { RenderBoundary(mesh); /* output boundary contours */ } else { RenderMesh(mesh); /* output strips and fans */ } } if (this.callMesh != null) { /* Throw away the exterior faces, so that all faces are interior. * This way the user doesn't have to check the "inside" flag, * and we don't need to even reveal its existence. It also leaves * the freedom for an implementation to not generate the exterior * faces in the first place. */ mesh.DiscardExterior(); callMesh(mesh); /* user wants the mesh itself */ this.mesh = null; return; } this.mesh = null; }
public virtual void BeginPolygon() { RequireState(ProcessingState.Dormant); processingState = ProcessingState.InPolygon; cacheCount = 0; emptyCache = false; mesh = null; }
/* * Delete any degenerate faces with only two edges. WalkDirtyRegions() * will catch almost all of these, but it won't catch degenerate faces * produced by splice operations on already-processed edges. * The two places this can happen are in FinishLeftRegions(), when * we splice in a "temporary" edge produced by ConnectRightVertex(), * and in CheckForLeftSplice(), where we splice already-processed * edges to ensure that our dictionary invariants are not violated * by numerical errors. * * In both these cases it is *very* dangerous to delete the offending * edge at the time, since one of the routines further up the stack * will sometimes be keeping a pointer to that edge. */ static bool RemoveDegenerateFaces(Mesh mesh) { Face f, fNext; HalfEdge e; for (f = mesh.faceHead.nextFace; f != mesh.faceHead; f = fNext) { fNext = f.nextFace; e = f.halfEdgeThisIsLeftFaceOf; if(e.nextEdgeCCWAroundLeftFace == e) { throw new Exception(); } if (e.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == e) { /* A face with only two edges */ AddWinding(e.nextEdgeCCWAroundOrigin, e); Mesh.DeleteHalfEdge(e); } } return true; }
void EmptyCache() { Vertex[] vCaches = this.simpleVertexCache; int[] index_caches = this.indexCached; this.mesh = new Mesh(); int count = this.cacheCount; for (int i = 0; i < count; i++) { Vertex v = vCaches[i]; this.AddVertex(v.x, v.y, index_caches[i]); } this.cacheCount = 0; this.emptyCache = false; }
/* face.TessellateMonoRegion() 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). */ internal bool TessellateMonoRegion() { /* 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. */ HalfEdge up = this.halfEdgeThisIsLeftFaceOf; if (up.nextEdgeCCWAroundLeftFace == up || up.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace == up) { throw new Exception(); } for (; up.directionVertex.VertLeq(up.originVertex); up = up.Lprev) { ; } for (; up.originVertex.VertLeq(up.directionVertex); up = up.nextEdgeCCWAroundLeftFace) { ; } HalfEdge lo = up.Lprev; while (up.nextEdgeCCWAroundLeftFace != lo) { if (up.directionVertex.VertLeq(lo.originVertex)) { /* 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.nextEdgeCCWAroundLeftFace != up && (lo.nextEdgeCCWAroundLeftFace.EdgeGoesLeft() || ContourVertex.EdgeSign(lo.originVertex, lo.directionVertex, lo.nextEdgeCCWAroundLeftFace.directionVertex) <= 0)) { HalfEdge tempHalfEdge = Mesh.meshConnect(lo.nextEdgeCCWAroundLeftFace, lo); lo = tempHalfEdge.otherHalfOfThisEdge; } lo = lo.Lprev; } else { /* lo.Org is on the left. We can make CCW triangles from up.Dst. */ while (lo.nextEdgeCCWAroundLeftFace != up && (up.Lprev.EdgeGoesRight() || ContourVertex.EdgeSign(up.directionVertex, up.originVertex, up.Lprev.originVertex) >= 0)) { HalfEdge tempHalfEdge = Mesh.meshConnect(up, up.Lprev); up = tempHalfEdge.otherHalfOfThisEdge; } up = up.nextEdgeCCWAroundLeftFace; } } // Now lo.Org == up.Dst == the leftmost vertex. The remaining region // can be tessellated in a fan from this leftmost vertex. if (lo.nextEdgeCCWAroundLeftFace == up) { throw new Exception(); } while (lo.nextEdgeCCWAroundLeftFace.nextEdgeCCWAroundLeftFace != up) { HalfEdge tempHalfEdge = Mesh.meshConnect(lo.nextEdgeCCWAroundLeftFace, lo); lo = tempHalfEdge.otherHalfOfThisEdge; } return(true); }