Add code to extrude a triangle mesh, and to perform Boolean ops on

a triangle mesh in a BSP. That works, although it splits too often,
the initial triangulations are not good quality, and coplanar faces
are not yet handled. I'll do the coplanar thing tomorrow.

[git-p4: depot-paths = "//depot/solvespace/": change = 1735]
solver
Jonathan Westhues 2008-05-23 02:05:07 -08:00
parent c079497762
commit de46118324
7 changed files with 368 additions and 76 deletions

View File

@ -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);
}
}

View File

@ -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();
}
}

222
mesh.cpp
View File

@ -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();
}

View File

@ -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);

View File

@ -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

View File

@ -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> *param, hParam hp) {

View File

@ -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);