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>
|
|
|
|
|
2008-05-22 10:28:50 +00:00
|
|
|
void SMesh::Clear(void) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
void SMesh::AddTriangle(STriangle *st) {
|
2015-03-26 10:30:12 +00:00
|
|
|
if(st->meta.color.alpha != 255) isTransparent = true;
|
2008-05-24 12:23:25 +00:00
|
|
|
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;
|
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);
|
2015-03-29 00:30:52 +00:00
|
|
|
root->MakeCertainEdgesInto(sel, SKdNode::NAKED_OR_SELF_INTER_EDGES,
|
2009-05-29 05:40:17 +00:00
|
|
|
false, NULL, NULL);
|
2009-05-28 07:07:54 +00:00
|
|
|
|
|
|
|
m.Clear();
|
|
|
|
}
|
|
|
|
|
2016-03-14 16:14:24 +00:00
|
|
|
void SMesh::MakeCertainEdgesAndOutlinesInto(SEdgeList *sel, SOutlineList *sol, int type) {
|
2009-05-29 05:40:17 +00:00
|
|
|
SKdNode *root = SKdNode::From(this);
|
2016-03-22 10:52:09 +00:00
|
|
|
root->MakeCertainEdgesInto(sel, type, false, NULL, NULL);
|
2016-03-14 16:14:24 +00:00
|
|
|
root->MakeOutlinesInto(sol);
|
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);
|
|
|
|
}
|
|
|
|
|
2009-12-15 12:26:22 +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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SMesh::IsEmpty(void) {
|
|
|
|
return (l.n == 0);
|
|
|
|
}
|
|
|
|
|
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 SMesh::FirstIntersectionWith(Point2d mp) {
|
2008-06-17 19:12:25 +00:00
|
|
|
Vector p0 = Vector::From(mp.x, mp.y, 0);
|
|
|
|
Vector gn = Vector::From(0, 0, 1);
|
2008-06-02 03:31:37 +00:00
|
|
|
|
|
|
|
double maxT = -1e12;
|
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;
|
2008-06-02 03:31:37 +00:00
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < l.n; i++) {
|
2008-06-17 19:12:25 +00:00
|
|
|
STriangle tr = l.elem[i];
|
|
|
|
tr.a = SS.GW.ProjectPoint3(tr.a);
|
|
|
|
tr.b = SS.GW.ProjectPoint3(tr.b);
|
|
|
|
tr.c = SS.GW.ProjectPoint3(tr.c);
|
|
|
|
|
|
|
|
Vector n = tr.Normal();
|
|
|
|
|
2008-06-02 03:31:37 +00:00
|
|
|
if(n.Dot(gn) < LENGTH_EPS) continue; // back-facing or on edge
|
|
|
|
|
2008-06-17 19:12:25 +00:00
|
|
|
if(tr.ContainsPointProjd(gn, p0)) {
|
2008-06-02 03:31:37 +00:00
|
|
|
// Let our line have the form r(t) = p0 + gn*t
|
2008-06-17 19:12:25 +00:00
|
|
|
double t = -(n.Dot((tr.a).Minus(p0)))/(n.Dot(gn));
|
2008-06-02 03:31:37 +00:00
|
|
|
if(t > maxT) {
|
|
|
|
maxT = t;
|
2008-06-17 19:12:25 +00:00
|
|
|
face = tr.meta.face;
|
2008-06-02 03:31:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return face;
|
|
|
|
}
|
|
|
|
|
2015-03-29 00:30:52 +00:00
|
|
|
STriangleLl *STriangleLl::Alloc(void)
|
2008-07-06 07:56:24 +00:00
|
|
|
{ return (STriangleLl *)AllocTemporary(sizeof(STriangleLl)); }
|
2015-03-29 00:30:52 +00:00
|
|
|
SKdNode *SKdNode::Alloc(void)
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SKdNode::ClearTags(void) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SKdNode::MakeMeshInto(SMesh *m) {
|
|
|
|
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-03-30 08:01:44 +00:00
|
|
|
void SKdNode::ListTrianglesInto(std::vector<STriangle *> *tl) {
|
|
|
|
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++) {
|
2015-03-29 00:30:52 +00:00
|
|
|
Vector v = ((j == 0) ? tr->a :
|
2009-05-28 07:07:54 +00:00
|
|
|
((j == 1) ? tr->b :
|
|
|
|
tr->c));
|
|
|
|
|
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
|
|
|
|
// them for occlusion. Keep only the visible segments. sel is both our input
|
|
|
|
// and our output.
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-03-09 04:53:46 +00:00
|
|
|
void SKdNode::SplitLinesAgainstTriangle(SEdgeList *sel, STriangle *tr, bool removeHidden) {
|
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
|
|
|
|
// the style associated with this line.
|
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);
|
2009-09-22 06:47:11 +00:00
|
|
|
seln.AddEdge(m, se->b, se->auxA);
|
2009-03-17 16:33:46 +00:00
|
|
|
se->b = m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(se = seln.l.First(); se; se = seln.l.NextAfter(se)) {
|
2009-09-22 06:47:11 +00:00
|
|
|
sel->AddEdge(se->a, se->b, se->auxA);
|
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));
|
2009-09-22 06:47:11 +00:00
|
|
|
seln.AddEdge(spl, se->b, se->auxA);
|
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.
|
|
|
|
sel->AddEdge(se->a, se->b, se->auxA, 0);
|
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)) {
|
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.
|
|
|
|
se->tag = 0;
|
|
|
|
} 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();
|
|
|
|
se->tag = 1;
|
|
|
|
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.
|
|
|
|
if(n[i].Dot(pt) - d[i] > LENGTH_EPS) se->tag = 0;
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-09 04:53:46 +00:00
|
|
|
if(!removeHidden && se->tag == 1)
|
|
|
|
se->auxA = Style::HIDDEN_EDGE;
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
2016-03-09 04:53:46 +00:00
|
|
|
if(removeHidden)
|
|
|
|
sel->l.RemoveTagged();
|
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
|
|
|
|
// list in sel, containing the visible portions of that edge.
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-03-09 04:53:46 +00:00
|
|
|
void SKdNode::OcclusionTestLine(SEdge orig, SEdgeList *sel, int cnt, bool removeHidden) {
|
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-03-09 04:53:46 +00:00
|
|
|
lt->OcclusionTestLine(orig, sel, cnt, removeHidden);
|
2009-03-17 16:33:46 +00:00
|
|
|
}
|
|
|
|
if(ac > c - KDTREE_EPS ||
|
|
|
|
bc > c - KDTREE_EPS ||
|
|
|
|
which == 2)
|
|
|
|
{
|
2016-03-09 04:53:46 +00:00
|
|
|
gt->OcclusionTestLine(orig, sel, cnt, removeHidden);
|
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-03-09 04:53:46 +00:00
|
|
|
SplitLinesAgainstTriangle(sel, tr, removeHidden);
|
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,
|
|
|
|
EdgeOnInfo *info)
|
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
|
|
|
//-----------------------------------------------------------------------------
|
2016-03-14 16:14:24 +00:00
|
|
|
void SKdNode::MakeCertainEdgesInto(SEdgeList *sel, int how, bool coplanarIsInter,
|
|
|
|
bool *inter, bool *leaky, int auxA)
|
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++) {
|
2008-07-06 07:56:24 +00:00
|
|
|
Vector a = (j == 0) ? tr->a : ((j == 1) ? tr->b : tr->c);
|
|
|
|
Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a);
|
|
|
|
|
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) {
|
|
|
|
case NAKED_OR_SELF_INTER_EDGES:
|
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
|
|
|
|
2009-06-06 09:44:58 +00:00
|
|
|
case SELF_INTER_EDGES:
|
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;
|
|
|
|
|
2009-05-29 05:40:17 +00:00
|
|
|
case TURNING_EDGES:
|
|
|
|
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
|
|
|
|
2009-05-29 05:40:17 +00:00
|
|
|
case EMPHASIZED_EDGES:
|
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
|
|
|
|
2016-03-22 10:52:09 +00:00
|
|
|
case SHARP_EDGES:
|
|
|
|
if(info.count == 1) {
|
|
|
|
Vector na0 = (j == 0) ? tr->an :
|
|
|
|
((j == 1) ? tr->bn : tr->cn);
|
|
|
|
Vector nb0 = (j == 0) ? tr->bn :
|
|
|
|
((j == 1) ? tr->cn : tr->an);
|
|
|
|
Vector na1 = (info.ai == 0) ? info.tr->an :
|
|
|
|
((info.ai == 1) ? info.tr->bn : info.tr->cn);
|
|
|
|
Vector nb1 = (info.bi == 0) ? info.tr->an :
|
|
|
|
((info.bi == 1) ? info.tr->bn : info.tr->cn);
|
|
|
|
na0 = na0.WithMagnitude(1.0);
|
|
|
|
nb0 = nb0.WithMagnitude(1.0);
|
|
|
|
na1 = na1.WithMagnitude(1.0);
|
|
|
|
nb1 = nb1.WithMagnitude(1.0);
|
|
|
|
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-05-29 05:40:17 +00:00
|
|
|
default: oops();
|
2009-03-18 04:26:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-14 16:14:24 +00:00
|
|
|
void SKdNode::MakeOutlinesInto(SOutlineList *sol)
|
|
|
|
{
|
|
|
|
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++) {
|
|
|
|
Vector a = (j == 0) ? tr->a : ((j == 1) ? tr->b : tr->c);
|
|
|
|
Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a);
|
|
|
|
|
|
|
|
SKdNode::EdgeOnInfo info = {};
|
|
|
|
FindEdgeOn(a, b, cnt, /*coplanarIsInter=*/false, &info);
|
|
|
|
cnt++;
|
|
|
|
if(info.count != 1) continue;
|
|
|
|
if(CheckAndAddTrianglePair(&edgeTris, tr, info.tr))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sol->AddEdge(a, b, tr->Normal(), info.tr->Normal());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SOutlineList::Clear() {
|
|
|
|
l.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SOutlineList::AddEdge(Vector a, Vector b, Vector nl, Vector nr) {
|
|
|
|
SOutline so = {};
|
|
|
|
so.a = a;
|
|
|
|
so.b = b;
|
|
|
|
so.nl = nl;
|
|
|
|
so.nr = nr;
|
|
|
|
l.Add(&so);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SOutlineList::FillOutlineTags(Vector projDir) {
|
|
|
|
for(SOutline *so = l.First(); so; so = l.NextAfter(so)) {
|
|
|
|
so->tag = ((so->nl.Dot(projDir) > 0.0) != (so->nr.Dot(projDir) > 0.0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SOutlineList::MakeFromCopyOf(SOutlineList *sol) {
|
|
|
|
for(SOutline *so = sol->l.First(); so; so = sol->l.NextAfter(so)) {
|
|
|
|
l.Add(so);
|
|
|
|
}
|
|
|
|
}
|