Add a pass in triangulation to create convex triangle fans. These triangles will have smaller bounding boxes and look better.
This commit is contained in:
parent
b3eb589240
commit
575ddb5aaa
@ -131,6 +131,7 @@ public:
|
|||||||
void FindPointWithMinX();
|
void FindPointWithMinX();
|
||||||
Vector AnyEdgeMidpoint() const;
|
Vector AnyEdgeMidpoint() const;
|
||||||
|
|
||||||
|
bool IsEmptyTriangle(int ap, int bp, int cp, double scaledEPS) const;
|
||||||
bool IsEar(int bp, double scaledEps) const;
|
bool IsEar(int bp, double scaledEps) const;
|
||||||
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
|
bool BridgeToContour(SContour *sc, SEdgeList *el, List<Vector> *vl);
|
||||||
void ClipEarInto(SMesh *m, int bp, double scaledEps);
|
void ClipEarInto(SMesh *m, int bp, double scaledEps);
|
||||||
|
@ -213,6 +213,41 @@ haveEdge:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SContour::IsEmptyTriangle(int ap, int bp, int cp, double scaledEPS) const {
|
||||||
|
|
||||||
|
STriangle tr = {};
|
||||||
|
tr.a = l[ap].p;
|
||||||
|
tr.b = l[bp].p;
|
||||||
|
tr.c = l[cp].p;
|
||||||
|
|
||||||
|
// Accelerate with an axis-aligned bounding box test
|
||||||
|
Vector maxv = tr.a, minv = tr.a;
|
||||||
|
(tr.b).MakeMaxMin(&maxv, &minv);
|
||||||
|
(tr.c).MakeMaxMin(&maxv, &minv);
|
||||||
|
|
||||||
|
Vector n = Vector::From(0, 0, -1);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < l.n; i++) {
|
||||||
|
if(i == ap || i == bp || i == cp) continue;
|
||||||
|
|
||||||
|
Vector p = l[i].p;
|
||||||
|
if(p.OutsideAndNotOn(maxv, minv)) continue;
|
||||||
|
|
||||||
|
// A point on the edge of the triangle is considered to be inside,
|
||||||
|
// and therefore makes it a non-ear; but a point on the vertex is
|
||||||
|
// "outside", since that's necessary to make bridges work.
|
||||||
|
if(p.EqualsExactly(tr.a)) continue;
|
||||||
|
if(p.EqualsExactly(tr.b)) continue;
|
||||||
|
if(p.EqualsExactly(tr.c)) continue;
|
||||||
|
|
||||||
|
if(tr.ContainsPointProjd(n, p)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SContour::IsEar(int bp, double scaledEps) const {
|
bool SContour::IsEar(int bp, double scaledEps) const {
|
||||||
int ap = WRAP(bp-1, l.n),
|
int ap = WRAP(bp-1, l.n),
|
||||||
cp = WRAP(bp+1, l.n);
|
cp = WRAP(bp+1, l.n);
|
||||||
@ -308,6 +343,71 @@ void SContour::UvTriangulateInto(SMesh *m, SSurface *srf) {
|
|||||||
}
|
}
|
||||||
l.RemoveTagged();
|
l.RemoveTagged();
|
||||||
|
|
||||||
|
// Handle simple triangle fans all at once. This pass is optional.
|
||||||
|
if(srf->degm == 1 && srf->degn == 1) {
|
||||||
|
l.ClearTags();
|
||||||
|
int j=0;
|
||||||
|
int pstart = 0;
|
||||||
|
double elen = -1.0;
|
||||||
|
double oldspan = 0.0;
|
||||||
|
for(i = 1; i < l.n; i++) {
|
||||||
|
Vector ab = l[i].p.Minus(l[i-1].p);
|
||||||
|
// first time just measure the segment
|
||||||
|
if (elen < 0.0) {
|
||||||
|
elen = ab.Dot(ab);
|
||||||
|
oldspan = elen;
|
||||||
|
j = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// check for consecutive segments of similar size which are also
|
||||||
|
// ears and where the group forms a convex ear
|
||||||
|
bool end = false;
|
||||||
|
double ratio = ab.Dot(ab) / elen;
|
||||||
|
if ((ratio < 0.25) || (ratio > 4.0)) end = true;
|
||||||
|
|
||||||
|
double slen = l[pstart].p.Minus(l[i].p).MagSquared();
|
||||||
|
if (slen < oldspan) end = true;
|
||||||
|
|
||||||
|
if (!IsEar(i-1, scaledEps) ) end = true;
|
||||||
|
// if ((j>0) && !IsEar(pstart, i-1, i, scaledEps)) end = true;
|
||||||
|
if ((j>0) && !IsEmptyTriangle(pstart, i-1, i, scaledEps)) end = true;
|
||||||
|
// the new segment is valid so add to the fan
|
||||||
|
if (!end) {
|
||||||
|
j++;
|
||||||
|
oldspan = slen;
|
||||||
|
}
|
||||||
|
// we need to stop at the end of polygon but may still
|
||||||
|
if (i == l.n-1) {
|
||||||
|
end = true;
|
||||||
|
}
|
||||||
|
if (end) { // triangulate the fan and tag the verticies
|
||||||
|
if (j > 3) {
|
||||||
|
Vector center = l[pstart+1].p.Plus(l[pstart+j-1].p).ScaledBy(0.5);
|
||||||
|
for (int x=0; x<j; x++) {
|
||||||
|
STriangle tr = {};
|
||||||
|
tr.a = center;
|
||||||
|
tr.b = l[pstart+x].p;
|
||||||
|
tr.c = l[pstart+x+1].p;
|
||||||
|
m->AddTriangle(&tr);
|
||||||
|
}
|
||||||
|
for (int x=1; x<j; x++) {
|
||||||
|
l[pstart+x].tag = 1;
|
||||||
|
}
|
||||||
|
STriangle tr = {};
|
||||||
|
tr.a = center;
|
||||||
|
tr.b = l[pstart+j].p;
|
||||||
|
tr.c = l[pstart].p;
|
||||||
|
m->AddTriangle(&tr);
|
||||||
|
}
|
||||||
|
pstart = i-1;
|
||||||
|
elen = ab.Dot(ab);
|
||||||
|
oldspan = elen;
|
||||||
|
j = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.RemoveTagged();
|
||||||
|
} // end optional fan creation pass
|
||||||
|
|
||||||
bool toggle = false;
|
bool toggle = false;
|
||||||
while(l.n > 3) {
|
while(l.n > 3) {
|
||||||
int bestEar = -1;
|
int bestEar = -1;
|
||||||
|
Loading…
Reference in New Issue
Block a user