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.00000001f; | ||
|  | 	 | ||
|  | 	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; | ||
|  | } | ||
|  | 
 |