forked from bartvdbraak/blender
245 lines
5.9 KiB
C++
245 lines
5.9 KiB
C++
//
|
|
// Copyright (c) 2009 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 <math.h>
|
|
#include "DetourCommon.h"
|
|
|
|
void closestPtPointTriangle(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];
|
|
vsub(ab, b, a);
|
|
vsub(ac, c, a);
|
|
vsub(ap, p, a);
|
|
float d1 = vdot(ab, ap);
|
|
float d2 = vdot(ac, ap);
|
|
if (d1 <= 0.0f && d2 <= 0.0f)
|
|
{
|
|
// barycentric coordinates (1,0,0)
|
|
vcopy(closest, a);
|
|
return;
|
|
}
|
|
|
|
// Check if P in vertex region outside B
|
|
float bp[3];
|
|
vsub(bp, p, b);
|
|
float d3 = vdot(ab, bp);
|
|
float d4 = vdot(ac, bp);
|
|
if (d3 >= 0.0f && d4 <= d3)
|
|
{
|
|
// barycentric coordinates (0,1,0)
|
|
vcopy(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];
|
|
vsub(cp, p, c);
|
|
float d5 = vdot(ab, cp);
|
|
float d6 = vdot(ac, cp);
|
|
if (d6 >= 0.0f && d5 <= d6)
|
|
{
|
|
// barycentric coordinates (0,0,1)
|
|
vcopy(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 intersectSegmentPoly2D(const float* p0, const float* p1,
|
|
const float* verts, int nverts,
|
|
float& tmin, float& tmax,
|
|
int& segMin, int& segMax)
|
|
{
|
|
static const float EPS = 0.00000001f;
|
|
|
|
tmin = 0;
|
|
tmax = 1;
|
|
segMin = -1;
|
|
segMax = -1;
|
|
|
|
float dir[3];
|
|
vsub(dir, p1, p0);
|
|
|
|
for (int i = 0, j = nverts-1; i < nverts; j=i++)
|
|
{
|
|
float edge[3], diff[3];
|
|
vsub(edge, &verts[i*3], &verts[j*3]);
|
|
vsub(diff, p0, &verts[j*3]);
|
|
float n = vperp2D(edge, diff);
|
|
float d = -vperp2D(edge, dir);
|
|
if (fabs(d) < EPS)
|
|
{
|
|
// S is nearly parallel to this edge
|
|
if (n < 0)
|
|
return false;
|
|
else
|
|
continue;
|
|
}
|
|
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 distancePtSegSqr2D(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 calcPolyCenter(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;
|
|
}
|
|
|
|
inline float vdot2(const float* a, const float* b)
|
|
{
|
|
return a[0]*b[0] + a[2]*b[2];
|
|
}
|
|
|
|
#include <stdio.h>
|
|
|
|
bool closestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
|
|
{
|
|
float v0[3], v1[3], v2[3];
|
|
vsub(v0, c,a);
|
|
vsub(v1, b,a);
|
|
vsub(v2, p,a);
|
|
|
|
const float dot00 = vdot2(v0, v0);
|
|
const float dot01 = vdot2(v0, v1);
|
|
const float dot02 = vdot2(v0, v2);
|
|
const float dot11 = vdot2(v1, v1);
|
|
const float dot12 = vdot2(v1, v2);
|
|
|
|
// Compute barycentric coordinates
|
|
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
|
|
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
|
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
|
|
|
// The (sloppy) epsilon is needed to allow to get height of points which
|
|
// are interpolated along the edges of the triangles.
|
|
static const float EPS = 1e-4f;
|
|
|
|
// If point lies inside the triangle, return interpolated ycoord.
|
|
if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
|
|
{
|
|
h = a[1] + v0[1]*u + v1[1]*v;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|