From 4d7ffc85f947d9121f573ee179ad49c6156beb02 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sat, 24 May 2008 02:34:06 -0800 Subject: [PATCH] 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] --- graphicswin.cpp | 2 +- mesh.cpp | 317 ++++++++++++++++++++++++++++++++++++++++++++---- polygon.cpp | 17 +++ polygon.h | 18 ++- 4 files changed, 329 insertions(+), 25 deletions(-) diff --git a/graphicswin.cpp b/graphicswin.cpp index c9c39ef..6faa47f 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -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(); diff --git a/mesh.cpp b/mesh.cpp index 05a5507..996f3de 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -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); } diff --git a/polygon.cpp b/polygon.cpp index 9fa65ea..36ebe0f 100644 --- a/polygon.cpp +++ b/polygon.cpp @@ -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(); } diff --git a/polygon.h b/polygon.h index 286d9fc..5871c3d 100644 --- a/polygon.h +++ b/polygon.h @@ -4,6 +4,7 @@ class SPolygon; class SMesh; +class SBsp3; template 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); };