2013-07-28 22:08:34 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Operations on triangle meshes, like our mesh Booleans using the BSP, and
|
|
|
|
// the stuff to check for watertightness.
|
|
|
|
//
|
|
|
|
// Copyright 2008-2013 Jonathan Westhues.
|
|
|
|
//-----------------------------------------------------------------------------
|
2008-05-22 10:28:50 +00:00
|
|
|
#include "solvespace.h"
|
|
|
|
|
2016-03-30 08:01:44 +00:00
|
|
|
#include <set>
|
|
|
|
|
2016-05-05 05:54:05 +00:00
|
|
|
void SMesh::Clear() {
|
2008-05-22 10:28:50 +00:00
|
|
|
l.Clear();
|
|
|
|
}
|
|
|
|
|
2008-06-21 10:18:20 +00:00
|
|
|
void SMesh::AddTriangle(STriMeta meta, Vector n, Vector a, Vector b, Vector c) {
|
2008-05-23 10:05:07 +00:00
|
|
|
Vector ab = b.Minus(a), bc = c.Minus(b);
|
|
|
|
Vector np = ab.Cross(bc);
|
2008-05-24 13:23:25 +00:00
|
|
|
if(np.Magnitude() < 1e-10) {
|
|
|
|
// ugh; gl sometimes tesselates to collinear triangles
|
|
|
|
return;
|
|
|
|
}
|
2008-05-23 10:05:07 +00:00
|
|
|
if(np.Dot(n) > 0) {
|
2008-05-30 06:09:41 +00:00
|
|
|
AddTriangle(meta, a, b, c);
|
2008-05-23 10:05:07 +00:00
|
|
|
} else {
|
2008-05-30 06:09:41 +00:00
|
|
|
AddTriangle(meta, c, b, a);
|
2008-05-23 10:05:07 +00:00
|
|
|
}
|
|
|
|
}
|
2008-05-30 06:09:41 +00:00
|
|
|
void SMesh::AddTriangle(STriMeta meta, Vector a, Vector b, Vector c) {
|
2015-03-27 15:31:23 +00:00
|
|
|
STriangle t = {};
|
2008-05-30 06:09:41 +00:00
|
|
|
t.meta = meta;
|
2008-05-22 10:28:50 +00:00
|
|
|
t.a = a;
|
|
|
|
t.b = b;
|
|
|
|
t.c = c;
|
2008-05-24 12:23:25 +00:00
|
|
|
AddTriangle(&t);
|
|
|
|
}
|
2016-05-21 05:18:00 +00:00
|
|
|
void SMesh::AddTriangle(const STriangle *st) {
|
2008-05-24 12:23:25 +00:00
|
|
|
l.Add(st);
|
|
|
|
}
|
|
|
|
|
2016-05-21 05:18:00 +00:00
|
|
|
void SMesh::DoBounding(Vector v, Vector *vmax, Vector *vmin) const {
|
2008-05-24 12:23:25 +00:00
|
|
|
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);
|
|
|
|
}
|
2016-05-21 05:18:00 +00:00
|
|
|
void SMesh::GetBounding(Vector *vmax, Vector *vmin) const {
|
2008-05-24 12:23:25 +00:00
|
|
|
int i;
|
2008-06-01 08:45:11 +00:00
|
|
|
*vmin = Vector::From( 1e12, 1e12, 1e12);
|
|
|
|
*vmax = Vector::From(-1e12, -1e12, -1e12);
|
2008-05-24 12:23:25 +00:00
|
|
|
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);
|
|
|
|
}
|
2008-05-22 10:28:50 +00:00
|
|
|
}
|
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Report the edges of the boundary of the region(s) of our mesh that lie
|
|
|
|
// within the plane n dot p = d.
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
void SMesh::MakeEdgesInPlaneInto(SEdgeList *sel, Vector n, double d) {
|
2015-03-27 15:31:23 +00:00
|
|
|
SMesh m = {};
|
2009-05-28 07:07:54 +00:00
|
|
|
m.MakeFromCopyOf(this);
|
|
|
|
|
|
|
|
// Delete all triangles in the mesh that do not lie in our export plane.
|
|
|
|
m.l.ClearTags();
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < m.l.n; i++) {
|
|
|
|
STriangle *tr = &(m.l.elem[i]);
|
|
|
|
|
|
|
|
if((fabs(n.Dot(tr->a) - d) >= LENGTH_EPS) ||
|
|
|
|
(fabs(n.Dot(tr->b) - d) >= LENGTH_EPS) ||
|
|
|
|
(fabs(n.Dot(tr->c) - d) >= LENGTH_EPS))
|
|
|
|
{
|
|
|
|
tr->tag = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m.l.RemoveTagged();
|
|
|
|
|
|
|
|
// Select the naked edges in our resulting open mesh.
|
|
|
|
SKdNode *root = SKdNode::From(&m);
|
|
|
|
root->SnapToMesh(&m);
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
root->MakeCertainEdgesInto(sel, EdgeKind::NAKED_OR_SELF_INTER,
|
2016-05-25 12:08:19 +00:00
|
|
|
/*coplanarIsInter=*/false, NULL, NULL);
|
2009-05-28 07:07:54 +00:00
|
|
|
|
|
|
|
m.Clear();
|
|
|
|
}
|
|
|
|
|
2016-06-26 06:39:27 +00:00
|
|
|
void SMesh::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) {
|
2009-05-29 05:40:17 +00:00
|
|
|
SKdNode *root = SKdNode::From(this);
|
2016-06-26 06:39:27 +00:00
|
|
|
root->MakeOutlinesInto(sol, edgeKind);
|
2009-05-29 05:40:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// When we are called, all of the triangles from l.elem[start] to the end must
|
|
|
|
// be coplanar. So we try to find a set of fewer triangles that covers the
|
|
|
|
// exact same area, in order to reduce the number of triangles in the mesh.
|
|
|
|
// We use this after a triangle has been split against the BSP.
|
|
|
|
//
|
|
|
|
// This is really ugly code; basically it just pastes things together to
|
|
|
|
// form convex polygons, merging collinear edges when possible, then
|
|
|
|
// triangulates the convex poly.
|
|
|
|
//-----------------------------------------------------------------------------
|
2008-05-26 03:39:45 +00:00
|
|
|
void SMesh::Simplify(int start) {
|
2008-07-13 12:58:52 +00:00
|
|
|
int maxTriangles = (l.n - start) + 10;
|
2008-05-26 03:39:45 +00:00
|
|
|
|
2008-05-30 06:09:41 +00:00
|
|
|
STriMeta meta = l.elem[start].meta;
|
|
|
|
|
2008-07-13 12:58:52 +00:00
|
|
|
STriangle *tout = (STriangle *)AllocTemporary(maxTriangles*sizeof(*tout));
|
2008-05-26 03:39:45 +00:00
|
|
|
int toutc = 0;
|
|
|
|
|
2013-08-26 19:36:00 +00:00
|
|
|
Vector n = Vector::From(0, 0, 0);
|
|
|
|
Vector *conv = (Vector *)AllocTemporary(maxTriangles*3*sizeof(*conv));
|
2008-05-26 03:39:45 +00:00
|
|
|
int convc = 0;
|
|
|
|
|
|
|
|
int start0 = start;
|
|
|
|
|
|
|
|
int i, j;
|
|
|
|
for(i = start; i < l.n; i++) {
|
2008-05-26 06:23:05 +00:00
|
|
|
STriangle *tr = &(l.elem[i]);
|
2008-07-02 04:32:24 +00:00
|
|
|
if(tr->MinAltitude() < LENGTH_EPS) {
|
|
|
|
tr->tag = 1;
|
2008-05-26 06:23:05 +00:00
|
|
|
} else {
|
|
|
|
tr->tag = 0;
|
|
|
|
}
|
2008-05-26 03:39:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for(;;) {
|
2008-05-26 06:23:05 +00:00
|
|
|
bool didAdd;
|
2008-05-26 03:39:45 +00:00
|
|
|
convc = 0;
|
|
|
|
for(i = start; i < l.n; i++) {
|
|
|
|
STriangle *tr = &(l.elem[i]);
|
|
|
|
if(tr->tag) continue;
|
|
|
|
|
|
|
|
tr->tag = 1;
|
|
|
|
n = (tr->Normal()).WithMagnitude(1);
|
|
|
|
conv[convc++] = tr->a;
|
|
|
|
conv[convc++] = tr->b;
|
|
|
|
conv[convc++] = tr->c;
|
|
|
|
|
|
|
|
start = i+1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(i >= l.n) break;
|
|
|
|
|
|
|
|
do {
|
|
|
|
didAdd = false;
|
|
|
|
|
|
|
|
for(j = 0; j < convc; j++) {
|
|
|
|
Vector a = conv[WRAP((j-1), convc)],
|
2015-03-29 00:30:52 +00:00
|
|
|
b = conv[j],
|
2008-05-26 03:39:45 +00:00
|
|
|
d = conv[WRAP((j+1), convc)],
|
|
|
|
e = conv[WRAP((j+2), convc)];
|
|
|
|
|
|
|
|
Vector c;
|
|
|
|
for(i = start; i < l.n; i++) {
|
|
|
|
STriangle *tr = &(l.elem[i]);
|
|
|
|
if(tr->tag) continue;
|
|
|
|
|
|
|
|
if((tr->a).Equals(d) && (tr->b).Equals(b)) {
|
|
|
|
c = tr->c;
|
|
|
|
} else if((tr->b).Equals(d) && (tr->c).Equals(b)) {
|
|
|
|
c = tr->a;
|
|
|
|
} else if((tr->c).Equals(d) && (tr->a).Equals(b)) {
|
|
|
|
c = tr->b;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// The vertex at C must be convex; but the others must
|
|
|
|
// be tested
|
|
|
|
Vector ab = b.Minus(a);
|
|
|
|
Vector bc = c.Minus(b);
|
|
|
|
Vector cd = d.Minus(c);
|
|
|
|
Vector de = e.Minus(d);
|
|
|
|
|
|
|
|
double bDot = (ab.Cross(bc)).Dot(n);
|
|
|
|
double dDot = (cd.Cross(de)).Dot(n);
|
|
|
|
|
|
|
|
bDot /= min(ab.Magnitude(), bc.Magnitude());
|
|
|
|
dDot /= min(cd.Magnitude(), de.Magnitude());
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2008-05-30 07:32:30 +00:00
|
|
|
if(fabs(bDot) < LENGTH_EPS && fabs(dDot) < LENGTH_EPS) {
|
|
|
|
conv[WRAP((j+1), convc)] = c;
|
|
|
|
// and remove the vertex at j, which is a dup
|
2015-03-29 00:30:52 +00:00
|
|
|
memmove(conv+j, conv+j+1,
|
2008-05-30 07:32:30 +00:00
|
|
|
(convc - j - 1)*sizeof(conv[0]));
|
|
|
|
convc--;
|
|
|
|
} else if(fabs(bDot) < LENGTH_EPS && dDot > 0) {
|
2008-05-26 03:39:45 +00:00
|
|
|
conv[j] = c;
|
2008-05-26 04:27:34 +00:00
|
|
|
} else if(fabs(dDot) < LENGTH_EPS && bDot > 0) {
|
2008-05-26 03:39:45 +00:00
|
|
|
conv[WRAP((j+1), convc)] = c;
|
|
|
|
} else if(bDot > 0 && dDot > 0) {
|
|
|
|
// conv[j] is unchanged, conv[j+1] goes to [j+2]
|
|
|
|
memmove(conv+j+2, conv+j+1,
|
|
|
|
(convc - j - 1)*sizeof(conv[0]));
|
|
|
|
conv[j+1] = c;
|
|
|
|
convc++;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
didAdd = true;
|
|
|
|
tr->tag = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while(didAdd);
|
|
|
|
|
2008-05-26 06:23:05 +00:00
|
|
|
// I need to debug why this is required; sometimes the above code
|
|
|
|
// still generates a convex polygon
|
|
|
|
for(i = 0; i < convc; i++) {
|
|
|
|
Vector a = conv[WRAP((i-1), convc)],
|
2015-03-29 00:30:52 +00:00
|
|
|
b = conv[i],
|
2008-05-26 06:23:05 +00:00
|
|
|
c = conv[WRAP((i+1), convc)];
|
|
|
|
Vector ab = b.Minus(a);
|
|
|
|
Vector bc = c.Minus(b);
|
|
|
|
double bDot = (ab.Cross(bc)).Dot(n);
|
|
|
|
bDot /= min(ab.Magnitude(), bc.Magnitude());
|
|
|
|
|
2008-02-10 13:34:32 +00:00
|
|
|
if(bDot < 0) return; // XXX, shouldn't happen
|
2008-05-26 06:23:05 +00:00
|
|
|
}
|
|
|
|
|
2008-05-26 03:39:45 +00:00
|
|
|
for(i = 0; i < convc - 2; i++) {
|
2008-05-30 06:09:41 +00:00
|
|
|
STriangle tr = STriangle::From(meta, conv[0], conv[i+1], conv[i+2]);
|
2008-07-02 04:32:24 +00:00
|
|
|
if(tr.MinAltitude() > LENGTH_EPS) {
|
2008-05-26 06:23:05 +00:00
|
|
|
tout[toutc++] = tr;
|
|
|
|
}
|
2008-05-26 03:39:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
l.n = start0;
|
|
|
|
for(i = 0; i < toutc; i++) {
|
|
|
|
AddTriangle(&(tout[i]));
|
|
|
|
}
|
2008-07-13 12:58:52 +00:00
|
|
|
FreeTemporary(tout);
|
|
|
|
FreeTemporary(conv);
|
2008-05-26 03:39:45 +00:00
|
|
|
}
|
|
|
|
|
2008-05-24 23:10:00 +00:00
|
|
|
void SMesh::AddAgainstBsp(SMesh *srcm, SBsp3 *bsp3) {
|
|
|
|
int i;
|
2008-05-25 13:11:44 +00:00
|
|
|
|
2008-05-24 23:10:00 +00:00
|
|
|
for(i = 0; i < srcm->l.n; i++) {
|
|
|
|
STriangle *st = &(srcm->l.elem[i]);
|
|
|
|
int pn = l.n;
|
|
|
|
atLeastOneDiscarded = false;
|
2016-04-27 12:48:40 +00:00
|
|
|
SBsp3::InsertOrCreate(bsp3, st, this);
|
2008-05-24 23:10:00 +00:00
|
|
|
if(!atLeastOneDiscarded && (l.n != (pn+1))) {
|
|
|
|
l.n = pn;
|
|
|
|
if(flipNormal) {
|
2008-05-30 06:09:41 +00:00
|
|
|
AddTriangle(st->meta, st->c, st->b, st->a);
|
2008-05-24 23:10:00 +00:00
|
|
|
} else {
|
2008-05-30 06:09:41 +00:00
|
|
|
AddTriangle(st->meta, st->a, st->b, st->c);
|
2008-05-24 23:10:00 +00:00
|
|
|
}
|
2008-05-26 03:39:45 +00:00
|
|
|
}
|
2008-05-26 06:23:05 +00:00
|
|
|
if(l.n - pn > 1) {
|
2008-05-26 03:39:45 +00:00
|
|
|
Simplify(pn);
|
2008-05-24 12:23:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-24 11:37:07 +00:00
|
|
|
void SMesh::MakeFromUnionOf(SMesh *a, SMesh *b) {
|
2008-05-25 13:11:44 +00:00
|
|
|
SBsp3 *bspa = SBsp3::FromMesh(a);
|
|
|
|
SBsp3 *bspb = SBsp3::FromMesh(b);
|
|
|
|
|
|
|
|
flipNormal = false;
|
|
|
|
keepCoplanar = false;
|
|
|
|
AddAgainstBsp(b, bspa);
|
|
|
|
|
|
|
|
flipNormal = false;
|
|
|
|
keepCoplanar = true;
|
|
|
|
AddAgainstBsp(a, bspb);
|
|
|
|
}
|
|
|
|
|
2009-05-24 11:37:07 +00:00
|
|
|
void SMesh::MakeFromDifferenceOf(SMesh *a, SMesh *b) {
|
2008-05-25 13:11:44 +00:00
|
|
|
SBsp3 *bspa = SBsp3::FromMesh(a);
|
|
|
|
SBsp3 *bspb = SBsp3::FromMesh(b);
|
|
|
|
|
|
|
|
flipNormal = true;
|
2008-06-04 06:39:32 +00:00
|
|
|
keepCoplanar = true;
|
2008-05-25 13:11:44 +00:00
|
|
|
AddAgainstBsp(b, bspa);
|
|
|
|
|
|
|
|
flipNormal = false;
|
|
|
|
keepCoplanar = false;
|
|
|
|
AddAgainstBsp(a, bspb);
|
|
|
|
}
|
|
|
|
|
2009-05-24 11:37:07 +00:00
|
|
|
void SMesh::MakeFromCopyOf(SMesh *a) {
|
2008-06-21 22:49:57 +00:00
|
|
|
int i;
|
|
|
|
for(i = 0; i < a->l.n; i++) {
|
|
|
|
AddTriangle(&(a->l.elem[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-24 11:37:07 +00:00
|
|
|
void SMesh::MakeFromAssemblyOf(SMesh *a, SMesh *b) {
|
|
|
|
MakeFromCopyOf(a);
|
|
|
|
MakeFromCopyOf(b);
|
|
|
|
}
|
|
|
|
|
2016-05-21 05:18:00 +00:00
|
|
|
void SMesh::MakeFromTransformationOf(SMesh *a, Vector trans,
|
|
|
|
Quaternion q, double scale)
|
2009-10-09 12:57:10 +00:00
|
|
|
{
|
2009-05-24 11:37:07 +00:00
|
|
|
STriangle *tr;
|
|
|
|
for(tr = a->l.First(); tr; tr = a->l.NextAfter(tr)) {
|
|
|
|
STriangle tt = *tr;
|
2009-12-15 12:26:22 +00:00
|
|
|
tt.a = (tt.a).ScaledBy(scale);
|
|
|
|
tt.b = (tt.b).ScaledBy(scale);
|
|
|
|
tt.c = (tt.c).ScaledBy(scale);
|
|
|
|
if(scale < 0) {
|
2009-10-09 12:57:10 +00:00
|
|
|
// The mirroring would otherwise turn a closed mesh inside out.
|
2015-03-27 15:43:28 +00:00
|
|
|
swap(tt.a, tt.b);
|
2009-10-09 12:57:10 +00:00
|
|
|
}
|
2009-05-24 11:37:07 +00:00
|
|
|
tt.a = (q.Rotate(tt.a)).Plus(trans);
|
|
|
|
tt.b = (q.Rotate(tt.b)).Plus(trans);
|
|
|
|
tt.c = (q.Rotate(tt.c)).Plus(trans);
|
|
|
|
AddTriangle(&tt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-21 05:18:00 +00:00
|
|
|
bool SMesh::IsEmpty() const {
|
2009-05-24 11:37:07 +00:00
|
|
|
return (l.n == 0);
|
|
|
|
}
|
|
|
|
|
2016-05-21 05:18:00 +00:00
|
|
|
uint32_t SMesh::FirstIntersectionWith(Point2d mp) const {
|
2016-11-19 09:21:11 +00:00
|
|
|
Vector rayPoint = SS.GW.UnProjectPoint3(Vector::From(mp.x, mp.y, 0.0));
|
|
|
|
Vector rayDir = SS.GW.UnProjectPoint3(Vector::From(mp.x, mp.y, 1.0)).Minus(rayPoint);
|
2008-06-02 03:31:37 +00:00
|
|
|
|
Use C99 integer types and C++ boolean types/values
This change comprehensively replaces the use of Microsoft-standard integer
and boolean types with their C99/C++ standard equivalents, as the latter is
more appropriate for a cross-platform application. With matter-of-course
exceptions in the Win32-specific code, the types/values have been converted
as follows:
QWORD --> uint64_t
SQWORD --> int64_t
DWORD --> uint32_t
SDWORD --> int32_t
WORD --> uint16_t
SWORD --> int16_t
BYTE --> uint8_t
BOOL --> bool
TRUE --> true
FALSE --> false
The following related changes are also included:
* Added C99 integer type definitions for Windows, as stdint.h is not
available prior to Visual Studio 2010
* Changed types of some variables in the SolveSpace class from 'int' to
'bool', as they actually represent boolean settings
* Implemented new Cnf{Freeze,Thaw}Bool() functions to support boolean
variables in the Registry
* Cnf{Freeze,Thaw}DWORD() are now Cnf{Freeze,Thaw}Int()
* TtfFont::Get{WORD,DWORD}() are now TtfFont::Get{USHORT,ULONG}() (names
inspired by the OpenType spec)
* RGB colors are packed into an integer of type uint32_t (nee DWORD), but
in a few places, these were represented by an int; these have been
corrected to uint32_t
2013-10-02 05:45:13 +00:00
|
|
|
uint32_t face = 0;
|
2016-11-19 09:21:11 +00:00
|
|
|
double faceT = VERY_NEGATIVE;
|
|
|
|
for(int i = 0; i < l.n; i++) {
|
|
|
|
const STriangle &tr = l.elem[i];
|
|
|
|
if(tr.meta.face == 0) continue;
|
|
|
|
|
|
|
|
double t;
|
|
|
|
if(!tr.Raytrace(rayPoint, rayDir, &t, NULL)) continue;
|
|
|
|
if(t > faceT) {
|
|
|
|
face = tr.meta.face;
|
|
|
|
faceT = t;
|
2008-06-02 03:31:37 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-19 09:21:11 +00:00
|
|
|
|
2008-06-02 03:31:37 +00:00
|
|
|
return face;
|
|
|
|
}
|
|
|
|
|
2016-05-05 05:54:05 +00:00
|
|
|
STriangleLl *STriangleLl::Alloc()
|
2008-07-06 07:56:24 +00:00
|
|
|
{ return (STriangleLl *)AllocTemporary(sizeof(STriangleLl)); }
|
2016-05-05 05:54:05 +00:00
|
|
|
SKdNode *SKdNode::Alloc()
|
2008-07-06 07:56:24 +00:00
|
|
|
{ return (SKdNode *)AllocTemporary(sizeof(SKdNode)); }
|
|
|
|
|
|
|
|
SKdNode *SKdNode::From(SMesh *m) {
|
|
|
|
int i;
|
|
|
|
STriangle *tra = (STriangle *)AllocTemporary((m->l.n) * sizeof(*tra));
|
|
|
|
|
|
|
|
for(i = 0; i < m->l.n; i++) {
|
|
|
|
tra[i] = m->l.elem[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
srand(0);
|
|
|
|
int n = m->l.n;
|
|
|
|
while(n > 1) {
|
|
|
|
int k = rand() % n;
|
|
|
|
n--;
|
2015-03-27 15:43:28 +00:00
|
|
|
swap(tra[k], tra[n]);
|
2008-07-06 07:56:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
STriangleLl *tll = NULL;
|
|
|
|
for(i = 0; i < m->l.n; i++) {
|
|
|
|
STriangleLl *tn = STriangleLl::Alloc();
|
|
|
|
tn->tri = &(tra[i]);
|
|
|
|
tn->next = tll;
|
|
|
|
tll = tn;
|
|
|
|
}
|
|
|
|
|
2009-11-09 11:51:38 +00:00
|
|
|
return SKdNode::From(tll);
|
2008-07-06 07:56:24 +00:00
|
|
|
}
|
|
|
|
|
2009-11-09 11:51:38 +00:00
|
|
|
SKdNode *SKdNode::From(STriangleLl *tll) {
|
|
|
|
int which = 0;
|
2008-07-06 07:56:24 +00:00
|
|
|
SKdNode *ret = Alloc();
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int gtc[3] = { 0, 0, 0 }, ltc[3] = { 0, 0, 0 }, allc = 0;
|
2009-11-09 11:51:38 +00:00
|
|
|
double badness[3] = { 0, 0, 0 };
|
|
|
|
double split[3] = { 0, 0, 0 };
|
2013-08-26 19:08:16 +00:00
|
|
|
|
|
|
|
if(!tll) {
|
|
|
|
goto leaf;
|
|
|
|
}
|
|
|
|
|
2008-07-06 07:56:24 +00:00
|
|
|
for(i = 0; i < 3; i++) {
|
|
|
|
int tcnt = 0;
|
|
|
|
STriangleLl *ll;
|
|
|
|
for(ll = tll; ll; ll = ll->next) {
|
|
|
|
split[i] += (ll->tri->a).Element(i);
|
|
|
|
split[i] += (ll->tri->b).Element(i);
|
|
|
|
split[i] += (ll->tri->c).Element(i);
|
|
|
|
tcnt++;
|
|
|
|
}
|
|
|
|
split[i] /= (tcnt*3);
|
|
|
|
|
|
|
|
for(ll = tll; ll; ll = ll->next) {
|
|
|
|
STriangle *tr = ll->tri;
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2008-07-06 07:56:24 +00:00
|
|
|
double a = (tr->a).Element(i),
|
|
|
|
b = (tr->b).Element(i),
|
|
|
|
c = (tr->c).Element(i);
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2008-07-06 07:56:24 +00:00
|
|
|
if(a < split[i] + KDTREE_EPS ||
|
|
|
|
b < split[i] + KDTREE_EPS ||
|
|
|
|
c < split[i] + KDTREE_EPS)
|
|
|
|
{
|
|
|
|
ltc[i]++;
|
|
|
|
}
|
|
|
|
if(a > split[i] - KDTREE_EPS ||
|
|
|
|
b > split[i] - KDTREE_EPS ||
|
|
|
|
c > split[i] - KDTREE_EPS)
|
|
|
|
{
|
|
|
|
gtc[i]++;
|
|
|
|
}
|
|
|
|
if(i == 0) allc++;
|
|
|
|
}
|
|
|
|
badness[i] = pow((double)ltc[i], 4) + pow((double)gtc[i], 4);
|
|
|
|
}
|
|
|
|
if(badness[0] < badness[1] && badness[0] < badness[2]) {
|
|
|
|
which = 0;
|
|
|
|
} else if(badness[1] < badness[2]) {
|
|
|
|
which = 1;
|
|
|
|
} else {
|
|
|
|
which = 2;
|
|
|
|
}
|
|
|
|
|
2009-11-09 11:51:38 +00:00
|
|
|
if(allc < 3 || allc == gtc[which] || allc == ltc[which]) {
|
|
|
|
goto leaf;
|
|
|
|
}
|
2008-07-06 07:56:24 +00:00
|
|
|
|
|
|
|
STriangleLl *ll;
|
2013-08-26 19:08:16 +00:00
|
|
|
STriangleLl *lgt, *llt; lgt = llt = NULL;
|
2008-07-06 07:56:24 +00:00
|
|
|
for(ll = tll; ll; ll = ll->next) {
|
|
|
|
STriangle *tr = ll->tri;
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2008-07-06 07:56:24 +00:00
|
|
|
double a = (tr->a).Element(which),
|
|
|
|
b = (tr->b).Element(which),
|
|
|
|
c = (tr->c).Element(which);
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2008-07-06 07:56:24 +00:00
|
|
|
if(a < split[which] + KDTREE_EPS ||
|
|
|
|
b < split[which] + KDTREE_EPS ||
|
|
|
|
c < split[which] + KDTREE_EPS)
|
|
|
|
{
|
|
|
|
STriangleLl *n = STriangleLl::Alloc();
|
|
|
|
*n = *ll;
|
|
|
|
n->next = llt;
|
|
|
|
llt = n;
|
|
|
|
}
|
|
|
|
if(a > split[which] - KDTREE_EPS ||
|
|
|
|
b > split[which] - KDTREE_EPS ||
|
|
|
|
c > split[which] - KDTREE_EPS)
|
|
|
|
{
|
|
|
|
STriangleLl *n = STriangleLl::Alloc();
|
|
|
|
*n = *ll;
|
|
|
|
n->next = lgt;
|
|
|
|
lgt = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret->which = which;
|
|
|
|
ret->c = split[which];
|
2009-11-09 11:51:38 +00:00
|
|
|
ret->gt = SKdNode::From(lgt);
|
|
|
|
ret->lt = SKdNode::From(llt);
|
2008-07-06 07:56:24 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
leaf:
|
|
|
|
ret->tris = tll;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-05-21 05:18:00 +00:00
|
|
|
void SKdNode::ClearTags() const {
|
2008-07-06 07:56:24 +00:00
|
|
|
if(gt && lt) {
|
|
|
|
gt->ClearTags();
|
|
|
|
lt->ClearTags();
|
|
|
|
} else {
|
|
|
|
STriangleLl *ll;
|
|
|
|
for(ll = tris; ll; ll = ll->next) {
|
|
|
|
ll->tri->tag = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SKdNode::AddTriangle(STriangle *tr) {
|
|
|
|
if(gt && lt) {
|
|
|
|
double ta = (tr->a).Element(which),
|
|
|
|
tb = (tr->b).Element(which),
|
|
|
|
tc = (tr->c).Element(which);
|
|
|
|
if(ta < c + KDTREE_EPS ||
|
|
|
|
tb < c + KDTREE_EPS ||
|
|
|
|
tc < c + KDTREE_EPS)
|
|
|
|
{
|
|
|
|
lt->AddTriangle(tr);
|
|
|
|
}
|
|
|
|
if(ta > c - KDTREE_EPS ||
|
|
|
|
tb > c - KDTREE_EPS ||
|
|
|
|
tc > c - KDTREE_EPS)
|
|
|
|
{
|
|
|
|
gt->AddTriangle(tr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
STriangleLl *tn = STriangleLl::Alloc();
|
|
|
|
tn->tri = tr;
|
|
|
|
tn->next = tris;
|
|
|
|
tris = tn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-21 05:18:00 +00:00
|
|
|
void SKdNode::MakeMeshInto(SMesh *m) const {
|
2008-07-06 07:56:24 +00:00
|
|
|
if(gt) gt->MakeMeshInto(m);
|
|
|
|
if(lt) lt->MakeMeshInto(m);
|
|
|
|
|
|
|
|
STriangleLl *ll;
|
|
|
|
for(ll = tris; ll; ll = ll->next) {
|
|
|
|
if(ll->tri->tag) continue;
|
|
|
|
|
|
|
|
m->AddTriangle(ll->tri);
|
|
|
|
ll->tri->tag = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-21 05:18:00 +00:00
|
|
|
void SKdNode::ListTrianglesInto(std::vector<STriangle *> *tl) const {
|
2016-03-30 08:01:44 +00:00
|
|
|
if(gt) gt->ListTrianglesInto(tl);
|
|
|
|
if(lt) lt->ListTrianglesInto(tl);
|
|
|
|
|
|
|
|
STriangleLl *ll;
|
|
|
|
for(ll = tris; ll; ll = ll->next) {
|
|
|
|
if(ll->tri->tag) continue;
|
|
|
|
|
|
|
|
tl->push_back(ll->tri);
|
|
|
|
ll->tri->tag = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// If any triangles in the mesh have an edge that goes through v (but not
|
|
|
|
// a vertex at v), then split those triangles so that they now have a vertex
|
|
|
|
// there. The existing triangle is modified, and the new triangle appears
|
|
|
|
// in extras.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SKdNode::SnapToVertex(Vector v, SMesh *extras) {
|
2008-07-06 07:56:24 +00:00
|
|
|
if(gt && lt) {
|
2009-05-28 07:07:54 +00:00
|
|
|
double vc = v.Element(which);
|
|
|
|
if(vc < c + KDTREE_EPS) {
|
|
|
|
lt->SnapToVertex(v, extras);
|
2008-07-06 07:56:24 +00:00
|
|
|
}
|
2009-05-28 07:07:54 +00:00
|
|
|
if(vc > c - KDTREE_EPS) {
|
|
|
|
gt->SnapToVertex(v, extras);
|
2008-07-06 07:56:24 +00:00
|
|
|
}
|
2009-05-28 07:07:54 +00:00
|
|
|
// Nothing bad happens if the triangle to be split appears in both
|
|
|
|
// branches; the first call will split the triangle, so that the
|
|
|
|
// second call will do nothing, because the modified triangle will
|
|
|
|
// already contain v
|
|
|
|
} else {
|
|
|
|
STriangleLl *ll;
|
|
|
|
for(ll = tris; ll; ll = ll->next) {
|
|
|
|
STriangle *tr = ll->tri;
|
2008-07-06 07:56:24 +00:00
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
// Do a cheap bbox test first
|
|
|
|
int k;
|
|
|
|
bool mightHit = true;
|
|
|
|
|
|
|
|
for(k = 0; k < 3; k++) {
|
|
|
|
if((tr->a).Element(k) < v.Element(k) - KDTREE_EPS &&
|
|
|
|
(tr->b).Element(k) < v.Element(k) - KDTREE_EPS &&
|
|
|
|
(tr->c).Element(k) < v.Element(k) - KDTREE_EPS)
|
|
|
|
{
|
|
|
|
mightHit = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if((tr->a).Element(k) > v.Element(k) + KDTREE_EPS &&
|
|
|
|
(tr->b).Element(k) > v.Element(k) + KDTREE_EPS &&
|
|
|
|
(tr->c).Element(k) > v.Element(k) + KDTREE_EPS)
|
|
|
|
{
|
|
|
|
mightHit = false;
|
|
|
|
break;
|
2009-01-28 07:09:01 +00:00
|
|
|
}
|
2008-07-06 07:56:24 +00:00
|
|
|
}
|
2009-05-28 07:07:54 +00:00
|
|
|
if(!mightHit) continue;
|
|
|
|
|
|
|
|
if(tr->a.Equals(v)) { tr->a = v; continue; }
|
|
|
|
if(tr->b.Equals(v)) { tr->b = v; continue; }
|
|
|
|
if(tr->c.Equals(v)) { tr->c = v; continue; }
|
|
|
|
|
|
|
|
if(v.OnLineSegment(tr->a, tr->b)) {
|
|
|
|
STriangle nt = STriangle::From(tr->meta, tr->a, v, tr->c);
|
|
|
|
extras->AddTriangle(&nt);
|
|
|
|
tr->a = v;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(v.OnLineSegment(tr->b, tr->c)) {
|
|
|
|
STriangle nt = STriangle::From(tr->meta, tr->b, v, tr->a);
|
|
|
|
extras->AddTriangle(&nt);
|
|
|
|
tr->b = v;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(v.OnLineSegment(tr->c, tr->a)) {
|
|
|
|
STriangle nt = STriangle::From(tr->meta, tr->c, v, tr->b);
|
|
|
|
extras->AddTriangle(&nt);
|
|
|
|
tr->c = v;
|
|
|
|
continue;
|
|
|
|
}
|
2008-07-06 07:56:24 +00:00
|
|
|
}
|
2009-05-28 07:07:54 +00:00
|
|
|
}
|
|
|
|
}
|
2009-05-22 10:02:02 +00:00
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Snap to each vertex of each triangle of the given mesh. If the given mesh
|
|
|
|
// is identical to the mesh used to make this kd tree, then the result should
|
|
|
|
// be a vertex-to-vertex mesh.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void SKdNode::SnapToMesh(SMesh *m) {
|
|
|
|
int i, j, k;
|
|
|
|
for(i = 0; i < m->l.n; i++) {
|
|
|
|
STriangle *tr = &(m->l.elem[i]);
|
|
|
|
for(j = 0; j < 3; j++) {
|
2016-06-25 07:00:49 +00:00
|
|
|
Vector v = tr->vertices[j];
|
2009-05-28 07:07:54 +00:00
|
|
|
|
2015-03-27 15:31:23 +00:00
|
|
|
SMesh extra = {};
|
2009-05-28 07:07:54 +00:00
|
|
|
SnapToVertex(v, &extra);
|
|
|
|
|
|
|
|
for(k = 0; k < extra.l.n; k++) {
|
|
|
|
STriangle *tra = (STriangle *)AllocTemporary(sizeof(*tra));
|
|
|
|
*tra = extra.l.elem[k];
|
|
|
|
AddTriangle(tra);
|
|
|
|
}
|
|
|
|
extra.Clear();
|
|
|
|
}
|
2008-07-06 07:56:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// For all the edges in sel, split them against the given triangle, and test
|
2016-07-21 20:27:53 +00:00
|
|
|
// them for occlusion. sel is both our input and our output. tag indicates
|
|
|
|
// whether an edge is occluded.
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-07-21 20:27:53 +00:00
|
|
|
void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr) const {
|
2015-03-27 15:31:23 +00:00
|
|
|
SEdgeList seln = {};
|
2009-03-17 16:33:46 +00:00
|
|
|
|
|
|
|
Vector tn = tr->Normal().WithMagnitude(1);
|
|
|
|
double td = tn.Dot(tr->a);
|
|
|
|
|
2009-09-22 06:47:11 +00:00
|
|
|
// Consider front-facing triangles only.
|
2009-03-17 16:33:46 +00:00
|
|
|
if(tn.z > LENGTH_EPS) {
|
|
|
|
// If the edge crosses our triangle's plane, then split into above
|
2009-09-22 06:47:11 +00:00
|
|
|
// and below parts. Note that we must preserve auxA, which contains
|
2016-07-21 20:27:53 +00:00
|
|
|
// the style associated with this line, as well as the tag, which
|
|
|
|
// contains the occlusion status.
|
2009-03-17 16:33:46 +00:00
|
|
|
SEdge *se;
|
|
|
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
|
|
|
double da = (se->a).Dot(tn) - td,
|
|
|
|
db = (se->b).Dot(tn) - td;
|
|
|
|
if((da < -LENGTH_EPS && db > LENGTH_EPS) ||
|
|
|
|
(db < -LENGTH_EPS && da > LENGTH_EPS))
|
|
|
|
{
|
|
|
|
Vector m = Vector::AtIntersectionOfPlaneAndLine(
|
|
|
|
tn, td,
|
|
|
|
se->a, se->b, NULL);
|
2016-07-21 20:27:53 +00:00
|
|
|
seln.AddEdge(m, se->b, se->auxA, 0, se->tag);
|
2009-03-17 16:33:46 +00:00
|
|
|
se->b = m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
|
2016-07-21 20:27:53 +00:00
|
|
|
sel->AddEdge(se->a, se->b, se->auxA, 0, se->tag);
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
seln.Clear();
|
|
|
|
|
|
|
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
|
|
|
Vector pt = ((se->a).Plus(se->b)).ScaledBy(0.5);
|
|
|
|
if(pt.Dot(tn) - td > -LENGTH_EPS) {
|
|
|
|
// Edge is in front of or on our plane (remember, tn.z > 0)
|
|
|
|
// so it is exempt from further splitting
|
2009-09-22 06:47:11 +00:00
|
|
|
se->auxB = 1;
|
2009-03-17 16:33:46 +00:00
|
|
|
} else {
|
|
|
|
// Edge is behind our plane, needs further splitting
|
2009-09-22 06:47:11 +00:00
|
|
|
se->auxB = 0;
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Considering only the (x, y) coordinates, split the edge against our
|
|
|
|
// triangle.
|
|
|
|
Point2d a = (tr->a).ProjectXy(),
|
|
|
|
b = (tr->b).ProjectXy(),
|
|
|
|
c = (tr->c).ProjectXy();
|
|
|
|
|
|
|
|
Point2d n[3] = { (b.Minus(a)).Normal().WithMagnitude(1),
|
|
|
|
(c.Minus(b)).Normal().WithMagnitude(1),
|
|
|
|
(a.Minus(c)).Normal().WithMagnitude(1) };
|
|
|
|
|
|
|
|
double d[3] = { n[0].Dot(b),
|
|
|
|
n[1].Dot(c),
|
|
|
|
n[2].Dot(a) };
|
|
|
|
|
|
|
|
// Split all of the edges where they intersect the triangle edges
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < 3; i++) {
|
|
|
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
2009-09-22 06:47:11 +00:00
|
|
|
if(se->auxB) continue;
|
2009-03-17 16:33:46 +00:00
|
|
|
|
|
|
|
Point2d ap = (se->a).ProjectXy(),
|
|
|
|
bp = (se->b).ProjectXy();
|
|
|
|
double da = n[i].Dot(ap) - d[i],
|
|
|
|
db = n[i].Dot(bp) - d[i];
|
|
|
|
if((da < -LENGTH_EPS && db > LENGTH_EPS) ||
|
|
|
|
(db < -LENGTH_EPS && da > LENGTH_EPS))
|
|
|
|
{
|
|
|
|
double dab = (db - da);
|
|
|
|
Vector spl = ((se->a).ScaledBy( db/dab)).Plus(
|
|
|
|
(se->b).ScaledBy(-da/dab));
|
2016-07-21 20:27:53 +00:00
|
|
|
seln.AddEdge(spl, se->b, se->auxA, 0, se->tag);
|
2009-03-17 16:33:46 +00:00
|
|
|
se->b = spl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
|
2009-09-22 06:47:11 +00:00
|
|
|
// The split pieces are all behind the triangle, since only
|
|
|
|
// edges behind the triangle got split. So their auxB is 0.
|
2016-07-21 20:27:53 +00:00
|
|
|
sel->AddEdge(se->a, se->b, se->auxA, 0, se->tag);
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
seln.Clear();
|
|
|
|
}
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2009-03-17 16:33:46 +00:00
|
|
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
2016-07-21 20:27:53 +00:00
|
|
|
bool occluded;
|
2009-09-22 06:47:11 +00:00
|
|
|
if(se->auxB) {
|
2009-03-17 16:33:46 +00:00
|
|
|
// Lies above or on the triangle plane, so triangle doesn't
|
|
|
|
// occlude it.
|
2016-07-21 20:27:53 +00:00
|
|
|
occluded = false;
|
2009-03-17 16:33:46 +00:00
|
|
|
} else {
|
|
|
|
// Test the segment to see if it lies outside the triangle
|
|
|
|
// (i.e., outside wrt at least one edge), and keep it only
|
|
|
|
// then.
|
|
|
|
Point2d pt = ((se->a).Plus(se->b).ScaledBy(0.5)).ProjectXy();
|
2016-07-21 20:27:53 +00:00
|
|
|
occluded = true;
|
2009-03-17 16:33:46 +00:00
|
|
|
for(i = 0; i < 3; i++) {
|
2009-05-20 13:07:56 +00:00
|
|
|
// If the test point lies on the boundary of our triangle,
|
|
|
|
// then we still discard the edge.
|
2016-07-21 20:27:53 +00:00
|
|
|
if(n[i].Dot(pt) - d[i] > LENGTH_EPS) occluded = false;
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-21 20:27:53 +00:00
|
|
|
|
|
|
|
if(occluded) {
|
|
|
|
se->tag = 1;
|
|
|
|
}
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Given an edge orig, occlusion test it against our mesh. We output an edge
|
2016-07-21 20:27:53 +00:00
|
|
|
// list in sel, where only invisible portions of the edge are tagged.
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-07-21 20:27:53 +00:00
|
|
|
void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt) const {
|
2009-03-17 16:33:46 +00:00
|
|
|
if(gt && lt) {
|
|
|
|
double ac = (orig.a).Element(which),
|
|
|
|
bc = (orig.b).Element(which);
|
|
|
|
// We can ignore triangles that are separated in x or y, but triangles
|
|
|
|
// that are separated in z may still contribute
|
|
|
|
if(ac < c + KDTREE_EPS ||
|
|
|
|
bc < c + KDTREE_EPS ||
|
|
|
|
which == 2)
|
|
|
|
{
|
2016-07-21 20:27:53 +00:00
|
|
|
lt->OcclusionTestLine(orig, sel, cnt);
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
if(ac > c - KDTREE_EPS ||
|
|
|
|
bc > c - KDTREE_EPS ||
|
|
|
|
which == 2)
|
|
|
|
{
|
2016-07-21 20:27:53 +00:00
|
|
|
gt->OcclusionTestLine(orig, sel, cnt);
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
STriangleLl *ll;
|
|
|
|
for(ll = tris; ll; ll = ll->next) {
|
|
|
|
STriangle *tr = ll->tri;
|
|
|
|
|
|
|
|
if(tr->tag == cnt) continue;
|
|
|
|
|
2016-07-21 20:27:53 +00:00
|
|
|
SplitLinesAgainstTriangle(sel, tr);
|
2009-03-17 16:33:46 +00:00
|
|
|
tr->tag = cnt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Search the mesh for a triangle with an edge from b to a (i.e., the mate
|
2016-03-22 05:03:12 +00:00
|
|
|
// for the edge from a to b), and increment info->count each time that we
|
|
|
|
// find one. If a triangle is found, then report whether it is front- or
|
|
|
|
// back-facing using info->frontFacing. And regardless of whether a mate is
|
|
|
|
// found, report whether the edge intersects the mesh with info->intersectsMesh;
|
|
|
|
// if coplanarIsInter then we count the edge as intersecting if it's coplanar
|
|
|
|
// with a triangle in the mesh, otherwise not.
|
2009-05-28 07:07:54 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-03-22 05:03:12 +00:00
|
|
|
void SKdNode::FindEdgeOn(Vector a, Vector b, int cnt, bool coplanarIsInter,
|
2016-05-21 05:18:00 +00:00
|
|
|
EdgeOnInfo *info) const
|
2009-05-28 07:07:54 +00:00
|
|
|
{
|
|
|
|
if(gt && lt) {
|
|
|
|
double ac = a.Element(which),
|
|
|
|
bc = b.Element(which);
|
|
|
|
if(ac < c + KDTREE_EPS ||
|
|
|
|
bc < c + KDTREE_EPS)
|
|
|
|
{
|
2016-03-22 05:03:12 +00:00
|
|
|
lt->FindEdgeOn(a, b, cnt, coplanarIsInter, info);
|
2009-05-28 07:07:54 +00:00
|
|
|
}
|
|
|
|
if(ac > c - KDTREE_EPS ||
|
|
|
|
bc > c - KDTREE_EPS)
|
|
|
|
{
|
2016-03-22 05:03:12 +00:00
|
|
|
gt->FindEdgeOn(a, b, cnt, coplanarIsInter, info);
|
2009-05-28 07:07:54 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
// We are a leaf node; so we iterate over all the triangles in our
|
|
|
|
// linked list.
|
|
|
|
STriangleLl *ll;
|
|
|
|
for(ll = tris; ll; ll = ll->next) {
|
|
|
|
STriangle *tr = ll->tri;
|
|
|
|
|
|
|
|
if(tr->tag == cnt) continue;
|
2015-03-29 00:30:52 +00:00
|
|
|
|
2009-05-28 07:07:54 +00:00
|
|
|
// Test if this triangle matches up with the given edge
|
|
|
|
if((a.Equals(tr->b) && b.Equals(tr->a)) ||
|
|
|
|
(a.Equals(tr->c) && b.Equals(tr->b)) ||
|
|
|
|
(a.Equals(tr->a) && b.Equals(tr->c)))
|
|
|
|
{
|
2016-03-22 05:03:12 +00:00
|
|
|
info->count++;
|
2009-05-28 07:07:54 +00:00
|
|
|
// Record whether this triangle is front- or back-facing.
|
|
|
|
if(tr->Normal().z > LENGTH_EPS) {
|
2016-03-22 05:03:12 +00:00
|
|
|
info->frontFacing = true;
|
2009-05-28 07:07:54 +00:00
|
|
|
} else {
|
2016-03-22 05:03:12 +00:00
|
|
|
info->frontFacing = false;
|
2009-05-28 07:07:54 +00:00
|
|
|
}
|
2016-03-22 10:52:09 +00:00
|
|
|
// Record the triangle
|
|
|
|
info->tr = tr;
|
|
|
|
// And record which vertexes a and b correspond to
|
|
|
|
info->ai = a.Equals(tr->a) ? 0 : (a.Equals(tr->b) ? 1 : 2);
|
|
|
|
info->bi = b.Equals(tr->a) ? 0 : (b.Equals(tr->b) ? 1 : 2);
|
2009-05-28 07:07:54 +00:00
|
|
|
} else if(((a.Equals(tr->a) && b.Equals(tr->b)) ||
|
|
|
|
(a.Equals(tr->b) && b.Equals(tr->c)) ||
|
|
|
|
(a.Equals(tr->c) && b.Equals(tr->a))))
|
|
|
|
{
|
|
|
|
// It's an edge of this triangle, okay.
|
|
|
|
} else {
|
|
|
|
// Check for self-intersection
|
|
|
|
Vector n = (tr->Normal()).WithMagnitude(1);
|
|
|
|
double d = (tr->a).Dot(n);
|
|
|
|
double pa = a.Dot(n) - d, pb = b.Dot(n) - d;
|
|
|
|
// It's an intersection if neither point lies in-plane,
|
|
|
|
// and the edge crosses the plane (should handle in-plane
|
|
|
|
// intersections separately but don't yet).
|
|
|
|
if((pa < -LENGTH_EPS || pa > LENGTH_EPS) &&
|
|
|
|
(pb < -LENGTH_EPS || pb > LENGTH_EPS) &&
|
|
|
|
(pa*pb < 0))
|
|
|
|
{
|
|
|
|
// The edge crosses the plane of the triangle; now see if
|
|
|
|
// it crosses inside the triangle.
|
|
|
|
if(tr->ContainsPointProjd(b.Minus(a), a)) {
|
|
|
|
if(coplanarIsInter) {
|
2016-03-22 05:03:12 +00:00
|
|
|
info->intersectsMesh = true;
|
2009-05-28 07:07:54 +00:00
|
|
|
} else {
|
|
|
|
Vector p = Vector::AtIntersectionOfPlaneAndLine(
|
|
|
|
n, d, a, b, NULL);
|
|
|
|
Vector ta = tr->a,
|
|
|
|
tb = tr->b,
|
|
|
|
tc = tr->c;
|
|
|
|
if((p.DistanceToLine(ta, tb.Minus(ta)) < LENGTH_EPS) ||
|
|
|
|
(p.DistanceToLine(tb, tc.Minus(tb)) < LENGTH_EPS) ||
|
|
|
|
(p.DistanceToLine(tc, ta.Minus(tc)) < LENGTH_EPS))
|
|
|
|
{
|
|
|
|
// Intersection lies on edge. This happens when
|
|
|
|
// our edge is from a triangle coplanar with
|
|
|
|
// another triangle in the mesh. We don't test
|
|
|
|
// the edge against triangles whose plane contains
|
|
|
|
// that edge, but we do end up testing against
|
|
|
|
// the coplanar triangle's neighbours, which we
|
|
|
|
// will intersect on their edges.
|
|
|
|
} else {
|
2016-03-22 05:03:12 +00:00
|
|
|
info->intersectsMesh = true;
|
2009-05-28 07:07:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that we don't count this triangle twice if it appears
|
|
|
|
// in two buckets of the kd tree.
|
|
|
|
tr->tag = cnt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-30 08:01:44 +00:00
|
|
|
static bool CheckAndAddTrianglePair(std::set<std::pair<STriangle *, STriangle *>> *pairs,
|
|
|
|
STriangle *a, STriangle *b)
|
|
|
|
{
|
|
|
|
if(pairs->find(std::make_pair(a, b)) != pairs->end() ||
|
|
|
|
pairs->find(std::make_pair(b, a)) != pairs->end())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
pairs->emplace(a, b);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-05-22 10:02:02 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
2009-05-29 05:40:17 +00:00
|
|
|
// Pick certain classes of edges out from our mesh. These might be:
|
|
|
|
// * naked edges (i.e., edges with no anti-parallel neighbor) and self-
|
|
|
|
// intersecting edges (i.e., edges that cross another triangle)
|
|
|
|
// * turning edges (i.e., edges where a front-facing triangle joins
|
|
|
|
// a back-facing triangle)
|
|
|
|
// * emphasized edges (i.e., edges where a triangle from one face joins
|
|
|
|
// a triangle from a different face)
|
2009-05-22 10:02:02 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, EdgeKind how, bool coplanarIsInter,
|
2016-05-21 05:18:00 +00:00
|
|
|
bool *inter, bool *leaky, int auxA) const
|
2009-05-22 10:02:02 +00:00
|
|
|
{
|
2009-01-28 07:09:01 +00:00
|
|
|
if(inter) *inter = false;
|
|
|
|
if(leaky) *leaky = false;
|
|
|
|
|
2016-03-30 08:01:44 +00:00
|
|
|
std::vector<STriangle *> tris;
|
2008-07-06 07:56:24 +00:00
|
|
|
ClearTags();
|
2016-03-30 08:01:44 +00:00
|
|
|
ListTrianglesInto(&tris);
|
2008-07-06 07:56:24 +00:00
|
|
|
|
2016-03-30 08:01:44 +00:00
|
|
|
std::set<std::pair<STriangle *, STriangle *>> edgeTris;
|
2008-07-06 07:56:24 +00:00
|
|
|
int cnt = 1234;
|
2016-03-30 08:01:44 +00:00
|
|
|
for(STriangle *tr : tris) {
|
|
|
|
for(int j = 0; j < 3; j++) {
|
2016-06-25 07:00:49 +00:00
|
|
|
Vector a = tr->vertices[j];
|
|
|
|
Vector b = tr->vertices[(j + 1) % 3];
|
2008-07-06 07:56:24 +00:00
|
|
|
|
2016-03-22 05:03:12 +00:00
|
|
|
SKdNode::EdgeOnInfo info = {};
|
|
|
|
FindEdgeOn(a, b, cnt, coplanarIsInter, &info);
|
2009-05-29 05:40:17 +00:00
|
|
|
|
|
|
|
switch(how) {
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
case EdgeKind::NAKED_OR_SELF_INTER:
|
2016-03-22 05:03:12 +00:00
|
|
|
if(info.count != 1) {
|
2016-03-14 16:14:24 +00:00
|
|
|
sel->AddEdge(a, b, auxA);
|
2009-05-29 05:40:17 +00:00
|
|
|
if(leaky) *leaky = true;
|
|
|
|
}
|
2016-03-22 05:03:12 +00:00
|
|
|
if(info.intersectsMesh) {
|
2016-03-14 16:14:24 +00:00
|
|
|
sel->AddEdge(a, b, auxA);
|
2009-05-29 05:40:17 +00:00
|
|
|
if(inter) *inter = true;
|
|
|
|
}
|
|
|
|
break;
|
2009-03-18 04:26:04 +00:00
|
|
|
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
case EdgeKind::SELF_INTER:
|
2016-03-22 05:03:12 +00:00
|
|
|
if(info.intersectsMesh) {
|
2016-03-14 16:14:24 +00:00
|
|
|
sel->AddEdge(a, b, auxA);
|
2009-06-06 09:44:58 +00:00
|
|
|
if(inter) *inter = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
case EdgeKind::TURNING:
|
2009-05-29 05:40:17 +00:00
|
|
|
if((tr->Normal().z < LENGTH_EPS) &&
|
2016-03-22 05:03:12 +00:00
|
|
|
(info.count == 1) &&
|
|
|
|
info.frontFacing)
|
2009-05-29 05:40:17 +00:00
|
|
|
{
|
2016-03-30 08:01:44 +00:00
|
|
|
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
|
|
|
|
break;
|
2009-05-29 05:40:17 +00:00
|
|
|
// This triangle is back-facing (or on edge), and
|
|
|
|
// this edge has exactly one mate, and that mate is
|
|
|
|
// front-facing. So this is a turning edge.
|
2016-03-14 16:14:24 +00:00
|
|
|
sel->AddEdge(a, b, auxA);
|
2009-05-29 05:40:17 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-03-18 04:26:04 +00:00
|
|
|
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
case EdgeKind::EMPHASIZED:
|
2016-04-28 07:21:33 +00:00
|
|
|
if(info.count == 1 && tr->meta.face != info.tr->meta.face) {
|
2016-03-30 08:01:44 +00:00
|
|
|
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
|
|
|
|
break;
|
2009-05-29 05:40:17 +00:00
|
|
|
// The two triangles that join at this edge come from
|
|
|
|
// different faces; either really different faces,
|
|
|
|
// or one is from a face and the other is zero (i.e.,
|
|
|
|
// not from a face).
|
2016-03-14 16:14:24 +00:00
|
|
|
sel->AddEdge(a, b, auxA);
|
2009-05-29 05:40:17 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-03-18 04:26:04 +00:00
|
|
|
|
Convert all enumerations to use `enum class`.
Specifically, take the old code that looks like this:
class Foo {
enum { X = 1, Y = 2 };
int kind;
}
... foo.kind = Foo::X; ...
and convert it to this:
class Foo {
enum class Kind : uint32_t { X = 1, Y = 2 };
Kind kind;
}
... foo.kind = Foo::Kind::X;
(In some cases the enumeration would not be in the class namespace,
such as when it is generally useful.)
The benefits are as follows:
* The type of the field gives a clear indication of intent, both
to humans and tools (such as binding generators).
* The compiler is able to automatically warn when a switch is not
exhaustive; but this is currently suppressed by the
default: ssassert(false, ...)
idiom.
* Integers and plain enums are weakly type checked: they implicitly
convert into each other. This can hide bugs where type conversion
is performed but not intended. Enum classes are strongly type
checked.
* Plain enums pollute parent namespaces; enum classes do not.
Almost every defined enum we have already has a kind of ad-hoc
namespacing via `NAMESPACE_`, which is now explicit.
* Plain enums do not have a well-defined ABI size, which is
important for bindings. Enum classes can have it, if specified.
We specify the base type for all enums as uint32_t, which is
a safe choice and allows us to not change the numeric values
of any variants.
This commit introduces absolutely no functional change to the code,
just renaming and change of types. It handles almost all cases,
except GraphicsWindow::pending.operation, which needs minor
functional change.
2016-05-20 08:31:20 +00:00
|
|
|
case EdgeKind::SHARP:
|
2016-03-22 10:52:09 +00:00
|
|
|
if(info.count == 1) {
|
2016-06-25 07:00:49 +00:00
|
|
|
Vector na0 = tr->normals[j].WithMagnitude(1.0);
|
|
|
|
Vector nb0 = tr->normals[(j + 1) % 3].WithMagnitude(1.0);
|
|
|
|
Vector na1 = info.tr->normals[info.ai].WithMagnitude(1.0);
|
|
|
|
Vector nb1 = info.tr->normals[info.bi].WithMagnitude(1.0);
|
2016-03-22 10:52:09 +00:00
|
|
|
if(!((na0.Equals(na1) && nb0.Equals(nb1)) ||
|
|
|
|
(na0.Equals(nb1) && nb0.Equals(na1)))) {
|
2016-03-30 08:01:44 +00:00
|
|
|
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
|
|
|
|
break;
|
2016-03-22 10:52:09 +00:00
|
|
|
// The two triangles that join at this edge meet at a sharp
|
|
|
|
// angle. This implies they come from different faces.
|
2016-03-14 16:14:24 +00:00
|
|
|
sel->AddEdge(a, b, auxA);
|
2016-03-22 10:52:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2009-03-18 04:26:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-26 06:39:27 +00:00
|
|
|
void SKdNode::MakeOutlinesInto(SOutlineList *sol, EdgeKind edgeKind) const
|
2016-03-14 16:14:24 +00:00
|
|
|
{
|
|
|
|
std::vector<STriangle *> tris;
|
|
|
|
ClearTags();
|
|
|
|
ListTrianglesInto(&tris);
|
|
|
|
|
|
|
|
std::set<std::pair<STriangle *, STriangle *>> edgeTris;
|
|
|
|
int cnt = 1234;
|
|
|
|
for(STriangle *tr : tris) {
|
|
|
|
for(int j = 0; j < 3; j++) {
|
2016-06-25 07:00:49 +00:00
|
|
|
Vector a = tr->vertices[j];
|
|
|
|
Vector b = tr->vertices[(j + 1) % 3];
|
2016-03-14 16:14:24 +00:00
|
|
|
|
|
|
|
SKdNode::EdgeOnInfo info = {};
|
|
|
|
FindEdgeOn(a, b, cnt, /*coplanarIsInter=*/false, &info);
|
|
|
|
cnt++;
|
|
|
|
if(info.count != 1) continue;
|
|
|
|
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
|
|
|
|
continue;
|
|
|
|
|
2016-06-26 06:39:27 +00:00
|
|
|
int tag = 0;
|
|
|
|
switch(edgeKind) {
|
|
|
|
case EdgeKind::EMPHASIZED:
|
|
|
|
if(tr->meta.face != info.tr->meta.face) {
|
|
|
|
tag = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EdgeKind::SHARP: {
|
|
|
|
Vector na0 = tr->normals[j].WithMagnitude(1.0);
|
|
|
|
Vector nb0 = tr->normals[(j + 1) % 3].WithMagnitude(1.0);
|
|
|
|
Vector na1 = info.tr->normals[info.ai].WithMagnitude(1.0);
|
|
|
|
Vector nb1 = info.tr->normals[info.bi].WithMagnitude(1.0);
|
|
|
|
if(!((na0.Equals(na1) && nb0.Equals(nb1)) ||
|
|
|
|
(na0.Equals(nb1) && nb0.Equals(na1)))) {
|
|
|
|
tag = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ssassert(false, "Unexpected edge kind");
|
|
|
|
}
|
|
|
|
|
2016-06-24 07:10:19 +00:00
|
|
|
Vector nl = tr->Normal().WithMagnitude(1.0);
|
|
|
|
Vector nr = info.tr->Normal().WithMagnitude(1.0);
|
|
|
|
|
|
|
|
// We don't add edges with the same left and right
|
|
|
|
// normals because they can't produce outlines.
|
2016-06-26 06:39:27 +00:00
|
|
|
if(tag == 0 && nl.Equals(nr)) continue;
|
|
|
|
sol->AddEdge(a, b, nl, nr, tag);
|
2016-03-14 16:14:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-24 02:26:52 +00:00
|
|
|
bool SOutline::IsVisible(Vector projDir) const {
|
2016-07-04 07:57:46 +00:00
|
|
|
double ldot = nl.Dot(projDir);
|
|
|
|
double rdot = nr.Dot(projDir);
|
|
|
|
return (ldot > -LENGTH_EPS) == (rdot < LENGTH_EPS) ||
|
|
|
|
(rdot > -LENGTH_EPS) == (ldot < LENGTH_EPS);
|
2016-05-24 02:26:52 +00:00
|
|
|
}
|
|
|
|
|
2016-03-14 16:14:24 +00:00
|
|
|
void SOutlineList::Clear() {
|
|
|
|
l.Clear();
|
|
|
|
}
|
|
|
|
|
2016-06-26 06:39:27 +00:00
|
|
|
void SOutlineList::AddEdge(Vector a, Vector b, Vector nl, Vector nr, int tag) {
|
2016-03-14 16:14:24 +00:00
|
|
|
SOutline so = {};
|
|
|
|
so.a = a;
|
|
|
|
so.b = b;
|
|
|
|
so.nl = nl;
|
|
|
|
so.nr = nr;
|
2016-06-26 06:39:27 +00:00
|
|
|
so.tag = tag;
|
2016-03-14 16:14:24 +00:00
|
|
|
l.Add(&so);
|
|
|
|
}
|
|
|
|
|
2016-06-26 06:39:27 +00:00
|
|
|
void SOutlineList::ListTaggedInto(SEdgeList *el, int auxA, int auxB) {
|
|
|
|
for(const SOutline &so : l) {
|
|
|
|
if(so.tag == 0) continue;
|
|
|
|
el->AddEdge(so.a, so.b, auxA, auxB);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-14 16:14:24 +00:00
|
|
|
void SOutlineList::MakeFromCopyOf(SOutlineList *sol) {
|
|
|
|
for(SOutline *so = sol->l.First(); so; so = sol->l.NextAfter(so)) {
|
|
|
|
l.Add(so);
|
|
|
|
}
|
|
|
|
}
|
2016-12-05 03:11:34 +00:00
|
|
|
|
|
|
|
void SMesh::PrecomputeTransparency() {
|
|
|
|
std::sort(l.begin(), l.end(),
|
|
|
|
[&](const STriangle &sta, const STriangle &stb) {
|
|
|
|
RgbaColor colora = sta.meta.color,
|
|
|
|
colorb = stb.meta.color;
|
|
|
|
bool opaquea = colora.IsEmpty() || colora.alpha == 255,
|
|
|
|
opaqueb = colorb.IsEmpty() || colorb.alpha == 255;
|
|
|
|
|
|
|
|
if(!opaquea || !opaqueb) isTransparent = true;
|
|
|
|
return (opaquea != opaqueb && opaquea == true);
|
|
|
|
});
|
|
|
|
}
|