solvespace/mesh.cpp

701 lines
19 KiB
C++
Raw Normal View History

#include "solvespace.h"
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.Magnitude() < 1e-10) {
// ugh; gl sometimes tesselates to collinear triangles
return;
}
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;
t.b = b;
t.c = c;
AddTriangle(&t);
}
void SMesh::AddTriangle(STriangle *st) {
l.Add(st);
}
void SMesh::DoBounding(Vector v, Vector *vmax, Vector *vmin) {
vmax->x = max(vmax->x, v.x);
vmax->y = max(vmax->y, v.y);
vmax->z = max(vmax->z, v.z);
vmin->x = min(vmin->x, v.x);
vmin->y = min(vmin->y, v.y);
vmin->z = min(vmin->z, v.z);
}
void SMesh::GetBounding(Vector *vmax, Vector *vmin) {
int i;
*vmin = Vector::MakeFrom( 1e12, 1e12, 1e12);
*vmax = Vector::MakeFrom(-1e12, -1e12, -1e12);
for(i = 0; i < l.n; i++) {
STriangle *st = &(l.elem[i]);
DoBounding(st->a, vmax, vmin);
DoBounding(st->b, vmax, vmin);
DoBounding(st->c, vmax, vmin);
}
}
void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) {
int i;
for(i = 0; i < srcm->l.n; i++) {
STriangle *st = &(srcm->l.elem[i]);
int pn = l.n;
atLeastOneDiscarded = false;
bsp3->Insert(st, this);
if(!atLeastOneDiscarded && (l.n != (pn+1))) {
l.n = pn;
if(flipNormal) {
AddTriangle(st->c, st->b, st->a);
} else {
AddTriangle(st->a, st->b, st->c);
}
continue;
}
}
}
void SMesh::MakeFromUnion(SMesh *a, SMesh *b) {
SBsp3 *bspa = SBsp3::FromMesh(a);
SBsp3 *bspb = SBsp3::FromMesh(b);
flipNormal = false;
keepCoplanar = false;
AddAgainstBsp(b, bspa);
flipNormal = false;
keepCoplanar = true;
AddAgainstBsp(a, bspb);
}
void SMesh::MakeFromDifference(SMesh *a, SMesh *b) {
SBsp3 *bspa = SBsp3::FromMesh(a);
SBsp3 *bspb = SBsp3::FromMesh(b);
flipNormal = true;
keepCoplanar = false;
AddAgainstBsp(b, bspa);
flipNormal = false;
keepCoplanar = false;
AddAgainstBsp(a, bspb);
}
SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
SBsp3 *SBsp3::FromMesh(SMesh *m) {
SBsp3 *bsp3 = NULL;
int i;
for(i = 0; i < m->l.n; i++) {
bsp3 = bsp3->Insert(&(m->l.elem[i]), NULL);
}
return bsp3;
}
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);
return (a.ScaledBy(db/dab)).Plus(b.ScaledBy(-da/dab));
}
void SBsp3::InsertInPlane(bool pos2, STriangle *tr, SMesh *m) {
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(m->flipNormal && ((!pos2 && !onFace) || (onFace && !sameNormal))) {
m->AddTriangle(tr->c, tr->b, tr->a);
} else if(!(m->flipNormal) && ((pos2 && !onFace) ||
(onFace && sameNormal && m->keepCoplanar)))
{
m->AddTriangle(tr->a, tr->b, tr->c);
} else {
m->atLeastOneDiscarded = true;
}
}
void SBsp3::InsertHow(int how, STriangle *tr, SMesh *instead) {
switch(how) {
case POS:
if(instead && !pos) goto alt;
pos = pos->Insert(tr, instead);
break;
case NEG:
if(instead && !neg) goto alt;
neg = neg->Insert(tr, instead);
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 && !(instead->flipNormal)) {
instead->AddTriangle(tr->a, tr->b, tr->c);
} else if(how == NEG && instead->flipNormal) {
instead->AddTriangle(tr->c, tr->b, tr->a);
} else if(how == COPLANAR) {
if(edges) {
edges->InsertTriangle(tr, instead, this);
} 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(false, tr, instead);
}
} else {
instead->atLeastOneDiscarded = true;
}
}
void SBsp3::InsertConvexHow(int how, Vector *vertex, int n, SMesh *instead) {
switch(how) {
case POS:
if(pos) {
pos = pos->InsertConvex(vertex, n, instead);
return;
}
break;
case NEG:
if(neg) {
neg = neg->InsertConvex(vertex, n, instead);
return;
}
break;
default: oops();
}
int i;
for(i = 0; i < n - 2; i++) {
STriangle tr = { 0, vertex[0], vertex[i+1], vertex[i+2] };
InsertHow(how, &tr, instead);
}
}
SBsp3 *SBsp3::InsertConvex(Vector *vertex, int cnt, SMesh *instead) {
Vector e01 = (vertex[1]).Minus(vertex[0]);
Vector e12 = (vertex[2]).Minus(vertex[1]);
Vector out = e01.Cross(e12);
int i;
Vector on[2];
bool *isPos = (bool *)AllocTemporary(cnt*sizeof(bool));
bool *isNeg = (bool *)AllocTemporary(cnt*sizeof(bool));
bool *isOn = (bool *)AllocTemporary(cnt*sizeof(bool));
int posc = 0, negc = 0, onc = 0;
for(i = 0; i < cnt; i++) {
double dt = n.Dot(vertex[i]);
isPos[i] = isNeg[i] = isOn[i] = false;
if(fabs(dt - d) < LENGTH_EPS) {
isOn[i] = true;
if(onc < 2) {
on[onc] = vertex[i];
}
onc++;
} else if(dt > d) {
isPos[i] = true;
posc++;
} else {
isNeg[i] = true;
negc++;
}
}
if(onc != 2 && onc != 1 && onc != 0) goto triangulate;
if(onc == 2) {
if(!instead) {
SEdge se = { 0, on[0], on[1] };
edges = edges->InsertEdge(&se, n, out);
}
}
if(posc == 0) {
InsertConvexHow(NEG, vertex, cnt, instead);
return this;
}
if(negc == 0) {
InsertConvexHow(POS, vertex, cnt, instead);
return this;
}
Vector *vpos = (Vector *)AllocTemporary((cnt+1)*sizeof(Vector));
Vector *vneg = (Vector *)AllocTemporary((cnt+1)*sizeof(Vector));
int npos = 0, nneg = 0;
Vector inter[2];
int inters = 0;
for(i = 0; i < cnt; i++) {
int ip = (i + 1) % cnt;
if(isPos[i]) {
vpos[npos++] = vertex[i];
}
if(isNeg[i]) {
vneg[nneg++] = vertex[i];
}
if(isOn[i]) {
vneg[nneg++] = vertex[i];
vpos[npos++] = vertex[i];
}
if((isPos[i] && isNeg[ip]) || (isNeg[i] && isPos[ip])) {
Vector vi = IntersectionWith(vertex[i], vertex[ip]);
vpos[npos++] = vi;
vneg[nneg++] = vi;
if(inters >= 2) oops();
inter[inters++] = vi;
}
}
if(npos > cnt + 1 || nneg > cnt + 1) oops();
if(!instead) {
if(inters == 2) {
SEdge se = { 0, inter[0], inter[1] };
edges = edges->InsertEdge(&se, n, out);
} else if(inters == 1 && onc == 1) {
SEdge se = { 0, inter[0], on[0] };
edges = edges->InsertEdge(&se, n, out);
} else if(inters == 0 && onc == 2) {
// We already handled this on-plane existing edge
} else oops();
}
if(nneg < 3 || npos < 3) oops();
InsertConvexHow(NEG, vneg, nneg, instead);
InsertConvexHow(POS, vpos, npos, instead);
return this;
triangulate:
// We don't handle the special case for this; do it as triangles
SBsp3 *r = this;
for(i = 0; i < cnt - 2; i++) {
STriangle tr = { 0, vertex[0], vertex[i+1], vertex[i+2] };
r = r->Insert(&tr, instead);
}
return r;
}
SBsp3 *SBsp3::Insert(STriangle *tr, SMesh *instead) {
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);
return this;
}
// No split required
if(posc == 0 || negc == 0) {
if(inc == 2) {
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) {
InsertHow(POS, tr, instead);
} else {
InsertHow(NEG, tr, instead);
}
return this;
}
// 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) {
InsertHow(POS, &btri, instead);
InsertHow(NEG, &ctri, instead);
} else {
InsertHow(POS, &ctri, instead);
InsertHow(NEG, &btri, instead);
}
if(!instead) {
SEdge se = { 0, a, bPc };
edges = edges->InsertEdge(&se, n, tr->Normal());
}
return this;
}
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 };
Vector quad[4] = { aPb, b, c, cPa };
STriangle quad1 = { 0, aPb, b, c };
STriangle quad2 = { 0, aPb, c, cPa };
if(posc == 2 && negc == 1) {
InsertConvexHow(POS, quad, 4, instead);
InsertHow(NEG, &alone, instead);
} else {
InsertConvexHow(NEG, quad, 4, instead);
InsertHow(POS, &alone, instead);
}
if(!instead) {
SEdge se = { 0, aPb, cPa };
edges = edges->InsertEdge(&se, n, alone.Normal());
}
return this;
}
void SBsp3::DebugDraw(void) {
if(!this) return;
pos->DebugDraw();
Vector norm = tri.Normal();
glNormal3d(norm.x, norm.y, norm.z);
glEnable(GL_DEPTH_TEST);
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();
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) {
switch(how) {
case POS:
if(pos) {
pos->InsertTriangle(tr, m, bsp3);
} else {
bsp3->InsertInPlane(true, tr, m);
}
break;
case NEG:
if(neg) {
neg->InsertTriangle(tr, m, bsp3);
} else {
bsp3->InsertInPlane(false, tr, m);
}
break;
default: oops();
}
}
void SBsp2::InsertTriangle(STriangle *tr, SMesh *m, SBsp3 *bsp3) {
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);
} else {
InsertTriangleHow(NEG, tr, m, bsp3);
}
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);
InsertTriangleHow(NEG, &ctri, m, bsp3);
} else {
InsertTriangleHow(POS, &ctri, m, bsp3);
InsertTriangleHow(NEG, &btri, m, bsp3);
}
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);
InsertTriangleHow(POS, &quad2, m, bsp3);
InsertTriangleHow(NEG, &alone, m, bsp3);
} else {
InsertTriangleHow(NEG, &quad1, m, bsp3);
InsertTriangleHow(NEG, &quad2, m, bsp3);
InsertTriangleHow(POS, &alone, m, bsp3);
}
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);
}