diff --git a/glhelper.cpp b/glhelper.cpp index b7031e1..20aef05 100644 --- a/glhelper.cpp +++ b/glhelper.cpp @@ -111,6 +111,22 @@ void glxColor4d(double r, double g, double b, double a) if(!ColorLocked) glColor4d(r, g, b, a); } +void glxFillMesh(SMesh *m) +{ + glEnable(GL_NORMALIZE); + glBegin(GL_TRIANGLES); + for(int i = 0; i < m->l.n; i++) { + STriangle *tr = &(m->l.elem[i]); + Vector n = tr->Normal(); + glNormal3d(n.x, n.y, n.z); + + glxVertex3v(tr->a); + glxVertex3v(tr->b); + glxVertex3v(tr->c); + } + glEnd(); +} + static void GLX_CALLBACK Vertex(Vector *p) { glxVertex3v(*p); } @@ -220,26 +236,22 @@ void glxDebugEdgeList(SEdgeList *el) void glxDebugMesh(SMesh *m) { int i; - glLineWidth(2); + glLineWidth(1); glPointSize(7); - glDisable(GL_DEPTH_TEST); glxUnlockColor(); for(i = 0; i < m->l.n; i++) { STriangle *t = &(m->l.elem[i]); if(t->tag) continue; - glxColor4d(0, 1, 0, 0.3); - glBegin(GL_LINE_LOOP); - glxVertex3v(t->a); - glxVertex3v(t->b); - glxVertex3v(t->c); - glEnd(); - glxColor4d(0, 0, 1, 0.4); - glBegin(GL_POINTS); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glxColor4d(0, 1, 0, 1.0); + glBegin(GL_TRIANGLES); glxVertex3v(t->a); glxVertex3v(t->b); glxVertex3v(t->c); glEnd(); + glPolygonOffset(0, 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } } diff --git a/graphicswin.cpp b/graphicswin.cpp index 8dc5e2a..c9c39ef 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -113,7 +113,7 @@ void GraphicsWindow::Init(void) { showPoints = true; showConstraints = true; showHdnLines = false; - showSolids = false; + showSolids = true; solving = SOLVE_ALWAYS; @@ -1181,11 +1181,31 @@ void GraphicsWindow::Paint(int w, int h) { selection[i].Draw(); } - if(SS.group.n >= 2) { - SMesh m; ZERO(&m); - (SS.group.elem[1].poly).TriangulateInto(&m); - glxDebugMesh(&m); - m.Clear(); + if(SS.group.n >= 5) { + SMesh *ma = &(SS.group.elem[2].mesh); + SMesh *mb = &(SS.group.elem[4].mesh); + + SBsp3 *pa = SBsp3::FromMesh(ma); + SBsp3 *pb = SBsp3::FromMesh(mb); + + SMesh br; ZERO(&br); + for(i = 0; i < mb->l.n; i++) { + pa->Insert(&(mb->l.elem[i]), &br, true, false); + } + for(i = 0; i < ma->l.n; i++) { + pb->Insert(&(ma->l.elem[i]), &br, false, false); + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glxFillMesh(&br); + glDisable(GL_LIGHTING); + glxLockColorTo(0, 1, 0); + glEnable(GL_DEPTH_TEST); + glxDebugMesh(&br); + + br.Clear(); + FreeAllTemporary(); } } diff --git a/mesh.cpp b/mesh.cpp index 6671dc4..05a5507 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -4,6 +4,16 @@ void SMesh::Clear(void) { l.Clear(); } +void SMesh::AddTriangle(Vector n, Vector a, Vector b, Vector c) { + Vector ab = b.Minus(a), bc = c.Minus(b); + Vector np = ab.Cross(bc); + if(np.Dot(n) > 0) { + AddTriangle(a, b, c); + } else { + AddTriangle(c, b, a); + } +} + void SMesh::AddTriangle(Vector a, Vector b, Vector c) { STriangle t; ZERO(&t); t.a = a; @@ -12,3 +22,215 @@ void SMesh::AddTriangle(Vector a, Vector b, Vector c) { l.Add(&t); } +SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); } +SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); } + +SBsp3 *SBsp3::FromMesh(SMesh *m) { + int i; + SBsp3 *ret = NULL; + for(i = 0; i < m->l.n; i++) { + ret = ret->Insert(&(m->l.elem[i]), NULL, false, false); + } + return ret; +} + +Vector SBsp3::IntersectionWith(Vector a, Vector b) { + double da = a.Dot(n) - d; + double db = b.Dot(n) - d; + if(da*db > 0) oops(); + + double dab = (db - da); + Vector r = (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab)); + return r; +} + +void SBsp3::InsertHow(int how, STriangle *tr, + SMesh *instead, bool flip, bool cpl) +{ + switch(how) { + case POS: + if(instead && !pos) goto alt; + pos = pos->Insert(tr, instead, flip, cpl); + break; + + case NEG: + if(instead && !neg) goto alt; + neg = neg->Insert(tr, instead, flip, cpl); + break; + + case COPLANAR: { + if(instead) goto alt; + SBsp3 *m = Alloc(); + m->n = n; + m->d = d; + m->tri = *tr; + m->more = more; + more = m; + break; + } + default: oops(); + } + return; + +alt: + if(how == POS && !flip) { + instead->AddTriangle(tr->a, tr->b, tr->c); + } + if(how == NEG && flip) { + 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); + } +} + +SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead, bool flip, bool cpl) { + if(!this) { + // Brand new node; so allocate for it, and fill us in. + SBsp3 *r = Alloc(); + r->n = tr->Normal(); + r->d = (tr->a).Dot(r->n); + r->tri = *tr; + return r; + } + + 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); + // Count vertices in the plane + for(int i = 0; i < 3; i++) { + if(fabs(dt[i] - d) < LENGTH_EPS) { + inc++; + ison[i] = true; + } else if(dt[i] > d) { + posc++; + ispos[i] = true; + } else { + negc++; + isneg[i] = true; + } + } + + // All vertices in-plane + if(inc == 3) { + InsertHow(COPLANAR, tr, instead, flip, cpl); + return this; + } + + // No split required + if(posc == 0 || negc == 0) { + if(inc == 2) { + // Two vertices in-plane, other above or below + // XXX do edge bsp + } + + if(posc > 0) { + InsertHow(POS, tr, instead, flip, cpl); + } else { + InsertHow(NEG, tr, instead, flip, cpl); + } + return this; + } + + // 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]; + } else oops(); + + Vector bPc = IntersectionWith(b, c); + STriangle btri = { 0, a, b, bPc }; + STriangle ctri = { 0, c, a, bPc }; + + if(bpos) { + InsertHow(POS, &btri, instead, flip, cpl); + InsertHow(NEG, &ctri, instead, flip, cpl); + } else { + InsertHow(POS, &ctri, instead, flip, cpl); + InsertHow(NEG, &btri, instead, flip, cpl); + } + + 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; + } 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) { + InsertHow(POS, &quad1, instead, flip, cpl); + InsertHow(POS, &quad2, instead, flip, cpl); + InsertHow(NEG, &alone, instead, flip, cpl); + } else { + InsertHow(NEG, &quad1, instead, flip, cpl); + InsertHow(NEG, &quad2, instead, flip, cpl); + InsertHow(POS, &alone, instead, flip, cpl); + } + return this; +} + +void SBsp3::DebugDraw(void) { + if(!this) return; + + pos->DebugDraw(); + Vector norm = tri.Normal(); + glNormal3d(norm.x, norm.y, norm.z); + + glEnable(GL_LIGHTING); + glBegin(GL_TRIANGLES); + glxVertex3v(tri.a); + glxVertex3v(tri.b); + glxVertex3v(tri.c); + glEnd(); + + glDisable(GL_LIGHTING); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glPolygonOffset(-1, 0); + glBegin(GL_TRIANGLES); + glxVertex3v(tri.a); + glxVertex3v(tri.b); + glxVertex3v(tri.c); + glEnd(); + + glDisable(GL_LIGHTING); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + glPointSize(10); + glPolygonOffset(-1, 0); + glBegin(GL_TRIANGLES); + glxVertex3v(tri.a); + glxVertex3v(tri.b); + glxVertex3v(tri.c); + glEnd(); + + glPolygonOffset(0, 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + more->DebugDraw(); + neg->DebugDraw(); +} + diff --git a/polygon.cpp b/polygon.cpp index e58b6d4..9fa65ea 100644 --- a/polygon.cpp +++ b/polygon.cpp @@ -1,5 +1,10 @@ #include "solvespace.h" +Vector STriangle::Normal(void) { + Vector ab = b.Minus(a), bc = c.Minus(b); + return ab.Cross(bc); +} + void SEdgeList::Clear(void) { l.Clear(); } @@ -222,6 +227,7 @@ void SPolygon::FixContourDirections(void) { static int TriMode, TriVertexCount; static Vector Tri1, TriNMinus1, TriNMinus2; +static Vector TriNormal; static SMesh *TriMesh; static void GLX_CALLBACK TriBegin(int mode) { @@ -238,15 +244,15 @@ static void GLX_CALLBACK TriVertex(Vector *triN) } if(TriMode == GL_TRIANGLES) { if((TriVertexCount % 3) == 2) { - TriMesh->AddTriangle(TriNMinus2, TriNMinus1, *triN); + TriMesh->AddTriangle(TriNormal, TriNMinus2, TriNMinus1, *triN); } } else if(TriMode == GL_TRIANGLE_FAN) { if(TriVertexCount >= 2) { - TriMesh->AddTriangle(Tri1, TriNMinus1, *triN); + TriMesh->AddTriangle(TriNormal, Tri1, TriNMinus1, *triN); } } else if(TriMode == GL_TRIANGLE_STRIP) { if(TriVertexCount >= 2) { - TriMesh->AddTriangle(TriNMinus2, TriNMinus1, *triN); + TriMesh->AddTriangle(TriNormal, TriNMinus2, TriNMinus1, *triN); } } else oops(); @@ -256,6 +262,7 @@ static void GLX_CALLBACK TriVertex(Vector *triN) } void SPolygon::TriangulateInto(SMesh *m) { TriMesh = m; + TriNormal = normal; GLUtesselator *gt = gluNewTess(); gluTessCallback(gt, GLU_TESS_BEGIN, (glxCallbackFptr *)TriBegin); diff --git a/polygon.h b/polygon.h index a7c5da3..286d9fc 100644 --- a/polygon.h +++ b/polygon.h @@ -85,6 +85,8 @@ class STriangle { public: int tag; Vector a, b, c; + + Vector Normal(void); }; class SBsp2 { @@ -95,10 +97,16 @@ public: SBsp2 *neg; SBsp2 *more; + + void Insert(SEdge *se); + static SBsp2 *Alloc(void); }; class SBsp3 { public: + Vector n; + double d; + STriangle tri; SBsp3 *pos; SBsp3 *neg; @@ -106,6 +114,18 @@ public: SBsp3 *more; SBsp2 *edges; + + static SBsp3 *Alloc(void); + static SBsp3 *FromMesh(SMesh *m); + + Vector IntersectionWith(Vector a, Vector b); + + static const int POS = 100, NEG = 101, COPLANAR = 200; + void InsertHow(int how, STriangle *str, SMesh *instead, bool flip,bool cpl); + + SBsp3 *Insert(STriangle *str, SMesh *instead, bool flip, bool cpl); + + void DebugDraw(void); }; @@ -115,6 +135,7 @@ public: void Clear(void); void AddTriangle(Vector a, Vector b, Vector c); + void AddTriangle(Vector n, Vector a, Vector b, Vector c); }; #endif diff --git a/sketch.cpp b/sketch.cpp index 3ea380a..edb8832 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -384,6 +384,7 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz, void Group::MakePolygons(void) { int i; poly.Clear(); + mesh.Clear(); SEdgeList edges; ZERO(&edges); @@ -400,70 +401,75 @@ void Group::MakePolygons(void) { if(edges.AssemblePolygon(&poly, &error)) { polyError.yes = false; poly.normal = poly.ComputeNormal(); + poly.FixContourDirections(); } else { polyError.yes = true; polyError.notClosedAt = error; poly.Clear(); } } else if(type == EXTRUDE) { - Vector translate; - translate.x = SS.GetParam(h.param(0))->val; - translate.y = SS.GetParam(h.param(1))->val; - translate.z = SS.GetParam(h.param(2))->val; - Vector t0, dt; + int i; + Group *src = SS.GetGroup(opA); + Vector translate = Vector::MakeFrom( + SS.GetParam(h.param(0))->val, + SS.GetParam(h.param(1))->val, + SS.GetParam(h.param(2))->val + ); + Vector tbot, ttop; if(subtype == EXTRUDE_ONE_SIDED) { - t0 = Vector::MakeFrom(0, 0, 0); dt = translate; + tbot = Vector::MakeFrom(0, 0, 0); ttop = translate; } else { - t0 = translate.ScaledBy(-1); dt = translate.ScaledBy(2); + tbot = translate.ScaledBy(-1); ttop = translate.ScaledBy(1); } + bool flipBottom = translate.Dot(src->poly.normal) > 0; + // Get a triangulation of the source poly; this is not a closed mesh. + SMesh srcm; ZERO(&srcm); + (src->poly).TriangulateInto(&srcm); + + SMesh outm; ZERO(&outm); + // Do the bottom; that has normal pointing opposite from translate + for(i = 0; i < srcm.l.n; i++) { + STriangle *st = &(srcm.l.elem[i]); + Vector at = (st->a).Plus(tbot), + bt = (st->b).Plus(tbot), + ct = (st->c).Plus(tbot); + if(flipBottom) { + mesh.AddTriangle(ct, bt, at); + } else { + mesh.AddTriangle(at, bt, ct); + } + } + // And the top; that has the normal pointing the same dir as translate + for(i = 0; i < srcm.l.n; i++) { + STriangle *st = &(srcm.l.elem[i]); + Vector at = (st->a).Plus(ttop), + bt = (st->b).Plus(ttop), + ct = (st->c).Plus(ttop); + if(flipBottom) { + mesh.AddTriangle(at, bt, ct); + } else { + mesh.AddTriangle(ct, bt, at); + } + } + srcm.Clear(); // Get the source polygon to extrude, and break it down to edges edges.Clear(); - Group *src = SS.GetGroup(opA); - (src->poly).MakeEdgesInto(&edges); + + // The sides; these are quads, represented as two triangles. for(i = 0; i < edges.l.n; i++) { SEdge *edge = &(edges.l.elem[i]); - edge->a = (edge->a).Plus(t0); - edge->b = (edge->b).Plus(t0); + Vector abot = (edge->a).Plus(tbot), bbot = (edge->b).Plus(tbot); + Vector atop = (edge->a).Plus(ttop), btop = (edge->b).Plus(ttop); + if(flipBottom) { + mesh.AddTriangle(bbot, abot, atop); + mesh.AddTriangle(bbot, atop, btop); + } else { + mesh.AddTriangle(abot, bbot, atop); + mesh.AddTriangle(bbot, btop, atop); + } } - - SPolygon np; - memset(&np, 0, sizeof(np)); - // The bottom - if(!edges.AssemblePolygon(&np, NULL)) oops(); - Vector n = np.ComputeNormal(); - if(translate.Dot(n) > 0) { - n = n.ScaledBy(-1); - } - np.normal = n; - np.FixContourDirections(); - - // Regenerate the edges, with the contour directions fixed up. - edges.Clear(); - np.MakeEdgesInto(&edges); - - // The sides - int i; - for(i = 0; i < edges.l.n; i++) { - SEdge *edge = &(edges.l.elem[i]); - memset(&np, 0, sizeof(np)); - np.AddEmptyContour(); - np.AddPoint(edge->a); - np.AddPoint(edge->b); - np.AddPoint((edge->b).Plus(dt)); - np.AddPoint((edge->a).Plus(dt)); - np.AddPoint(edge->a); - np.normal = ((edge->a).Minus(edge->b).Cross(n)).WithMagnitude(1); - - edge->a = (edge->a).Plus(dt); - edge->b = (edge->b).Plus(dt); - } - - // The top - memset(&np, 0, sizeof(np)); - if(!edges.AssemblePolygon(&np, NULL)) oops(); - np.normal = n.ScaledBy(-1); } edges.Clear(); } @@ -471,6 +477,12 @@ void Group::MakePolygons(void) { void Group::Draw(void) { if(!visible) return; + + GLfloat mpf[] = { 0.4f, 0.4f, 0.4f, 1.0 }; + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf); + GLfloat mpb[] = { 1.0f, 0.1f, 0.1f, 1.0 }; + glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, mpb); + if(polyError.yes) { glxColor4d(1, 0, 0, 0.2); glLineWidth(10); @@ -486,16 +498,13 @@ void Group::Draw(void) { glxWriteText("not closed contour!"); glPopMatrix(); } else { - int i; - glEnable(GL_LIGHTING); - GLfloat mpf[] = { 0.3f, 1.0f, 0.3f, 0.5 }; - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mpf); - GLfloat mpb[] = { 1.0f, 0.3f, 0.3f, 0.5 }; - glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, mpb); - glxFillPolygon(&poly); - - glDisable(GL_LIGHTING); +// glxFillPolygon(&poly); } + glEnable(GL_LIGHTING); +// glxFillMesh(&mesh); + glDisable(GL_LIGHTING); + +// glxDebugMesh(&mesh); } hParam Request::AddParam(IdList *param, hParam hp) { diff --git a/solvespace.h b/solvespace.h index cb2f067..faefb90 100644 --- a/solvespace.h +++ b/solvespace.h @@ -77,6 +77,7 @@ void glxVertex3v(Vector u); typedef void GLX_CALLBACK glxCallbackFptr(void); void glxTesselatePolygon(GLUtesselator *gt, SPolygon *p); void glxFillPolygon(SPolygon *p); +void glxFillMesh(SMesh *m); void glxDebugPolygon(SPolygon *p); void glxDebugEdgeList(SEdgeList *l); void glxDebugMesh(SMesh *m);