
It's only used to detect if the segment is parallel to the polygon edge in question, and if so, skip actually doing the intersection test. This sets the epsilon to 1e-6, which is about 10x the machine epsilon for 32bit floats, so it's low enough to not give false positives but it's large enough to correctly detect segments and polygon edges that are very nearly parallel.
388 lines
9.6 KiB
C++
388 lines
9.6 KiB
C++
//
|
|
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
//
|
|
|
|
#include "DetourCommon.h"
|
|
#include "DetourMath.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void dtClosestPtPointTriangle(float* closest, const float* p,
|
|
const float* a, const float* b, const float* c)
|
|
{
|
|
// Check if P in vertex region outside A
|
|
float ab[3], ac[3], ap[3];
|
|
dtVsub(ab, b, a);
|
|
dtVsub(ac, c, a);
|
|
dtVsub(ap, p, a);
|
|
float d1 = dtVdot(ab, ap);
|
|
float d2 = dtVdot(ac, ap);
|
|
if (d1 <= 0.0f && d2 <= 0.0f)
|
|
{
|
|
// barycentric coordinates (1,0,0)
|
|
dtVcopy(closest, a);
|
|
return;
|
|
}
|
|
|
|
// Check if P in vertex region outside B
|
|
float bp[3];
|
|
dtVsub(bp, p, b);
|
|
float d3 = dtVdot(ab, bp);
|
|
float d4 = dtVdot(ac, bp);
|
|
if (d3 >= 0.0f && d4 <= d3)
|
|
{
|
|
// barycentric coordinates (0,1,0)
|
|
dtVcopy(closest, b);
|
|
return;
|
|
}
|
|
|
|
// Check if P in edge region of AB, if so return projection of P onto AB
|
|
float vc = d1*d4 - d3*d2;
|
|
if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
|
|
{
|
|
// barycentric coordinates (1-v,v,0)
|
|
float v = d1 / (d1 - d3);
|
|
closest[0] = a[0] + v * ab[0];
|
|
closest[1] = a[1] + v * ab[1];
|
|
closest[2] = a[2] + v * ab[2];
|
|
return;
|
|
}
|
|
|
|
// Check if P in vertex region outside C
|
|
float cp[3];
|
|
dtVsub(cp, p, c);
|
|
float d5 = dtVdot(ab, cp);
|
|
float d6 = dtVdot(ac, cp);
|
|
if (d6 >= 0.0f && d5 <= d6)
|
|
{
|
|
// barycentric coordinates (0,0,1)
|
|
dtVcopy(closest, c);
|
|
return;
|
|
}
|
|
|
|
// Check if P in edge region of AC, if so return projection of P onto AC
|
|
float vb = d5*d2 - d1*d6;
|
|
if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
|
|
{
|
|
// barycentric coordinates (1-w,0,w)
|
|
float w = d2 / (d2 - d6);
|
|
closest[0] = a[0] + w * ac[0];
|
|
closest[1] = a[1] + w * ac[1];
|
|
closest[2] = a[2] + w * ac[2];
|
|
return;
|
|
}
|
|
|
|
// Check if P in edge region of BC, if so return projection of P onto BC
|
|
float va = d3*d6 - d5*d4;
|
|
if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
|
|
{
|
|
// barycentric coordinates (0,1-w,w)
|
|
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
|
|
closest[0] = b[0] + w * (c[0] - b[0]);
|
|
closest[1] = b[1] + w * (c[1] - b[1]);
|
|
closest[2] = b[2] + w * (c[2] - b[2]);
|
|
return;
|
|
}
|
|
|
|
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
|
|
float denom = 1.0f / (va + vb + vc);
|
|
float v = vb * denom;
|
|
float w = vc * denom;
|
|
closest[0] = a[0] + ab[0] * v + ac[0] * w;
|
|
closest[1] = a[1] + ab[1] * v + ac[1] * w;
|
|
closest[2] = a[2] + ab[2] * v + ac[2] * w;
|
|
}
|
|
|
|
bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
|
|
const float* verts, int nverts,
|
|
float& tmin, float& tmax,
|
|
int& segMin, int& segMax)
|
|
{
|
|
static const float EPS = 0.000001f;
|
|
|
|
tmin = 0;
|
|
tmax = 1;
|
|
segMin = -1;
|
|
segMax = -1;
|
|
|
|
float dir[3];
|
|
dtVsub(dir, p1, p0);
|
|
|
|
for (int i = 0, j = nverts-1; i < nverts; j=i++)
|
|
{
|
|
float edge[3], diff[3];
|
|
dtVsub(edge, &verts[i*3], &verts[j*3]);
|
|
dtVsub(diff, p0, &verts[j*3]);
|
|
const float n = dtVperp2D(edge, diff);
|
|
const float d = dtVperp2D(dir, edge);
|
|
if (fabsf(d) < EPS)
|
|
{
|
|
// S is nearly parallel to this edge
|
|
if (n < 0)
|
|
return false;
|
|
else
|
|
continue;
|
|
}
|
|
const float t = n / d;
|
|
if (d < 0)
|
|
{
|
|
// segment S is entering across this edge
|
|
if (t > tmin)
|
|
{
|
|
tmin = t;
|
|
segMin = j;
|
|
// S enters after leaving polygon
|
|
if (tmin > tmax)
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// segment S is leaving across this edge
|
|
if (t < tmax)
|
|
{
|
|
tmax = t;
|
|
segMax = j;
|
|
// S leaves before entering polygon
|
|
if (tmax < tmin)
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t)
|
|
{
|
|
float pqx = q[0] - p[0];
|
|
float pqz = q[2] - p[2];
|
|
float dx = pt[0] - p[0];
|
|
float dz = pt[2] - p[2];
|
|
float d = pqx*pqx + pqz*pqz;
|
|
t = pqx*dx + pqz*dz;
|
|
if (d > 0) t /= d;
|
|
if (t < 0) t = 0;
|
|
else if (t > 1) t = 1;
|
|
dx = p[0] + t*pqx - pt[0];
|
|
dz = p[2] + t*pqz - pt[2];
|
|
return dx*dx + dz*dz;
|
|
}
|
|
|
|
void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts)
|
|
{
|
|
tc[0] = 0.0f;
|
|
tc[1] = 0.0f;
|
|
tc[2] = 0.0f;
|
|
for (int j = 0; j < nidx; ++j)
|
|
{
|
|
const float* v = &verts[idx[j]*3];
|
|
tc[0] += v[0];
|
|
tc[1] += v[1];
|
|
tc[2] += v[2];
|
|
}
|
|
const float s = 1.0f / nidx;
|
|
tc[0] *= s;
|
|
tc[1] *= s;
|
|
tc[2] *= s;
|
|
}
|
|
|
|
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
|
|
{
|
|
const float EPS = 1e-6f;
|
|
float v0[3], v1[3], v2[3];
|
|
|
|
dtVsub(v0, c, a);
|
|
dtVsub(v1, b, a);
|
|
dtVsub(v2, p, a);
|
|
|
|
// Compute scaled barycentric coordinates
|
|
float denom = v0[0] * v1[2] - v0[2] * v1[0];
|
|
if (fabsf(denom) < EPS)
|
|
return false;
|
|
|
|
float u = v1[2] * v2[0] - v1[0] * v2[2];
|
|
float v = v0[0] * v2[2] - v0[2] * v2[0];
|
|
|
|
if (denom < 0) {
|
|
denom = -denom;
|
|
u = -u;
|
|
v = -v;
|
|
}
|
|
|
|
// If point lies inside the triangle, return interpolated ycoord.
|
|
if (u >= 0.0f && v >= 0.0f && (u + v) <= denom) {
|
|
h = a[1] + (v0[1] * u + v1[1] * v) / denom;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// @par
|
|
///
|
|
/// All points are projected onto the xz-plane, so the y-values are ignored.
|
|
bool dtPointInPolygon(const float* pt, const float* verts, const int nverts)
|
|
{
|
|
// TODO: Replace pnpoly with triArea2D tests?
|
|
int i, j;
|
|
bool c = false;
|
|
for (i = 0, j = nverts-1; i < nverts; j = i++)
|
|
{
|
|
const float* vi = &verts[i*3];
|
|
const float* vj = &verts[j*3];
|
|
if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
|
|
(pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
|
|
c = !c;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
|
|
float* ed, float* et)
|
|
{
|
|
// TODO: Replace pnpoly with triArea2D tests?
|
|
int i, j;
|
|
bool c = false;
|
|
for (i = 0, j = nverts-1; i < nverts; j = i++)
|
|
{
|
|
const float* vi = &verts[i*3];
|
|
const float* vj = &verts[j*3];
|
|
if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
|
|
(pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
|
|
c = !c;
|
|
ed[j] = dtDistancePtSegSqr2D(pt, vj, vi, et[j]);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static void projectPoly(const float* axis, const float* poly, const int npoly,
|
|
float& rmin, float& rmax)
|
|
{
|
|
rmin = rmax = dtVdot2D(axis, &poly[0]);
|
|
for (int i = 1; i < npoly; ++i)
|
|
{
|
|
const float d = dtVdot2D(axis, &poly[i*3]);
|
|
rmin = dtMin(rmin, d);
|
|
rmax = dtMax(rmax, d);
|
|
}
|
|
}
|
|
|
|
inline bool overlapRange(const float amin, const float amax,
|
|
const float bmin, const float bmax,
|
|
const float eps)
|
|
{
|
|
return ((amin+eps) > bmax || (amax-eps) < bmin) ? false : true;
|
|
}
|
|
|
|
/// @par
|
|
///
|
|
/// All vertices are projected onto the xz-plane, so the y-values are ignored.
|
|
bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
|
|
const float* polyb, const int npolyb)
|
|
{
|
|
const float eps = 1e-4f;
|
|
|
|
for (int i = 0, j = npolya-1; i < npolya; j=i++)
|
|
{
|
|
const float* va = &polya[j*3];
|
|
const float* vb = &polya[i*3];
|
|
const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
|
|
float amin,amax,bmin,bmax;
|
|
projectPoly(n, polya, npolya, amin,amax);
|
|
projectPoly(n, polyb, npolyb, bmin,bmax);
|
|
if (!overlapRange(amin,amax, bmin,bmax, eps))
|
|
{
|
|
// Found separating axis
|
|
return false;
|
|
}
|
|
}
|
|
for (int i = 0, j = npolyb-1; i < npolyb; j=i++)
|
|
{
|
|
const float* va = &polyb[j*3];
|
|
const float* vb = &polyb[i*3];
|
|
const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
|
|
float amin,amax,bmin,bmax;
|
|
projectPoly(n, polya, npolya, amin,amax);
|
|
projectPoly(n, polyb, npolyb, bmin,bmax);
|
|
if (!overlapRange(amin,amax, bmin,bmax, eps))
|
|
{
|
|
// Found separating axis
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Returns a random point in a convex polygon.
|
|
// Adapted from Graphics Gems article.
|
|
void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
|
|
const float s, const float t, float* out)
|
|
{
|
|
// Calc triangle araes
|
|
float areasum = 0.0f;
|
|
for (int i = 2; i < npts; i++) {
|
|
areas[i] = dtTriArea2D(&pts[0], &pts[(i-1)*3], &pts[i*3]);
|
|
areasum += dtMax(0.001f, areas[i]);
|
|
}
|
|
// Find sub triangle weighted by area.
|
|
const float thr = s*areasum;
|
|
float acc = 0.0f;
|
|
float u = 1.0f;
|
|
int tri = npts - 1;
|
|
for (int i = 2; i < npts; i++) {
|
|
const float dacc = areas[i];
|
|
if (thr >= acc && thr < (acc+dacc))
|
|
{
|
|
u = (thr - acc) / dacc;
|
|
tri = i;
|
|
break;
|
|
}
|
|
acc += dacc;
|
|
}
|
|
|
|
float v = dtMathSqrtf(t);
|
|
|
|
const float a = 1 - v;
|
|
const float b = (1 - u) * v;
|
|
const float c = u * v;
|
|
const float* pa = &pts[0];
|
|
const float* pb = &pts[(tri-1)*3];
|
|
const float* pc = &pts[tri*3];
|
|
|
|
out[0] = a*pa[0] + b*pb[0] + c*pc[0];
|
|
out[1] = a*pa[1] + b*pb[1] + c*pc[1];
|
|
out[2] = a*pa[2] + b*pb[2] + c*pc[2];
|
|
}
|
|
|
|
inline float vperpXZ(const float* a, const float* b) { return a[0]*b[2] - a[2]*b[0]; }
|
|
|
|
bool dtIntersectSegSeg2D(const float* ap, const float* aq,
|
|
const float* bp, const float* bq,
|
|
float& s, float& t)
|
|
{
|
|
float u[3], v[3], w[3];
|
|
dtVsub(u,aq,ap);
|
|
dtVsub(v,bq,bp);
|
|
dtVsub(w,ap,bp);
|
|
float d = vperpXZ(u,v);
|
|
if (fabsf(d) < 1e-6f) return false;
|
|
s = vperpXZ(v,w) / d;
|
|
t = vperpXZ(u,w) / d;
|
|
return true;
|
|
}
|
|
|