Make coplanar faces work properly, by implementing a 2d BSP on each

set of coplanar faces. The polygon count still gets stupid fast;
I'm thinking I can fix that by adding some extra test planes at the
top of the 3d BSP, to quickly cull out stuff that doesn't intersect
us.

[git-p4: depot-paths = "//depot/solvespace/": change = 1736]
This commit is contained in:
Jonathan Westhues 2008-05-24 02:34:06 -08:00
parent de46118324
commit 4d7ffc85f9
4 changed files with 329 additions and 25 deletions

View File

@ -1202,7 +1202,7 @@ void GraphicsWindow::Paint(int w, int h) {
glDisable(GL_LIGHTING);
glxLockColorTo(0, 1, 0);
glEnable(GL_DEPTH_TEST);
glxDebugMesh(&br);
glxDebugMesh(&br);
br.Clear();
FreeAllTemporary();

317
mesh.cpp
View File

@ -40,8 +40,37 @@ Vector SBsp3::IntersectionWith(Vector a, Vector b) {
if(da*db > 0) oops();
double dab = (db - da);
Vector r = (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
return r;
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
}
void SBsp3::InsertInPlane(bool pos2, STriangle *tr,
SMesh *m, bool flip, bool cpl)
{
Vector tc = ((tr->a).Plus(tr->b).Plus(tr->c)).ScaledBy(1.0/3);
bool onFace = false;
bool sameNormal;
SBsp3 *ll = this;
while(ll) {
if((ll->tri).ContainsPoint(tc)) {
onFace = true;
sameNormal = (tr->Normal()).Dot((ll->tri).Normal()) > 0;
break;
}
ll = ll->more;
}
if(flip) {
if(cpl) oops();
if(!pos2 && (!onFace || !sameNormal)) {
m->AddTriangle(tr->c, tr->b, tr->a);
}
} else {
if(pos2 || (onFace && sameNormal && cpl)) {
m->AddTriangle(tr->a, tr->b, tr->c);
}
}
}
void SBsp3::InsertHow(int how, STriangle *tr,
@ -80,8 +109,13 @@ alt:
instead->AddTriangle(tr->c, tr->b, tr->a);
}
if(how == COPLANAR) {
// Arbitrarily pick a side. This fails if two faces are coplanar.
InsertHow(POS, tr, instead, flip, cpl);
if(edges) {
edges->InsertTriangle(tr, instead, this, flip, cpl);
} else {
// I suppose this actually is allowed to happen, if the coplanar
// face is the leaf, and all of its neighbors are earlier in tree?
InsertInPlane(true, tr, instead, flip, cpl);
}
}
}
@ -98,19 +132,19 @@ SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead, bool flip, bool cpl) {
double dt[3] = { (tr->a).Dot(n), (tr->b).Dot(n), (tr->c).Dot(n) };
int inc = 0, posc = 0, negc = 0;
bool ispos[3], isneg[3], ison[3];
ZERO(&ispos); ZERO(&isneg); ZERO(&ison);
bool isPos[3], isNeg[3], isOn[3];
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
// Count vertices in the plane
for(int i = 0; i < 3; i++) {
if(fabs(dt[i] - d) < LENGTH_EPS) {
inc++;
ison[i] = true;
isOn[i] = true;
} else if(dt[i] > d) {
posc++;
ispos[i] = true;
isPos[i] = true;
} else {
negc++;
isneg[i] = true;
isNeg[i] = true;
}
}
@ -123,8 +157,15 @@ SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead, bool flip, bool cpl) {
// No split required
if(posc == 0 || negc == 0) {
if(inc == 2) {
// Two vertices in-plane, other above or below
// XXX do edge bsp
Vector a, b;
if (!isOn[0]) { a = tr->b; b = tr->c; }
else if(!isOn[1]) { a = tr->c; b = tr->a; }
else if(!isOn[2]) { a = tr->a; b = tr->b; }
else oops();
if(!instead) {
SEdge se = { 0, a, b };
edges = edges->InsertEdge(&se, n, tr->Normal());
}
}
if(posc > 0) {
@ -138,12 +179,12 @@ SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead, bool flip, bool cpl) {
// The polygon must be split into two pieces, one above, one below.
Vector a, b, c;
// Standardize so that a is on the plane
if(posc == 1 && negc == 1 && inc == 1) {
bool bpos;
if (ison[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = ispos[1];
} else if(ison[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = ispos[2];
} else if(ison[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = ispos[0];
// Standardize so that a is on the plane
if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
} else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
} else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
} else oops();
Vector bPc = IntersectionWith(b, c);
@ -158,20 +199,25 @@ SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead, bool flip, bool cpl) {
InsertHow(NEG, &btri, instead, flip, cpl);
}
if(!instead) {
SEdge se = { 0, a, bPc };
edges = edges->InsertEdge(&se, n, tr->Normal());
}
return this;
}
// Standardize so that a is on one side, and b and c are on the other.
if(posc == 2 && negc == 1) {
if (isneg[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(isneg[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(isneg[2]) { a = tr->c; b = tr->a; c = tr->b;
// Standardize so that a is on one side, and b and c are on the other.
if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
} else oops();
} else if(posc == 1 && negc == 2) {
if (ispos[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(ispos[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(ispos[2]) { a = tr->c; b = tr->a; c = tr->b;
if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
} else oops();
} else oops();
@ -191,6 +237,11 @@ SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead, bool flip, bool cpl) {
InsertHow(NEG, &quad2, instead, flip, cpl);
InsertHow(POS, &alone, instead, flip, cpl);
}
if(!instead) {
SEdge se = { 0, aPb, cPa };
edges = edges->InsertEdge(&se, n, alone.Normal());
}
return this;
}
@ -201,6 +252,7 @@ void SBsp3::DebugDraw(void) {
Vector norm = tri.Normal();
glNormal3d(norm.x, norm.y, norm.z);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glBegin(GL_TRIANGLES);
glxVertex3v(tri.a);
@ -225,12 +277,231 @@ void SBsp3::DebugDraw(void) {
glxVertex3v(tri.a);
glxVertex3v(tri.b);
glxVertex3v(tri.c);
glEnd();
glEnd();
glPolygonOffset(0, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
more->DebugDraw();
neg->DebugDraw();
edges->DebugDraw(n, d);
}
/////////////////////////////////
Vector SBsp2::IntersectionWith(Vector a, Vector b) {
double da = a.Dot(no) - d;
double db = b.Dot(no) - d;
if(da*db > 0) oops();
double dab = (db - da);
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
}
SBsp2 *SBsp2::InsertEdge(SEdge *nedge, Vector nnp, Vector out) {
if(!this) {
// Brand new node; so allocate for it, and fill us in.
SBsp2 *r = Alloc();
r->np = nnp;
r->no = (r->np).Cross((nedge->b).Minus(nedge->a));
if(out.Dot(r->no) < 0) {
r->no = (r->no).ScaledBy(-1);
}
r->d = (nedge->a).Dot(r->no);
r->edge = *nedge;
return r;
}
double dt[2] = { (nedge->a).Dot(no), (nedge->b).Dot(no) };
bool isPos[2], isNeg[2], isOn[2];
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
for(int i = 0; i < 2; i++) {
if(fabs(dt[i] - d) < LENGTH_EPS) {
isOn[i] = true;
} else if(dt[i] > d) {
isPos[i] = true;
} else {
isNeg[i] = true;
}
}
if((isPos[0] && isPos[1])||(isPos[0] && isOn[1])||(isOn[0] && isPos[1])) {
pos = pos->InsertEdge(nedge, nnp, out);
return this;
}
if((isNeg[0] && isNeg[1])||(isNeg[0] && isOn[1])||(isOn[0] && isNeg[1])) {
neg = neg->InsertEdge(nedge, nnp, out);
return this;
}
if(isOn[0] && isOn[1]) {
SBsp2 *m = Alloc();
m->np = nnp;
m->no = (m->np).Cross((nedge->b).Minus(nedge->a));
if(out.Dot(m->no) < 0) {
m->no = (m->no).ScaledBy(-1);
}
m->d = (nedge->a).Dot(m->no);
m->edge = *nedge;
m->more = more;
more = m;
return this;
}
if((isPos[0] && isNeg[1]) || (isNeg[0] && isPos[1])) {
Vector aPb = IntersectionWith(nedge->a, nedge->b);
SEdge ea = { 0, nedge->a, aPb };
SEdge eb = { 0, aPb, nedge->b };
if(isPos[0]) {
pos = pos->InsertEdge(&ea, nnp, out);
neg = neg->InsertEdge(&eb, nnp, out);
} else {
neg = neg->InsertEdge(&ea, nnp, out);
pos = pos->InsertEdge(&eb, nnp, out);
}
return this;
}
oops();
}
void SBsp2::InsertTriangleHow(int how, STriangle *tr,
SMesh *m, SBsp3 *bsp3, bool flip, bool cpl)
{
switch(how) {
case POS:
if(pos) {
pos->InsertTriangle(tr, m, bsp3, flip, cpl);
} else {
bsp3->InsertInPlane(true, tr, m, flip, cpl);
}
break;
case NEG:
if(neg) {
neg->InsertTriangle(tr, m, bsp3, flip, cpl);
} else {
bsp3->InsertInPlane(false, tr, m, flip, cpl);
}
break;
default: oops();
}
}
void SBsp2::InsertTriangle(STriangle *tr,
SMesh *m, SBsp3 *bsp3, bool flip, bool cpl)
{
double dt[3] = { (tr->a).Dot(no), (tr->b).Dot(no), (tr->c).Dot(no) };
bool isPos[3], isNeg[3], isOn[3];
int inc = 0, posc = 0, negc = 0;
ZERO(&isPos); ZERO(&isNeg); ZERO(&isOn);
for(int i = 0; i < 3; i++) {
if(fabs(dt[i] - d) < LENGTH_EPS) {
isOn[i] = true;
inc++;
} else if(dt[i] > d) {
isPos[i] = true;
posc++;
} else {
isNeg[i] = true;
negc++;
}
}
if(inc == 3) {
// All vertices on-line; so it's a degenerate triangle, to ignore.
return;
}
// No split required
if(posc == 0 || negc == 0) {
if(posc > 0) {
InsertTriangleHow(POS, tr, m, bsp3, flip, cpl);
} else {
InsertTriangleHow(NEG, tr, m, bsp3, flip, cpl);
}
return;
}
// The polygon must be split into two pieces, one above, one below.
Vector a, b, c;
if(posc == 1 && negc == 1 && inc == 1) {
bool bpos;
// Standardize so that a is on the plane
if (isOn[0]) { a = tr->a; b = tr->b; c = tr->c; bpos = isPos[1];
} else if(isOn[1]) { a = tr->b; b = tr->c; c = tr->a; bpos = isPos[2];
} else if(isOn[2]) { a = tr->c; b = tr->a; c = tr->b; bpos = isPos[0];
} else oops();
Vector bPc = IntersectionWith(b, c);
STriangle btri = { 0, a, b, bPc };
STriangle ctri = { 0, c, a, bPc };
if(bpos) {
InsertTriangleHow(POS, &btri, m, bsp3, flip, cpl);
InsertTriangleHow(NEG, &ctri, m, bsp3, flip, cpl);
} else {
InsertTriangleHow(POS, &ctri, m, bsp3, flip, cpl);
InsertTriangleHow(NEG, &btri, m, bsp3, flip, cpl);
}
return;
}
if(posc == 2 && negc == 1) {
// Standardize so that a is on one side, and b and c are on the other.
if (isNeg[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(isNeg[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(isNeg[2]) { a = tr->c; b = tr->a; c = tr->b;
} else oops();
} else if(posc == 1 && negc == 2) {
if (isPos[0]) { a = tr->a; b = tr->b; c = tr->c;
} else if(isPos[1]) { a = tr->b; b = tr->c; c = tr->a;
} else if(isPos[2]) { a = tr->c; b = tr->a; c = tr->b;
} else oops();
} else oops();
Vector aPb = IntersectionWith(a, b);
Vector cPa = IntersectionWith(c, a);
STriangle alone = { 0, a, aPb, cPa };
STriangle quad1 = { 0, aPb, b, c };
STriangle quad2 = { 0, aPb, c, cPa };
if(posc == 2 && negc == 1) {
InsertTriangleHow(POS, &quad1, m, bsp3, flip, cpl);
InsertTriangleHow(POS, &quad2, m, bsp3, flip, cpl);
InsertTriangleHow(NEG, &alone, m, bsp3, flip, cpl);
} else {
InsertTriangleHow(NEG, &quad1, m, bsp3, flip, cpl);
InsertTriangleHow(NEG, &quad2, m, bsp3, flip, cpl);
InsertTriangleHow(POS, &alone, m, bsp3, flip, cpl);
}
return;
}
void SBsp2::DebugDraw(Vector n, double d) {
if(!this) return;
if(fabs((edge.a).Dot(n) - d) > LENGTH_EPS) oops();
if(fabs((edge.b).Dot(n) - d) > LENGTH_EPS) oops();
glLineWidth(10);
glBegin(GL_LINES);
glxVertex3v(edge.a);
glxVertex3v(edge.b);
glEnd();
pos->DebugDraw(n, d);
neg->DebugDraw(n, d);
more->DebugDraw(n, d);
glLineWidth(1);
}

View File

@ -5,6 +5,23 @@ Vector STriangle::Normal(void) {
return ab.Cross(bc);
}
bool STriangle::ContainsPoint(Vector p) {
Vector ab = b.Minus(a), bc = c.Minus(b), ca = a.Minus(c);
Vector n = ab.Cross(bc);
n = n.WithMagnitude(1);
Vector no_ab = n.Cross(ab);
if(no_ab.Dot(p) < no_ab.Dot(a) - LENGTH_EPS) return false;
Vector no_bc = n.Cross(bc);
if(no_bc.Dot(p) < no_bc.Dot(b) - LENGTH_EPS) return false;
Vector no_ca = n.Cross(ca);
if(no_ca.Dot(p) < no_ca.Dot(c) - LENGTH_EPS) return false;
return true;
}
void SEdgeList::Clear(void) {
l.Clear();
}

View File

@ -4,6 +4,7 @@
class SPolygon;
class SMesh;
class SBsp3;
template <class T>
class SList {
@ -87,10 +88,15 @@ public:
Vector a, b, c;
Vector Normal(void);
bool ContainsPoint(Vector p);
};
class SBsp2 {
public:
Vector np; // normal to the plane
Vector no; // outer normal to the edge
double d;
SEdge edge;
SBsp2 *pos;
@ -98,8 +104,16 @@ public:
SBsp2 *more;
void Insert(SEdge *se);
static const int POS = 100, NEG = 101, COPLANAR = 200;
void InsertTriangleHow(int how, STriangle *tr,
SMesh *m, SBsp3 *bsp3, bool flip, bool cpl);
void InsertTriangle(STriangle *tr,
SMesh *m, SBsp3 *bsp3, bool flip, bool cpl);
Vector IntersectionWith(Vector a, Vector b);
SBsp2 *InsertEdge(SEdge *nedge, Vector nnp, Vector out);
static SBsp2 *Alloc(void);
void DebugDraw(Vector n, double d);
};
class SBsp3 {
@ -125,6 +139,8 @@ public:
SBsp3 *Insert(STriangle *str, SMesh *instead, bool flip, bool cpl);
void InsertInPlane(bool pos2, STriangle *tr, SMesh *m, bool flip, bool cpl);
void DebugDraw(void);
};