Ported .Net Core commits:
hondacrx: - Initial commit: Switch to .Net Core 2.0 - Fix build and removed not needed files Fabi: - Updated solution platforms. - Changed folder structure. - Change library target framework to netstandard2.0. - Updated solution platforms again... - Removed windows specific kernel32 function usage (Ctrl-C handler).
This commit is contained in:
@@ -0,0 +1,989 @@
|
||||
using System;
|
||||
|
||||
public static partial class Detour
|
||||
{
|
||||
|
||||
/**
|
||||
@defgroup detour Detour
|
||||
|
||||
Members in this module are used to create, manipulate, and query navigation
|
||||
meshes.
|
||||
|
||||
@note This is a summary list of members. Use the index or search
|
||||
feature to find minor members.
|
||||
*/
|
||||
|
||||
/// Derives the closest point on a triangle from the specified reference point.
|
||||
/// @param[out] closest The closest point on the triangle.
|
||||
/// @param[in] p The reference point from which to test. [(x, y, z)]
|
||||
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
|
||||
public static void dtClosestPtPointTriangle(float[] closest, float[] p, float[] a, float[] b, float[] c)
|
||||
{
|
||||
// Check if P in vertex region outside A
|
||||
float[] ab = new float[3];//, ac[3], ap[3];
|
||||
float[] ac = new float[3];
|
||||
float[] ap = new float[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 = new float[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 = new float[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;
|
||||
}
|
||||
|
||||
public static bool dtIntersectSegmentPoly2D(float[] p0, float[] p1, float[] verts, int nverts, ref float tmin, ref float tmax, ref int segMin, ref int segMax)
|
||||
{
|
||||
const float EPS = 0.00000001f;
|
||||
|
||||
tmin = 0;
|
||||
tmax = 1;
|
||||
segMin = -1;
|
||||
segMax = -1;
|
||||
|
||||
float[] dir = new float[3];
|
||||
dtVsub(dir, p1, p0);
|
||||
|
||||
for (int i = 0, j = nverts - 1; i < nverts; j = i++)
|
||||
{
|
||||
float[] edge = new float[3];
|
||||
float[] diff = new float[3];
|
||||
dtVsub(edge, 0, verts, i * 3, verts, j * 3);
|
||||
dtVsub(diff, 0, p0, 0, verts, j * 3);
|
||||
float n = dtVperp2D(edge, diff);
|
||||
float d = dtVperp2D(dir, edge);
|
||||
if (Math.Abs(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;
|
||||
}
|
||||
|
||||
public static float dtDistancePtSegSqr2D(float[] pt, int ptStart, float[] p, int pStart, float[] q, int qStart, ref float t)
|
||||
{
|
||||
float pqx = q[qStart + 0] - p[pStart + 0];
|
||||
float pqz = q[qStart + 2] - p[pStart + 2];
|
||||
float dx = pt[ptStart + 0] - p[pStart + 0];
|
||||
float dz = pt[ptStart + 2] - p[pStart + 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[pStart + 0] + t * pqx - pt[ptStart + 0];
|
||||
dz = p[pStart + 2] + t * pqz - pt[ptStart + 2];
|
||||
return dx * dx + dz * dz;
|
||||
}
|
||||
|
||||
/// Derives the centroid of a convex polygon.
|
||||
/// @param[out] tc The centroid of the polgyon. [(x, y, z)]
|
||||
/// @param[in] idx The polygon indices. [(vertIndex) * @p nidx]
|
||||
/// @param[in] nidx The number of indices in the polygon. [Limit: >= 3]
|
||||
/// @param[in] verts The polygon vertices. [(x, y, z) * vertCount]
|
||||
public static void dtCalcPolyCenter(float[] tc, ushort[] idx, int nidx, float[] verts)
|
||||
{
|
||||
tc[0] = 0.0f;
|
||||
tc[1] = 0.0f;
|
||||
tc[2] = 0.0f;
|
||||
for (int j = 0; j < nidx; ++j)
|
||||
{
|
||||
int vIndex = idx[j] * 3;
|
||||
tc[0] += verts[vIndex + 0];
|
||||
tc[1] += verts[vIndex + 1];
|
||||
tc[2] += verts[vIndex + 2];
|
||||
}
|
||||
float s = 1.0f / nidx;
|
||||
tc[0] *= s;
|
||||
tc[1] *= s;
|
||||
tc[2] *= s;
|
||||
}
|
||||
|
||||
/// Derives the y-axis height of the closest point on the triangle from the specified reference point.
|
||||
/// @param[in] p The reference point from which to test. [(x, y, z)]
|
||||
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
|
||||
/// @param[out] h The resulting height.
|
||||
public static bool dtClosestHeightPointTriangle(float[] p, int pStart, float[] a, int aStart, float[] b, int bStart, float[] c, int cStart, ref float h)
|
||||
{
|
||||
float[] v0 = new float[3];
|
||||
float[] v1 = new float[3];
|
||||
float[] v2 = new float[3];
|
||||
dtVsub(v0, 0, c, cStart, a, aStart);
|
||||
dtVsub(v1, 0, b, bStart, a, aStart);
|
||||
dtVsub(v2, 0, p, pStart, a, aStart);
|
||||
|
||||
float dot00 = dtVdot2D(v0, v0);
|
||||
float dot01 = dtVdot2D(v0, v1);
|
||||
float dot02 = dtVdot2D(v0, v2);
|
||||
float dot11 = dtVdot2D(v1, v1);
|
||||
float dot12 = dtVdot2D(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.
|
||||
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[aStart + 1] + v0[1] * u + v1[1] * v;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Determines if the specified point is inside the convex polygon on the xz-plane.
|
||||
/// @param[in] pt The point to check. [(x, y, z)]
|
||||
/// @param[in] verts The polygon vertices. [(x, y, z) * @p nverts]
|
||||
/// @param[in] nverts The number of vertices. [Limit: >= 3]
|
||||
/// @return True if the point is inside the polygon.
|
||||
/// @par
|
||||
///
|
||||
/// All points are projected onto the xz-plane, so the y-values are ignored.
|
||||
public static bool dtPointInPolygon(float[] pt, float[] verts, int nverts)
|
||||
{
|
||||
// TODO: Replace pnpoly with triArea2D tests?
|
||||
int i, j;
|
||||
bool c = false;
|
||||
for (i = 0, j = nverts - 1; i < nverts; j = i++)
|
||||
{
|
||||
int viIndex = i * 3;
|
||||
int vjIndex = j * 3;
|
||||
if (((verts[viIndex + 2] > pt[2]) != (verts[vjIndex + 2] > pt[2])) &&
|
||||
(pt[0] < (verts[vjIndex + 0] - verts[viIndex + 0]) * (pt[2] - verts[viIndex + 2]) / (verts[vjIndex + 2] - verts[viIndex + 2]) + verts[viIndex + 0]))
|
||||
c = !c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public static bool dtDistancePtPolyEdgesSqr(float[] pt, int ptStart, float[] v, 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++)
|
||||
{
|
||||
int vi = i * 3;
|
||||
int vj = j * 3;
|
||||
if (((v[vi + 2] > pt[ptStart + 2]) != (v[vj + 2] > pt[ptStart + 2])) &&
|
||||
(pt[ptStart + 0] < (v[vj + 0] - v[vi + 0]) * (pt[ptStart + 2] - v[vi + 2]) / (v[vj + 2] - v[vi + 2]) + v[vi + 0]))
|
||||
c = !c;
|
||||
ed[j] = dtDistancePtSegSqr2D(pt, ptStart, v, vj, v, vi, ref et[j]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
public static void projectPoly(float[] axis, float[] poly, int npoly, ref float rmin, ref float rmax)
|
||||
{
|
||||
rmin = rmax = dtVdot2D(axis, poly);
|
||||
for (int i = 1; i < npoly; ++i)
|
||||
{
|
||||
float d = dtVdot2D(axis, 0, poly, i * 3);
|
||||
rmin = Math.Min(rmin, d);
|
||||
rmax = Math.Max(rmax, d);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool overlapRange(float amin, float amax, float bmin, float bmax, float eps)
|
||||
{
|
||||
return ((amin + eps) > bmax || (amax - eps) < bmin) ? false : true;
|
||||
}
|
||||
|
||||
/// Determines if the two convex polygons overlap on the xz-plane.
|
||||
/// @param[in] polya Polygon A vertices. [(x, y, z) * @p npolya]
|
||||
/// @param[in] npolya The number of vertices in polygon A.
|
||||
/// @param[in] polyb Polygon B vertices. [(x, y, z) * @p npolyb]
|
||||
/// @param[in] npolyb The number of vertices in polygon B.
|
||||
/// @return True if the two polygons overlap.
|
||||
/// @par
|
||||
///
|
||||
/// All vertices are projected onto the xz-plane, so the y-values are ignored.
|
||||
public static bool dtOverlapPolyPoly2D(float[] polya, int npolya, float[] polyb, int npolyb)
|
||||
{
|
||||
const float eps = 1e-4f;
|
||||
|
||||
for (int i = 0, j = npolya - 1; i < npolya; j = i++)
|
||||
{
|
||||
int vaStart = j * 3;
|
||||
int vbStart = i * 3;
|
||||
float[] n = new float[] { polya[vbStart + 2] - polya[vaStart + 2], 0, -(polya[vbStart + 0] - polya[vaStart + 0]) };
|
||||
float amin = 0.0f, amax = 0.0f, bmin = 0.0f, bmax = 0.0f;
|
||||
projectPoly(n, polya, npolya, ref amin, ref amax);
|
||||
projectPoly(n, polyb, npolyb, ref bmin, ref bmax);
|
||||
if (!overlapRange(amin, amax, bmin, bmax, eps))
|
||||
{
|
||||
// Found separating axis
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0, j = npolyb - 1; i < npolyb; j = i++)
|
||||
{
|
||||
int vaStart = j * 3;
|
||||
int vbStart = i * 3;
|
||||
float[] n = new float[] { polyb[vbStart + 2] - polyb[vaStart + 2], 0, -(polyb[vbStart + 0] - polyb[vaStart + 0]) };
|
||||
float amin = 0.0f, amax = 0.0f, bmin = 0.0f, bmax = 0.0f;
|
||||
projectPoly(n, polya, npolya, ref amin, ref amax);
|
||||
projectPoly(n, polyb, npolyb, ref bmin, ref 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.
|
||||
public static void dtRandomPointInConvexPoly(float[] pts, int npts, float[] areas, float s, 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 += Math.Max(0.001f, areas[i]);
|
||||
}
|
||||
// Find sub triangle weighted by area.
|
||||
float thr = s * areasum;
|
||||
float acc = 0.0f;
|
||||
float u = 0.0f;
|
||||
int tri = 0;
|
||||
for (int i = 2; i < npts; i++)
|
||||
{
|
||||
float dacc = areas[i];
|
||||
if (thr >= acc && thr < (acc + dacc))
|
||||
{
|
||||
u = (thr - acc) / dacc;
|
||||
tri = i;
|
||||
break;
|
||||
}
|
||||
acc += dacc;
|
||||
}
|
||||
|
||||
float v = (float)Math.Sqrt(t);
|
||||
|
||||
float a = 1 - v;
|
||||
float b = (1 - u) * v;
|
||||
float c = u * v;
|
||||
int paStart = 0;
|
||||
int pbStart = (tri - 1) * 3;
|
||||
int pcStart = tri * 3;
|
||||
|
||||
_out[0] = a * pts[paStart + 0] + b * pts[pbStart + 0] + c * pts[pcStart + 0];
|
||||
_out[1] = a * pts[paStart + 1] + b * pts[pbStart + 1] + c * pts[pcStart + 1];
|
||||
_out[2] = a * pts[paStart + 2] + b * pts[pbStart + 2] + c * pts[pcStart + 2];
|
||||
}
|
||||
|
||||
public static float vperpXZ(float[] a, float[] b)
|
||||
{
|
||||
return a[0] * b[2] - a[2] * b[0];
|
||||
}
|
||||
|
||||
public static bool dtIntersectSegSeg2D(float[] ap, float[] aq, float[] bp, float[] bq, ref float s, ref float t)
|
||||
{
|
||||
float[] u = new float[3];
|
||||
float[] v = new float[3];
|
||||
float[] w = new float[3];
|
||||
dtVsub(u, aq, ap);
|
||||
dtVsub(v, bq, bp);
|
||||
dtVsub(w, ap, bp);
|
||||
float d = vperpXZ(u, v);
|
||||
if (Math.Abs(d) < 1e-6f) return false;
|
||||
s = vperpXZ(v, w) / d;
|
||||
t = vperpXZ(u, w) / d;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool dtIntersectSegSeg2D(float[] ap, int apStart, float[] aq, int aqStart, float[] bp, int bpStart, float[] bq, int bqStart, ref float s, ref float t)
|
||||
{
|
||||
float[] u = new float[3];
|
||||
float[] v = new float[3];
|
||||
float[] w = new float[3];
|
||||
dtVsub(u, 0, aq, aqStart, ap, apStart);
|
||||
dtVsub(v, 0, bq, bqStart, bp, bpStart);
|
||||
dtVsub(w, 0, ap, apStart, bp, bpStart);
|
||||
float d = vperpXZ(u, v);
|
||||
if (Math.Abs(d) < 1e-6f) return false;
|
||||
s = vperpXZ(v, w) / d;
|
||||
t = vperpXZ(u, w) / d;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Swaps the values of the two parameters.
|
||||
/// @param[in,out] a Value A
|
||||
/// @param[in,out] b Value B
|
||||
static void dtSwap<T>(ref T lhs, ref T rhs)
|
||||
{
|
||||
T temp = lhs;
|
||||
lhs = rhs;
|
||||
rhs = temp;
|
||||
}
|
||||
|
||||
/// Returns the square of the value.
|
||||
/// @param[in] a The value.
|
||||
/// @return The square of the value.
|
||||
public static float dtSqr(float a)
|
||||
{
|
||||
return a * a;
|
||||
}
|
||||
public static int dtSqr(int a)
|
||||
{
|
||||
return a * a;
|
||||
}
|
||||
public static uint dtSqr(uint a)
|
||||
{
|
||||
return a * a;
|
||||
}
|
||||
public static byte dtSqr(byte a)
|
||||
{
|
||||
return (byte)(a * a);
|
||||
}
|
||||
|
||||
/// Clamps the value to the specified range.
|
||||
/// @param[in] v The value to clamp.
|
||||
/// @param[in] mn The minimum permitted return value.
|
||||
/// @param[in] mx The maximum permitted return value.
|
||||
/// @return The value, clamped to the specified range.
|
||||
// C#: Originally a template function but operators and template types in c# are a no
|
||||
public static int dtClamp(int v, int mn, int mx)
|
||||
{
|
||||
return v < mn ? mn : (v > mx ? mx : v);
|
||||
}
|
||||
public static uint dtClamp(uint v, uint mn, uint mx)
|
||||
{
|
||||
return v < mn ? mn : (v > mx ? mx : v);
|
||||
}
|
||||
public static byte dtClamp(byte v, byte mn, byte mx)
|
||||
{
|
||||
return v < mn ? mn : (v > mx ? mx : v);
|
||||
}
|
||||
public static ushort dtClamp(ushort v, ushort mn, ushort mx)
|
||||
{
|
||||
return v < mn ? mn : (v > mx ? mx : v);
|
||||
}
|
||||
public static float dtClamp(float v, float mn, float mx)
|
||||
{
|
||||
return v < mn ? mn : (v > mx ? mx : v);
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// @name Vector helper functions.
|
||||
/// @{
|
||||
|
||||
/// Derives the cross product of two vectors. (@p v1 x @p v2)
|
||||
/// @param[out] dest The cross product. [(x, y, z)]
|
||||
/// @param[in] v1 A Vector [(x, y, z)]
|
||||
/// @param[in] v2 A vector [(x, y, z)]
|
||||
public static void dtVcross(float[] dest, float[] v1, float[] v2)
|
||||
{
|
||||
dest[0] = v1[1] * v2[2] - v1[2] * v2[1];
|
||||
dest[1] = v1[2] * v2[0] - v1[0] * v2[2];
|
||||
dest[2] = v1[0] * v2[1] - v1[1] * v2[0];
|
||||
}
|
||||
/// Derives the dot product of two vectors. (@p v1 . @p v2)
|
||||
/// @param[in] v1 A Vector [(x, y, z)]
|
||||
/// @param[in] v2 A vector [(x, y, z)]
|
||||
/// @return The dot product.
|
||||
public static float dtVdot(float[] v1, float[] v2)
|
||||
{
|
||||
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
|
||||
}
|
||||
public static float dtVdot(float[] v1, int v1Start, float[] v2, int v2Start)
|
||||
{
|
||||
return v1[v1Start + 0] * v2[v2Start + 0] + v1[v1Start + 1] * v2[v2Start + 1] + v1[v1Start + 2] * v2[v2Start + 2];
|
||||
}
|
||||
|
||||
/// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s))
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
||||
/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)]
|
||||
/// @param[in] s The amount to scale @p v2 by before adding to @p v1.
|
||||
public static void dtVmad(float[] dest, float[] v1, float[] v2, float s)
|
||||
{
|
||||
dest[0] = v1[0] + v2[0] * s;
|
||||
dest[1] = v1[1] + v2[1] * s;
|
||||
dest[2] = v1[2] + v2[2] * s;
|
||||
}
|
||||
|
||||
/// Performs a linear interpolation between two vectors. (@p v1 toward @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, x)]
|
||||
/// @param[in] v1 The starting vector.
|
||||
/// @param[in] v2 The destination vector.
|
||||
/// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0]
|
||||
public static void dtVlerp(float[] dest, float[] v1, float[] v2, float t)
|
||||
{
|
||||
dest[0] = v1[0] + (v2[0] - v1[0]) * t;
|
||||
dest[1] = v1[1] + (v2[1] - v1[1]) * t;
|
||||
dest[2] = v1[2] + (v2[2] - v1[2]) * t;
|
||||
}
|
||||
public static void dtVlerp(float[] dest, int destStart, float[] v1, int v1Start, float[] v2, int v2Start, float t)
|
||||
{
|
||||
dest[destStart + 0] = v1[v1Start + 0] + (v2[v2Start + 0] - v1[v1Start + 0]) * t;
|
||||
dest[destStart + 1] = v1[v1Start + 1] + (v2[v2Start + 1] - v1[v1Start + 1]) * t;
|
||||
dest[destStart + 2] = v1[v1Start + 2] + (v2[v2Start + 2] - v1[v1Start + 2]) * t;
|
||||
}
|
||||
/// Performs a vector addition. (@p v1 + @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
||||
/// @param[in] v2 The vector to add to @p v1. [(x, y, z)]
|
||||
public static void dtVadd(float[] dest, float[] v1, float[] v2)
|
||||
{
|
||||
dest[0] = v1[0] + v2[0];
|
||||
dest[1] = v1[1] + v2[1];
|
||||
dest[2] = v1[2] + v2[2];
|
||||
}
|
||||
public static void dtVadd(float[] dest, int destStart, float[] v1, int v1Start, float[] v2, int v2Start)
|
||||
{
|
||||
dest[destStart + 0] = v1[v1Start + 0] + v2[v2Start + 0];
|
||||
dest[destStart + 1] = v1[v1Start + 1] + v2[v2Start + 1];
|
||||
dest[destStart + 2] = v1[v1Start + 2] + v2[v2Start + 2];
|
||||
}
|
||||
|
||||
/// Performs a vector subtraction. (@p v1 - @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
||||
/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)]
|
||||
public static void dtVsub(float[] dest, float[] v1, float[] v2)
|
||||
{
|
||||
dest[0] = v1[0] - v2[0];
|
||||
dest[1] = v1[1] - v2[1];
|
||||
dest[2] = v1[2] - v2[2];
|
||||
}
|
||||
public static void dtVsub(float[] dest, int destStart, float[] v1, int v1Start, float[] v2, int v2Start)
|
||||
{
|
||||
dest[destStart + 0] = v1[v1Start + 0] - v2[v2Start + 0];
|
||||
dest[destStart + 1] = v1[v1Start + 1] - v2[v2Start + 1];
|
||||
dest[destStart + 2] = v1[v1Start + 2] - v2[v2Start + 2];
|
||||
}
|
||||
|
||||
/// Scales the vector by the specified value. (@p v * @p t)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v The vector to scale. [(x, y, z)]
|
||||
/// @param[in] t The scaling factor.
|
||||
public static void dtVscale(float[] dest, float[] v, float t)
|
||||
{
|
||||
dest[0] = v[0] * t;
|
||||
dest[1] = v[1] * t;
|
||||
dest[2] = v[2] * t;
|
||||
}
|
||||
public static void dtVscale(float[] dest, int destStart, float[] v, int vStart, float t)
|
||||
{
|
||||
dest[destStart + 0] = v[vStart + 0] * t;
|
||||
dest[destStart + 1] = v[vStart + 1] * t;
|
||||
dest[destStart + 2] = v[vStart + 2] * t;
|
||||
}
|
||||
/// Selects the minimum value of each element from the specified vectors.
|
||||
/// @param[in,out] mn A vector. (Will be updated with the result.) [(x, y, z)]
|
||||
/// @param[in] v A vector. [(x, y, z)]
|
||||
public static void dtVmin(float[] mn, float[] v)
|
||||
{
|
||||
mn[0] = Math.Min(mn[0], v[0]);
|
||||
mn[1] = Math.Min(mn[1], v[1]);
|
||||
mn[2] = Math.Min(mn[2], v[2]);
|
||||
}
|
||||
public static void dtVmin(float[] mn, int mnStart, float[] v, int vStart)
|
||||
{
|
||||
mn[mnStart + 0] = Math.Min(mn[mnStart + 0], v[vStart + 0]);
|
||||
mn[mnStart + 1] = Math.Min(mn[mnStart + 1], v[vStart + 1]);
|
||||
mn[mnStart + 2] = Math.Min(mn[mnStart + 2], v[vStart + 2]);
|
||||
}
|
||||
|
||||
/// Selects the maximum value of each element from the specified vectors.
|
||||
/// @param[in,out] mx A vector. (Will be updated with the result.) [(x, y, z)]
|
||||
/// @param[in] v A vector. [(x, y, z)]
|
||||
public static void dtVmax(float[] mx, float[] v)
|
||||
{
|
||||
mx[0] = Math.Max(mx[0], v[0]);
|
||||
mx[1] = Math.Max(mx[1], v[1]);
|
||||
mx[2] = Math.Max(mx[2], v[2]);
|
||||
}
|
||||
public static void dtVmax(float[] mx, int mxStart, float[] v, int vStart)
|
||||
{
|
||||
mx[mxStart + 0] = Math.Max(mx[mxStart + 0], v[vStart + 0]);
|
||||
mx[mxStart + 1] = Math.Max(mx[mxStart + 1], v[vStart + 1]);
|
||||
mx[mxStart + 2] = Math.Max(mx[mxStart + 2], v[vStart + 2]);
|
||||
}
|
||||
|
||||
/// Sets the vector elements to the specified values.
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] x The x-value of the vector.
|
||||
/// @param[in] y The y-value of the vector.
|
||||
/// @param[in] z The z-value of the vector.
|
||||
public static void dtVset(float[] dest, float x, float y, float z)
|
||||
{
|
||||
dest[0] = x; dest[1] = y; dest[2] = z;
|
||||
}
|
||||
public static void dtVset(float[] dest, int destStart, float x, float y, float z)
|
||||
{
|
||||
dest[destStart + 0] = x; dest[destStart + 1] = y; dest[destStart + 2] = z;
|
||||
}
|
||||
|
||||
/// Performs a vector copy.
|
||||
/// @param[out] dest The result. [(x, y, z)]
|
||||
/// @param[in] a The vector to copy. [(x, y, z)]
|
||||
public static void dtVcopy(float[] dest, float[] a)
|
||||
{
|
||||
dest[0] = a[0];
|
||||
dest[1] = a[1];
|
||||
dest[2] = a[2];
|
||||
}
|
||||
public static void dtVcopy(float[] dest, int destStart, float[] a, int aStart)
|
||||
{
|
||||
dest[destStart + 0] = a[aStart + 0];
|
||||
dest[destStart + 1] = a[aStart + 1];
|
||||
dest[destStart + 2] = a[aStart + 2];
|
||||
}
|
||||
/// Derives the scalar length of the vector.
|
||||
/// @param[in] v The vector. [(x, y, z)]
|
||||
/// @return The scalar length of the vector.
|
||||
public static float dtVlen(float[] v)
|
||||
{
|
||||
return (float)Math.Sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
||||
}
|
||||
public static float dtVlen(float[] v, int vStart)
|
||||
{
|
||||
return (float)Math.Sqrt(v[0 + vStart] * v[0 + vStart] + v[1 + vStart] * v[1 + vStart] + v[2 + vStart] * v[2 + vStart]);
|
||||
}
|
||||
|
||||
/// Derives the square of the scalar length of the vector. (len * len)
|
||||
/// @param[in] v The vector. [(x, y, z)]
|
||||
/// @return The square of the scalar length of the vector.
|
||||
public static float dtVlenSqr(float[] v)
|
||||
{
|
||||
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
||||
}
|
||||
public static float dtVlenSqr(float[] v, int vStart)
|
||||
{
|
||||
return v[0 + vStart] * v[0 + vStart] + v[1 + vStart] * v[1 + vStart] + v[2 + vStart] * v[2 + vStart];
|
||||
}
|
||||
|
||||
/// Returns the distance between two points.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The distance between the two points.
|
||||
public static float dtVdist(float[] v1, float[] v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dy = v2[1] - v1[1];
|
||||
float dz = v2[2] - v1[2];
|
||||
return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
public static float dtVdist(float[] v1, int v1Start, float[] v2, int v2Start)
|
||||
{
|
||||
float dx = v2[v2Start + 0] - v1[v1Start + 0];
|
||||
float dy = v2[v2Start + 1] - v1[v1Start + 1];
|
||||
float dz = v2[v2Start + 2] - v1[v1Start + 2];
|
||||
return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
/// Returns the square of the distance between two points.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The square of the distance between the two points.
|
||||
public static float dtVdistSqr(float[] v1, float[] v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dy = v2[1] - v1[1];
|
||||
float dz = v2[2] - v1[2];
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
public static float dtVdistSqr(float[] v1, int v1Start, float[] v2, int v2Start)
|
||||
{
|
||||
float dx = v2[v2Start + 0] - v1[v1Start + 0];
|
||||
float dy = v2[v2Start + 1] - v1[v1Start + 1];
|
||||
float dz = v2[v2Start + 2] - v1[v1Start + 2];
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
/// Derives the distance between the specified points on the xz-plane.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The distance between the point on the xz-plane.
|
||||
///
|
||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
||||
public static float dtVdist2D(float[] v1, float[] v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dz = v2[2] - v1[2];
|
||||
return (float)Math.Sqrt(dx * dx + dz * dz);
|
||||
}
|
||||
public static float dtVdist2D(float[] v1, int v1Start, float[] v2, int v2Start)
|
||||
{
|
||||
float dx = v2[v2Start + 0] - v1[v1Start + 0];
|
||||
float dz = v2[v2Start + 2] - v1[v1Start + 2];
|
||||
return (float)Math.Sqrt(dx * dx + dz * dz);
|
||||
}
|
||||
/// Derives the square of the distance between the specified points on the xz-plane.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The square of the distance between the point on the xz-plane.
|
||||
public static float dtVdist2DSqr(float[] v1, float[] v2)
|
||||
{
|
||||
float dx = v2[0] - v1[0];
|
||||
float dz = v2[2] - v1[2];
|
||||
return dx * dx + dz * dz;
|
||||
}
|
||||
public static float dtVdist2DSqr(float[] v1, int v1Start, float[] v2, int v2Start)
|
||||
{
|
||||
float dx = v2[v2Start + 0] - v1[v1Start + 0];
|
||||
float dz = v2[v2Start + 2] - v1[v1Start + 2];
|
||||
return dx * dx + dz * dz;
|
||||
}
|
||||
/// Normalizes the vector.
|
||||
/// @param[in,out] v The vector to normalize. [(x, y, z)]
|
||||
public static void dtVnormalize(float[] v)
|
||||
{
|
||||
float d = 1.0f / (float)Math.Sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
||||
v[0] *= d;
|
||||
v[1] *= d;
|
||||
v[2] *= d;
|
||||
}
|
||||
public static void dtVnormalize(float[] v, int vStart)
|
||||
{
|
||||
float d = 1.0f / (float)Math.Sqrt(v[vStart + 0] * v[vStart + 0] + v[vStart + 1] * v[vStart + 1] + v[vStart + 2] * v[vStart + 2]);
|
||||
v[vStart + 0] *= d;
|
||||
v[vStart + 1] *= d;
|
||||
v[vStart + 2] *= d;
|
||||
}
|
||||
|
||||
/// Performs a 'sloppy' colocation check of the specified points.
|
||||
/// @param[in] p0 A point. [(x, y, z)]
|
||||
/// @param[in] p1 A point. [(x, y, z)]
|
||||
/// @return True if the points are considered to be at the same location.
|
||||
///
|
||||
/// Basically, this function will return true if the specified points are
|
||||
/// close enough to eachother to be considered colocated.
|
||||
public static bool dtVequal(float[] p0, float[] p1)
|
||||
{
|
||||
const float thrSqrt = (1.0f / 16384.0f);
|
||||
const float thr = thrSqrt * thrSqrt;
|
||||
float d = dtVdistSqr(p0, p1);
|
||||
return d < thr;
|
||||
}
|
||||
public static bool dtVequal(float[] p0, int p0Start, float[] p1, int p1Start)
|
||||
{
|
||||
const float thrSqrt = (1.0f / 16384.0f);
|
||||
const float thr = thrSqrt * thrSqrt;
|
||||
float d = dtVdistSqr(p0, p0Start, p1, p1Start);
|
||||
return d < thr;
|
||||
}
|
||||
/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
|
||||
/// @param[in] u A vector [(x, y, z)]
|
||||
/// @param[in] v A vector [(x, y, z)]
|
||||
/// @return The dot product on the xz-plane.
|
||||
///
|
||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
||||
public static float dtVdot2D(float[] u, float[] v)
|
||||
{
|
||||
return u[0] * v[0] + u[2] * v[2];
|
||||
}
|
||||
public static float dtVdot2D(float[] u, int uStart, float[] v, int vStart)
|
||||
{
|
||||
return u[uStart + 0] * v[vStart + 0] + u[uStart + 2] * v[vStart + 2];
|
||||
}
|
||||
|
||||
/// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz)
|
||||
/// @param[in] u The LHV vector [(x, y, z)]
|
||||
/// @param[in] v The RHV vector [(x, y, z)]
|
||||
/// @return The dot product on the xz-plane.
|
||||
///
|
||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
||||
public static float dtVperp2D(float[] u, float[] v)
|
||||
{
|
||||
return u[2] * v[0] - u[0] * v[2];
|
||||
}
|
||||
public static float dtVperp2D(float[] u, int uStart, float[] v, int vStart)
|
||||
{
|
||||
return u[uStart + 2] * v[vStart + 0] - u[uStart + 0] * v[vStart + 2];
|
||||
}
|
||||
/// @}
|
||||
/// @name Computational geometry helper functions.
|
||||
/// @{
|
||||
|
||||
/**
|
||||
|
||||
@fn float dtTriArea2D(const float* a, const float* b, const float* c)
|
||||
@par
|
||||
|
||||
The vertices are projected onto the xz-plane, so the y-values are ignored.
|
||||
|
||||
This is a low cost function than can be used for various purposes. Its main purpose
|
||||
is for point/line relationship testing.
|
||||
|
||||
In all cases: A value of zero indicates that all vertices are collinear or represent the same point.
|
||||
(On the xz-plane.)
|
||||
|
||||
When used for point/line relationship tests, AB usually represents a line against which
|
||||
the C point is to be tested. In this case:
|
||||
|
||||
A positive value indicates that point C is to the left of line AB, looking from A toward B.<br/>
|
||||
A negative value indicates that point C is to the right of lineAB, looking from A toward B.
|
||||
|
||||
When used for evaluating a triangle:
|
||||
|
||||
The absolute value of the return value is two times the area of the triangle when it is
|
||||
projected onto the xz-plane.
|
||||
|
||||
A positive return value indicates:
|
||||
|
||||
<ul>
|
||||
<li>The vertices are wrapped in the normal Detour wrap direction.</li>
|
||||
<li>The triangle's 3D face normal is in the general up direction.</li>
|
||||
</ul>
|
||||
|
||||
A negative return value indicates:
|
||||
|
||||
<ul>
|
||||
<li>The vertices are reverse wrapped. (Wrapped opposite the normal Detour wrap direction.)</li>
|
||||
<li>The triangle's 3D face normal is in the general down direction.</li>
|
||||
</ul>
|
||||
|
||||
*/
|
||||
|
||||
/// Derives the signed xz-plane area of the triangle ABC, or the relationship of line AB to point C.
|
||||
/// @param[in] a Vertex A. [(x, y, z)]
|
||||
/// @param[in] b Vertex B. [(x, y, z)]
|
||||
/// @param[in] c Vertex C. [(x, y, z)]
|
||||
/// @return The signed xz-plane area of the triangle.
|
||||
public static float dtTriArea2D(float[] a, float[] b, float[] c)
|
||||
{
|
||||
float abx = b[0] - a[0];
|
||||
float abz = b[2] - a[2];
|
||||
float acx = c[0] - a[0];
|
||||
float acz = c[2] - a[2];
|
||||
return acx * abz - abx * acz;
|
||||
}
|
||||
public static float dtTriArea2D(float[] a, int aStart, float[] b, int bStart, float[] c, int cStart)
|
||||
{
|
||||
float abx = b[bStart + 0] - a[aStart + 0];
|
||||
float abz = b[bStart + 2] - a[aStart + 2];
|
||||
float acx = c[cStart + 0] - a[aStart + 0];
|
||||
float acz = c[cStart + 2] - a[aStart + 2];
|
||||
return acx * abz - abx * acz;
|
||||
}
|
||||
/// Determines if two axis-aligned bounding boxes overlap.
|
||||
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
|
||||
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
|
||||
/// @return True if the two AABB's overlap.
|
||||
/// @see dtOverlapBounds
|
||||
public static bool dtOverlapQuantBounds(ushort[] amin, ushort[] amax, ushort[] bmin, ushort[] bmax)
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
|
||||
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
|
||||
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
/// Determines if two axis-aligned bounding boxes overlap.
|
||||
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
|
||||
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
|
||||
/// @return True if the two AABB's overlap.
|
||||
/// @see dtOverlapQuantBounds
|
||||
public static bool dtOverlapBounds(float[] amin, float[] amax, float[] bmin, float[] bmax)
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
|
||||
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
|
||||
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// @name Miscellanious functions.
|
||||
/// @{
|
||||
|
||||
public static uint dtNextPow2(uint v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
public static int dtIlog2(uint v)
|
||||
{
|
||||
//C# not happy with shifting uints
|
||||
int r;
|
||||
int shift;
|
||||
r = ((v > 0xffff) ? 1 : 0) << 4; v >>= r;
|
||||
shift = ((v > 0xff) ? 1 : 0) << 3; v >>= shift; r |= shift;
|
||||
shift = ((v > 0xf) ? 1 : 0) << 2; v >>= shift; r |= shift;
|
||||
shift = ((v > 0x3) ? 1 : 0) << 1; v >>= shift; r |= shift;
|
||||
r |= ((int)v >> 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static int dtAlign4(int x)
|
||||
{
|
||||
return (x + 3) & ~3;
|
||||
}
|
||||
|
||||
public static int dtOppositeTile(int side)
|
||||
{
|
||||
return (side + 4) & 0x7;
|
||||
}
|
||||
|
||||
public static void dtSwapEndian(ref ushort v)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(v);
|
||||
System.Array.Reverse(bytes);
|
||||
v = BitConverter.ToUInt16(bytes, 0);
|
||||
}
|
||||
|
||||
public static void dtSwapEndian(ref short v)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(v);
|
||||
System.Array.Reverse(bytes);
|
||||
v = BitConverter.ToInt16(bytes, 0);
|
||||
}
|
||||
|
||||
public static void dtSwapEndian(ref uint v)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(v);
|
||||
System.Array.Reverse(bytes);
|
||||
v = BitConverter.ToUInt32(bytes, 0);
|
||||
}
|
||||
|
||||
public static void dtSwapEndian(ref int v)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(v);
|
||||
System.Array.Reverse(bytes);
|
||||
v = BitConverter.ToInt32(bytes, 0);
|
||||
}
|
||||
|
||||
public static void dtSwapEndian(ref float v)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(v);
|
||||
System.Array.Reverse(bytes);
|
||||
v = BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
public static void dtcsArrayItemsCreate<T>(T[] array) where T : class, new()
|
||||
{
|
||||
for (int i = 0; i < array.Length; ++i)
|
||||
{
|
||||
array[i] = new T();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,301 @@
|
||||
using System.Diagnostics;
|
||||
using dtNodeIndex = System.UInt16;
|
||||
using dtPolyRef = System.UInt64;
|
||||
|
||||
public partial class Detour
|
||||
{
|
||||
// From Thomas Wang, https://gist.github.com/badboy/6267743
|
||||
public static uint dtHashRef(dtPolyRef a)
|
||||
{
|
||||
a = (~a) + (a << 18); // a = (a << 18) - a - 1;
|
||||
a = a ^ (a >> 31);
|
||||
a = a * 21; // a = (a + (a << 2)) + (a << 4);
|
||||
a = a ^ (a >> 11);
|
||||
a = a + (a << 6);
|
||||
a = a ^ (a >> 22);
|
||||
return (uint)a;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Detour
|
||||
{
|
||||
|
||||
public enum dtNodeFlags
|
||||
{
|
||||
DT_NODE_OPEN = 0x01,
|
||||
DT_NODE_CLOSED = 0x02,
|
||||
};
|
||||
|
||||
public const dtNodeIndex DT_NULL_IDX = dtNodeIndex.MaxValue; //(dtNodeIndex)~0;
|
||||
|
||||
public class dtNode
|
||||
{
|
||||
public float[] pos = new float[3]; //< Position of the node.
|
||||
public float cost; //< Cost from previous node to current node.
|
||||
public float total; //< Cost up to the node.
|
||||
public uint pidx;// : 30; //< Index to parent node.
|
||||
public byte flags;// : 2; //< Node flags 0/open/closed.
|
||||
public dtPolyRef id; //< Polygon ref the node corresponds to.
|
||||
///
|
||||
public static int getSizeOf()
|
||||
{
|
||||
//C# can't guess the sizeof of the float array, let's pretend
|
||||
return sizeof(float) * (3 + 1 + 1)
|
||||
+ sizeof(uint)
|
||||
+ sizeof(byte)
|
||||
+ sizeof(dtPolyRef);
|
||||
}
|
||||
public void dtcsClearFlag(dtNodeFlags flag)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
flags &= (byte)(~flag);
|
||||
}
|
||||
}
|
||||
public void dtcsSetFlag(dtNodeFlags flag)
|
||||
{
|
||||
flags |= (byte)flag;
|
||||
}
|
||||
public bool dtcsTestFlag(dtNodeFlags flag)
|
||||
{
|
||||
return (flags & (byte)flag) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public class dtNodePool
|
||||
{
|
||||
private dtNode[] m_nodes;
|
||||
private dtNodeIndex[] m_first;
|
||||
private dtNodeIndex[] m_next;
|
||||
private int m_maxNodes;
|
||||
private int m_hashSize;
|
||||
private int m_nodeCount;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
public dtNodePool(int maxNodes, int hashSize)
|
||||
|
||||
{
|
||||
m_maxNodes = maxNodes;
|
||||
m_hashSize = hashSize;
|
||||
|
||||
Debug.Assert(dtNextPow2((uint)m_hashSize) == (uint)m_hashSize);
|
||||
Debug.Assert(m_maxNodes > 0);
|
||||
|
||||
m_nodes = new dtNode[m_maxNodes];
|
||||
dtcsArrayItemsCreate(m_nodes);
|
||||
m_next = new dtNodeIndex[m_maxNodes];
|
||||
m_first = new dtNodeIndex[hashSize];
|
||||
|
||||
Debug.Assert(m_nodes != null);
|
||||
Debug.Assert(m_next != null);
|
||||
Debug.Assert(m_first != null);
|
||||
|
||||
for (int i = 0; i < hashSize; ++i)
|
||||
{
|
||||
m_first[i] = DT_NULL_IDX;
|
||||
}
|
||||
for (int i = 0; i < m_maxNodes; ++i)
|
||||
{
|
||||
m_next[i] = DT_NULL_IDX;
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
for (int i = 0; i < m_hashSize; ++i)
|
||||
{
|
||||
m_first[i] = DT_NULL_IDX;
|
||||
}
|
||||
m_nodeCount = 0;
|
||||
}
|
||||
|
||||
public uint getNodeIdx(dtNode node)
|
||||
{
|
||||
if (node == null)
|
||||
return 0;
|
||||
|
||||
return (uint)(System.Array.IndexOf(m_nodes, node)) + 1;
|
||||
}
|
||||
|
||||
public dtNode getNodeAtIdx(uint idx)
|
||||
{
|
||||
if (idx == 0)
|
||||
return null;
|
||||
return m_nodes[idx - 1];
|
||||
}
|
||||
|
||||
public int getMemUsed()
|
||||
{
|
||||
return
|
||||
sizeof(int) * 3 +
|
||||
dtNode.getSizeOf() * m_maxNodes +
|
||||
sizeof(dtNodeIndex) * m_maxNodes +
|
||||
sizeof(dtNodeIndex) * m_hashSize;
|
||||
}
|
||||
|
||||
public int getMaxNodes()
|
||||
{
|
||||
return m_maxNodes;
|
||||
}
|
||||
|
||||
public int getHashSize()
|
||||
{
|
||||
return m_hashSize;
|
||||
}
|
||||
public dtNodeIndex getFirst(int bucket)
|
||||
{
|
||||
return m_first[bucket];
|
||||
}
|
||||
public dtNodeIndex getNext(int i)
|
||||
{
|
||||
return m_next[i];
|
||||
}
|
||||
|
||||
public dtNode findNode(dtPolyRef id)
|
||||
{
|
||||
uint bucket = (uint)(dtHashRef(id) & (m_hashSize - 1));
|
||||
dtNodeIndex i = m_first[bucket];
|
||||
while (i != DT_NULL_IDX)
|
||||
{
|
||||
if (m_nodes[i].id == id)
|
||||
return m_nodes[i];
|
||||
i = m_next[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public dtNode getNode(dtPolyRef id)
|
||||
{
|
||||
uint bucket = (uint)(dtHashRef(id) & (m_hashSize - 1));
|
||||
dtNodeIndex i = m_first[bucket];
|
||||
dtNode node = null;
|
||||
while (i != DT_NULL_IDX)
|
||||
{
|
||||
if (m_nodes[i].id == id)
|
||||
return m_nodes[i];
|
||||
i = m_next[i];
|
||||
}
|
||||
|
||||
if (m_nodeCount >= m_maxNodes)
|
||||
return null;
|
||||
|
||||
i = (dtNodeIndex)m_nodeCount;
|
||||
m_nodeCount++;
|
||||
|
||||
// Init node
|
||||
node = m_nodes[i];
|
||||
node.pidx = 0;
|
||||
node.cost = 0;
|
||||
node.total = 0;
|
||||
node.id = id;
|
||||
node.flags = 0;
|
||||
|
||||
m_next[i] = m_first[bucket];
|
||||
m_first[bucket] = i;
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
public class dtNodeQueue
|
||||
{
|
||||
private dtNode[] m_heap;
|
||||
private int m_capacity;
|
||||
private int m_size;
|
||||
|
||||
public dtNodeQueue(int n)
|
||||
{
|
||||
m_capacity = n;
|
||||
Debug.Assert(m_capacity > 0);
|
||||
|
||||
m_heap = new dtNode[m_capacity + 1];//(dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM);
|
||||
Debug.Assert(m_heap != null);
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
public dtNode top()
|
||||
{
|
||||
return m_heap[0];
|
||||
}
|
||||
|
||||
public dtNode pop()
|
||||
{
|
||||
dtNode result = m_heap[0];
|
||||
m_size--;
|
||||
trickleDown(0, m_heap[m_size]);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void push(dtNode node)
|
||||
{
|
||||
m_size++;
|
||||
bubbleUp(m_size - 1, node);
|
||||
}
|
||||
|
||||
public void modify(dtNode node)
|
||||
{
|
||||
for (int i = 0; i < m_size; ++i)
|
||||
{
|
||||
if (m_heap[i] == node)
|
||||
{
|
||||
bubbleUp(i, node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool empty()
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
public int getMemUsed()
|
||||
{
|
||||
return sizeof(int) * 2 +
|
||||
dtNode.getSizeOf() * (m_capacity + 1);
|
||||
}
|
||||
|
||||
public int getCapacity()
|
||||
{
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
|
||||
public void bubbleUp(int i, dtNode node)
|
||||
{
|
||||
int parent = (i - 1) / 2;
|
||||
// note: (index > 0) means there is a parent
|
||||
while ((i > 0) && (m_heap[parent].total > node.total))
|
||||
{
|
||||
m_heap[i] = m_heap[parent];
|
||||
i = parent;
|
||||
parent = (i - 1) / 2;
|
||||
}
|
||||
m_heap[i] = node;
|
||||
}
|
||||
|
||||
public void trickleDown(int i, dtNode node)
|
||||
{
|
||||
int child = (i * 2) + 1;
|
||||
while (child < m_size)
|
||||
{
|
||||
if (((child + 1) < m_size) &&
|
||||
(m_heap[child].total > m_heap[child + 1].total))
|
||||
{
|
||||
child++;
|
||||
}
|
||||
m_heap[i] = m_heap[child];
|
||||
i = child;
|
||||
child = (i * 2) + 1;
|
||||
}
|
||||
bubbleUp(i, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using dtStatus = System.UInt32;
|
||||
|
||||
public static partial class Detour
|
||||
{
|
||||
// High level status.
|
||||
public const uint DT_FAILURE = 1u << 31; // Operation failed.
|
||||
public const uint DT_SUCCESS = 1u << 30; // Operation succeed.
|
||||
public const uint DT_IN_PROGRESS = 1u << 29; // Operation still in progress.
|
||||
|
||||
// Detail information for status.
|
||||
public const uint DT_STATUS_DETAIL_MASK = 0x0ffffff;
|
||||
public const uint DT_WRONG_MAGIC = 1 << 0; // Input data is not recognized.
|
||||
public const uint DT_WRONG_VERSION = 1 << 1; // Input data is in wrong version.
|
||||
public const uint DT_OUT_OF_MEMORY = 1 << 2; // Operation ran out of memory.
|
||||
public const uint DT_INVALID_PARAM = 1 << 3; // An input parameter was invalid.
|
||||
public const uint DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results.
|
||||
public const uint DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search.
|
||||
public const uint DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess.
|
||||
|
||||
|
||||
// Returns true of status is success.
|
||||
public static bool dtStatusSucceed(dtStatus status)
|
||||
{
|
||||
return (status & DT_SUCCESS) != 0;
|
||||
}
|
||||
|
||||
// Returns true of status is failure.
|
||||
public static bool dtStatusFailed(dtStatus status)
|
||||
{
|
||||
return (status & DT_FAILURE) != 0;
|
||||
}
|
||||
|
||||
// Returns true of status is in progress.
|
||||
public static bool dtStatusInProgress(dtStatus status)
|
||||
{
|
||||
return (status & DT_IN_PROGRESS) != 0;
|
||||
}
|
||||
|
||||
// Returns true if specific detail is set.
|
||||
public static bool dtStatusDetail(dtStatus status, uint detail)
|
||||
{
|
||||
return (status & detail) != 0;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,526 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class Recast {
|
||||
/// @par
|
||||
///
|
||||
/// Basically, any spans that are closer to a boundary or obstruction than the specified radius
|
||||
/// are marked as unwalkable.
|
||||
///
|
||||
/// This method is usually called immediately after the heightfield has been built.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
|
||||
public static bool rcErodeWalkableArea(rcContext ctx, int radius, rcCompactHeightfield chf) {
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
int w = chf.width;
|
||||
int h = chf.height;
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_ERODE_AREA);
|
||||
|
||||
byte[] dist = new byte[chf.spanCount];//(byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (dist == null) {
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' " + chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
for (int i=0; i < chf.spanCount; ++i) {
|
||||
dist[i] = 0xff;
|
||||
}
|
||||
// memset(dist, 0xff, sizeof(byte)*chf.spanCount);
|
||||
|
||||
// Mark boundary cells.
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
rcCompactCell c = chf.cells[x + y * w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
|
||||
if (chf.areas[i] == RC_NULL_AREA) {
|
||||
dist[i] = 0;
|
||||
} else {
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
int nc = 0;
|
||||
for (int dir = 0; dir < 4; ++dir) {
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED) {
|
||||
int nx = x + rcGetDirOffsetX(dir);
|
||||
int ny = y + rcGetDirOffsetY(dir);
|
||||
int nidx = (int)chf.cells[nx + ny * w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[nidx] != RC_NULL_AREA) {
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At least one missing neighbour.
|
||||
if (nc != 4)
|
||||
dist[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte nd = 0;
|
||||
|
||||
// Pass 1
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
rcCompactCell c = chf.cells[x + y * w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED) {
|
||||
// (-1,0)
|
||||
int ax = x + rcGetDirOffsetX(0);
|
||||
int ay = y + rcGetDirOffsetY(0);
|
||||
int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 0);
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
nd = (byte)Math.Min((int)dist[ai] + 2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,-1)
|
||||
if (rcGetCon(aSpan, 3) != RC_NOT_CONNECTED) {
|
||||
int aax = ax + rcGetDirOffsetX(3);
|
||||
int aay = ay + rcGetDirOffsetY(3);
|
||||
int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 3);
|
||||
nd = (byte)Math.Min((int)dist[aai] + 3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 3) != RC_NOT_CONNECTED) {
|
||||
// (0,-1)
|
||||
int ax = x + rcGetDirOffsetX(3);
|
||||
int ay = y + rcGetDirOffsetY(3);
|
||||
int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 3);
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
nd = (byte)Math.Min((int)dist[ai] + 2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,-1)
|
||||
if (rcGetCon(aSpan, 2) != RC_NOT_CONNECTED) {
|
||||
int aax = ax + rcGetDirOffsetX(2);
|
||||
int aay = ay + rcGetDirOffsetY(2);
|
||||
int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 2);
|
||||
nd = (byte)Math.Min((int)dist[aai] + 3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2
|
||||
for (int y = h - 1; y >= 0; --y) {
|
||||
for (int x = w - 1; x >= 0; --x) {
|
||||
rcCompactCell c = chf.cells[x + y * w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 2) != RC_NOT_CONNECTED) {
|
||||
// (1,0)
|
||||
int ax = x + rcGetDirOffsetX(2);
|
||||
int ay = y + rcGetDirOffsetY(2);
|
||||
int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 2);
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
nd = (byte)Math.Min((int)dist[ai] + 2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,1)
|
||||
if (rcGetCon(aSpan, 1) != RC_NOT_CONNECTED) {
|
||||
int aax = ax + rcGetDirOffsetX(1);
|
||||
int aay = ay + rcGetDirOffsetY(1);
|
||||
int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 1);
|
||||
nd = (byte)Math.Min((int)dist[aai] + 3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 1) != RC_NOT_CONNECTED) {
|
||||
// (0,1)
|
||||
int ax = x + rcGetDirOffsetX(1);
|
||||
int ay = y + rcGetDirOffsetY(1);
|
||||
int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, 1);
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
nd = (byte)Math.Min((int)dist[ai] + 2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,1)
|
||||
if (rcGetCon(aSpan, 0) != RC_NOT_CONNECTED) {
|
||||
int aax = ax + rcGetDirOffsetX(0);
|
||||
int aay = ay + rcGetDirOffsetY(0);
|
||||
int aai = (int)chf.cells[aax + aay * w].index + rcGetCon(aSpan, 0);
|
||||
nd = (byte)Math.Min((int)dist[aai] + 3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte thr = (byte)(radius * 2);
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
if (dist[i] < thr)
|
||||
chf.areas[i] = RC_NULL_AREA;
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_ERODE_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void insertSort(byte[] a, int n) {
|
||||
int i, j;
|
||||
for (i = 1; i < n; i++) {
|
||||
byte value = a[i];
|
||||
for (j = i - 1; j >= 0 && a[j] > value; j--)
|
||||
a[j + 1] = a[j];
|
||||
a[j + 1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This filter is usually applied after applying area id's using functions
|
||||
/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
|
||||
///
|
||||
/// @see rcCompactHeightfield
|
||||
public static bool rcMedianFilterWalkableArea(rcContext ctx, rcCompactHeightfield chf) {
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
int w = chf.width;
|
||||
int h = chf.height;
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
byte[] areas = new byte[chf.spanCount];//(byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (areas == null) {
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' " + chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
for (int i = 0; i < chf.spanCount; ++i) {
|
||||
areas[i] = 0xff;
|
||||
}
|
||||
//memset(areas, 0xff, sizeof(byte)*chf.spanCount);
|
||||
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
rcCompactCell c = chf.cells[x + y * w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA) {
|
||||
areas[i] = chf.areas[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] nei = new byte[9];
|
||||
for (int j = 0; j < 9; ++j)
|
||||
nei[j] = chf.areas[i];
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir) {
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED) {
|
||||
int ax = x + rcGetDirOffsetX(dir);
|
||||
int ay = y + rcGetDirOffsetY(dir);
|
||||
int ai = (int)chf.cells[ax + ay * w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[ai] != RC_NULL_AREA)
|
||||
nei[dir * 2 + 0] = chf.areas[ai];
|
||||
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
int dir2 = (dir + 1) & 0x3;
|
||||
if (rcGetCon(aSpan, dir2) != RC_NOT_CONNECTED) {
|
||||
int ax2 = ax + rcGetDirOffsetX(dir2);
|
||||
int ay2 = ay + rcGetDirOffsetY(dir2);
|
||||
int ai2 = (int)chf.cells[ax2 + ay2 * w].index + rcGetCon(aSpan, dir2);
|
||||
if (chf.areas[ai2] != RC_NULL_AREA)
|
||||
nei[dir * 2 + 1] = chf.areas[ai2];
|
||||
}
|
||||
}
|
||||
}
|
||||
insertSort(nei, 9);
|
||||
areas[i] = nei[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chf.areas = areas;
|
||||
//memcpy(chf.areas, areas, sizeof(byte)*chf.spanCount);
|
||||
|
||||
//rcFree(areas);
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
public static void rcMarkBoxArea(rcContext ctx, float[] bmin, float[] bmax, byte areaId,
|
||||
rcCompactHeightfield chf) {
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
|
||||
int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
|
||||
int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
|
||||
int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
|
||||
int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
|
||||
int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width - 1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height - 1;
|
||||
|
||||
for (int z = minz; z <= maxz; ++z) {
|
||||
for (int x = minx; x <= maxx; ++x) {
|
||||
rcCompactCell c = chf.cells[x + z * chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy) {
|
||||
if (chf.areas[i] != RC_NULL_AREA)
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static bool pointInPoly(int nvert, float[] verts, float[] p) {
|
||||
bool c = false;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
|
||||
int viStart = i * 3;
|
||||
int vjStart = j * 3;
|
||||
if (((verts[viStart + 2] > p[2]) != (verts[vjStart + 2] > p[2])) &&
|
||||
(p[0] < (verts[vjStart + 0] - verts[viStart + 0]) * (p[2] - verts[viStart + 2]) / (verts[vjStart + 2] - verts[viStart + 2]) + verts[viStart + 0])) {
|
||||
c = !c;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// The y-values of the polygon vertices are ignored. So the polygon is effectively
|
||||
/// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
public static void rcMarkConvexPolyArea(rcContext ctx, float[] verts, int nverts,
|
||||
float hmin, float hmax, byte areaId,
|
||||
rcCompactHeightfield chf) {
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
|
||||
float[] bmin = new float[3];
|
||||
float[] bmax = new float[3];
|
||||
rcVcopy(bmin, verts);
|
||||
rcVcopy(bmax, verts);
|
||||
for (int i = 1; i < nverts; ++i) {
|
||||
int vStart = i * 3;
|
||||
rcVmin(bmin, 0, verts, vStart);
|
||||
rcVmax(bmax, 0, verts, vStart);
|
||||
}
|
||||
bmin[1] = hmin;
|
||||
bmax[1] = hmax;
|
||||
|
||||
int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
|
||||
int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
|
||||
int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
|
||||
int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
|
||||
int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
|
||||
int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width - 1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height - 1;
|
||||
|
||||
|
||||
// TODO: Optimize.
|
||||
for (int z = minz; z <= maxz; ++z) {
|
||||
for (int x = minx; x <= maxx; ++x) {
|
||||
rcCompactCell c = chf.cells[x + z * chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy) {
|
||||
float[] p = new float[3];
|
||||
p[0] = chf.bmin[0] + (x + 0.5f) * chf.cs;
|
||||
p[1] = 0;
|
||||
p[2] = chf.bmin[2] + (z + 0.5f) * chf.cs;
|
||||
|
||||
if (pointInPoly(nverts, verts, p)) {
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
}
|
||||
|
||||
static int rcOffsetPoly(float[] verts, int nverts, float offset,
|
||||
float[] outVerts, int maxOutVerts) {
|
||||
const float MITER_LIMIT = 1.20f;
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < nverts; i++) {
|
||||
int a = (i + nverts - 1) % nverts;
|
||||
int b = i;
|
||||
int c = (i + 1) % nverts;
|
||||
int vaStart = a * 3;
|
||||
int vbStart = b * 3;
|
||||
int vcStart = c * 3;
|
||||
float dx0 = verts[vbStart + 0] - verts[vaStart + 0];
|
||||
float dy0 = verts[vbStart + 2] - verts[vaStart + 2];
|
||||
float d0 = dx0 * dx0 + dy0 * dy0;
|
||||
if (d0 > 1e-6f) {
|
||||
d0 = 1.0f / (float)Math.Sqrt(d0);
|
||||
dx0 *= d0;
|
||||
dy0 *= d0;
|
||||
}
|
||||
float dx1 = verts[vcStart + 0] - verts[vbStart + 0];
|
||||
float dy1 = verts[vcStart + 2] - verts[vbStart + 2];
|
||||
float d1 = dx1 * dx1 + dy1 * dy1;
|
||||
if (d1 > 1e-6f) {
|
||||
d1 = 1.0f / (float)Math.Sqrt(d1);
|
||||
dx1 *= d1;
|
||||
dy1 *= d1;
|
||||
}
|
||||
float dlx0 = -dy0;
|
||||
float dly0 = dx0;
|
||||
float dlx1 = -dy1;
|
||||
float dly1 = dx1;
|
||||
float cross = dx1 * dy0 - dx0 * dy1;
|
||||
float dmx = (dlx0 + dlx1) * 0.5f;
|
||||
float dmy = (dly0 + dly1) * 0.5f;
|
||||
float dmr2 = dmx * dmx + dmy * dmy;
|
||||
bool bevel = dmr2 * MITER_LIMIT * MITER_LIMIT < 1.0f;
|
||||
if (dmr2 > 1e-6f) {
|
||||
float scale = 1.0f / dmr2;
|
||||
dmx *= scale;
|
||||
dmy *= scale;
|
||||
}
|
||||
|
||||
if (bevel && cross < 0.0f) {
|
||||
if (n + 2 >= maxOutVerts)
|
||||
return 0;
|
||||
float d = (1.0f - (dx0 * dx1 + dy0 * dy1)) * 0.5f;
|
||||
outVerts[n * 3 + 0] = verts[vbStart + 0] + (-dlx0 + dx0 * d) * offset;
|
||||
outVerts[n * 3 + 1] = verts[vbStart + 1];
|
||||
outVerts[n * 3 + 2] = verts[vbStart + 2] + (-dly0 + dy0 * d) * offset;
|
||||
n++;
|
||||
outVerts[n * 3 + 0] = verts[vbStart + 0] + (-dlx1 - dx1 * d) * offset;
|
||||
outVerts[n * 3 + 1] = verts[vbStart + 1];
|
||||
outVerts[n * 3 + 2] = verts[vbStart + 2] + (-dly1 - dy1 * d) * offset;
|
||||
n++;
|
||||
} else {
|
||||
if (n + 1 >= maxOutVerts)
|
||||
return 0;
|
||||
outVerts[n * 3 + 0] = verts[vbStart + 0] - dmx * offset;
|
||||
outVerts[n * 3 + 1] = verts[vbStart + 1];
|
||||
outVerts[n * 3 + 2] = verts[vbStart + 2] - dmy * offset;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
static public void rcMarkCylinderArea(rcContext ctx, float[] pos,
|
||||
float r, float h, byte areaId,
|
||||
rcCompactHeightfield chf) {
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA);
|
||||
|
||||
float[] bmin = new float[3];
|
||||
float[] bmax = new float[3];
|
||||
bmin[0] = pos[0] - r;
|
||||
bmin[1] = pos[1];
|
||||
bmin[2] = pos[2] - r;
|
||||
bmax[0] = pos[0] + r;
|
||||
bmax[1] = pos[1] + h;
|
||||
bmax[2] = pos[2] + r;
|
||||
float r2 = r * r;
|
||||
|
||||
int minx = (int)((bmin[0] - chf.bmin[0]) / chf.cs);
|
||||
int miny = (int)((bmin[1] - chf.bmin[1]) / chf.ch);
|
||||
int minz = (int)((bmin[2] - chf.bmin[2]) / chf.cs);
|
||||
int maxx = (int)((bmax[0] - chf.bmin[0]) / chf.cs);
|
||||
int maxy = (int)((bmax[1] - chf.bmin[1]) / chf.ch);
|
||||
int maxz = (int)((bmax[2] - chf.bmin[2]) / chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width - 1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height - 1;
|
||||
|
||||
|
||||
for (int z = minz; z <= maxz; ++z) {
|
||||
for (int x = minx; x <= maxx; ++x) {
|
||||
rcCompactCell c = chf.cells[x + z * chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) {
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy) {
|
||||
float sx = chf.bmin[0] + (x + 0.5f) * chf.cs;
|
||||
float sz = chf.bmin[2] + (z + 0.5f) * chf.cs;
|
||||
float dx = sx - pos[0];
|
||||
float dz = sz - pos[2];
|
||||
|
||||
if (dx * dx + dz * dz < r2) {
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_MARK_CYLINDER_AREA);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,860 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class Recast{
|
||||
static int getCornerHeight(int x, int y, int i, int dir,
|
||||
rcCompactHeightfield chf,
|
||||
ref bool isBorderVertex)
|
||||
{
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
int ch = (int)s.y;
|
||||
int dirp = (dir+1) & 0x3;
|
||||
|
||||
uint[] regs = new uint[] {0,0,0,0};
|
||||
|
||||
// Combine region and area codes in order to prevent
|
||||
// border vertices which are in between two areas to be removed.
|
||||
regs[0] = (uint)( chf.spans[i].reg | (chf.areas[i] << 16) );
|
||||
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = x + rcGetDirOffsetX(dir);
|
||||
int ay = y + rcGetDirOffsetY(dir);
|
||||
int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
ch = Math.Max(ch, (int)aSpan.y);
|
||||
regs[1] = (uint)( chf.spans[ai].reg | (chf.areas[ai] << 16) );
|
||||
if (rcGetCon(aSpan, dirp) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax2 = ax + rcGetDirOffsetX(dirp);
|
||||
int ay2 = ay + rcGetDirOffsetY(dirp);
|
||||
int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dirp);
|
||||
rcCompactSpan as2 = chf.spans[ai2];
|
||||
ch = Math.Max(ch, (int)as2.y);
|
||||
regs[2] = (uint)(chf.spans[ai2].reg | (chf.areas[ai2] << 16));
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, dirp) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = x + rcGetDirOffsetX(dirp);
|
||||
int ay = y + rcGetDirOffsetY(dirp);
|
||||
int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
ch = Math.Max(ch, (int)aSpan.y);
|
||||
regs[3] = (uint)(chf.spans[ai].reg | (chf.areas[ai] << 16));
|
||||
if (rcGetCon(aSpan, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax2 = ax + rcGetDirOffsetX(dir);
|
||||
int ay2 = ay + rcGetDirOffsetY(dir);
|
||||
int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(aSpan, dir);
|
||||
rcCompactSpan as2 = chf.spans[ai2];
|
||||
ch = Math.Max(ch, (int)as2.y);
|
||||
regs[2] = (uint)(chf.spans[ai2].reg | (chf.areas[ai2] << 16));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the vertex is special edge vertex, these vertices will be removed later.
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
int a = j;
|
||||
int b = (j+1) & 0x3;
|
||||
int c = (j+2) & 0x3;
|
||||
int d = (j+3) & 0x3;
|
||||
|
||||
// The vertex is a border vertex there are two same exterior cells in a row,
|
||||
// followed by two interior cells and none of the regions are out of bounds.
|
||||
bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b];
|
||||
bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0;
|
||||
bool intsSameArea = (regs[c]>>16) == (regs[d]>>16);
|
||||
bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
|
||||
if (twoSameExts && twoInts && intsSameArea && noZeros)
|
||||
{
|
||||
isBorderVertex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
public static void walkContour(int x, int y, int i,
|
||||
rcCompactHeightfield chf,
|
||||
byte[] flags, List<int> points)
|
||||
{
|
||||
// Choose the first non-connected edge
|
||||
byte dir = 0;
|
||||
while ((flags[i] & (1 << dir)) == 0)
|
||||
dir++;
|
||||
|
||||
byte startDir = dir;
|
||||
int starti = i;
|
||||
|
||||
byte area = chf.areas[i];
|
||||
|
||||
int iter = 0;
|
||||
while (++iter < 40000)
|
||||
{
|
||||
if ((flags[i] & (1 << dir)) != 0)
|
||||
{
|
||||
// Choose the edge corner
|
||||
bool isBorderVertex = false;
|
||||
bool isAreaBorder = false;
|
||||
int px = x;
|
||||
int py = getCornerHeight(x, y, i, dir, chf,ref isBorderVertex);
|
||||
int pz = y;
|
||||
switch(dir)
|
||||
{
|
||||
case 0: pz++; break;
|
||||
case 1: px++; pz++; break;
|
||||
case 2: px++; break;
|
||||
}
|
||||
int r = 0;
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = x + rcGetDirOffsetX(dir);
|
||||
int ay = y + rcGetDirOffsetY(dir);
|
||||
int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
|
||||
r = (int)chf.spans[ai].reg;
|
||||
if (area != chf.areas[ai])
|
||||
isAreaBorder = true;
|
||||
}
|
||||
if (isBorderVertex)
|
||||
r |= RC_BORDER_VERTEX;
|
||||
if (isAreaBorder)
|
||||
r |= RC_AREA_BORDER;
|
||||
points.Add(px);
|
||||
points.Add(py);
|
||||
points.Add(pz);
|
||||
points.Add(r);
|
||||
|
||||
flags[i] &= (byte)( ~(1 << dir) ); // Remove visited edges
|
||||
dir = (byte)( (dir+1) & 0x3); // Rotate CW
|
||||
}
|
||||
else
|
||||
{
|
||||
int ni = -1;
|
||||
int nx = x + rcGetDirOffsetX(dir);
|
||||
int ny = y + rcGetDirOffsetY(dir);
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
rcCompactCell nc = chf.cells[nx+ny*chf.width];
|
||||
ni = (int)nc.index + rcGetCon(s, dir);
|
||||
}
|
||||
if (ni == -1)
|
||||
{
|
||||
// Should not happen.
|
||||
return;
|
||||
}
|
||||
x = nx;
|
||||
y = ny;
|
||||
i = ni;
|
||||
dir = (byte)((dir+3) & 0x3); // Rotate CCW
|
||||
}
|
||||
|
||||
if (starti == i && startDir == dir)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static float distancePtSeg(int x, int z,
|
||||
int px, int pz,
|
||||
int qx, int qz)
|
||||
{
|
||||
/* float pqx = (float)(qx - px);
|
||||
float pqy = (float)(qy - py);
|
||||
float pqz = (float)(qz - pz);
|
||||
float dx = (float)(x - px);
|
||||
float dy = (float)(y - py);
|
||||
float dz = (float)(z - pz);
|
||||
float d = pqx*pqx + pqy*pqy + pqz*pqz;
|
||||
float t = pqx*dx + pqy*dy + pqz*dz;
|
||||
if (d > 0)
|
||||
t /= d;
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
else if (t > 1)
|
||||
t = 1;
|
||||
|
||||
dx = px + t*pqx - x;
|
||||
dy = py + t*pqy - y;
|
||||
dz = pz + t*pqz - z;
|
||||
|
||||
return dx*dx + dy*dy + dz*dz;*/
|
||||
|
||||
float pqx = (float)(qx - px);
|
||||
float pqz = (float)(qz - pz);
|
||||
float dx = (float)(x - px);
|
||||
float dz = (float)(z - pz);
|
||||
float d = pqx*pqx + pqz*pqz;
|
||||
float t = pqx*dx + pqz*dz;
|
||||
if (d > 0)
|
||||
t /= d;
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
else if (t > 1)
|
||||
t = 1;
|
||||
|
||||
dx = px + t*pqx - x;
|
||||
dz = pz + t*pqz - z;
|
||||
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
public static void simplifyContour(List<int> points, List<int> simplified,
|
||||
float maxError, int maxEdgeLen, int buildFlags)
|
||||
{
|
||||
// Add initial points.
|
||||
bool hasConnections = false;
|
||||
for (int i = 0; i < points.Count; i += 4)
|
||||
{
|
||||
if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0)
|
||||
{
|
||||
hasConnections = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConnections)
|
||||
{
|
||||
// The contour has some portals to other regions.
|
||||
// Add a new point to every location where the region changes.
|
||||
for (int i = 0, ni = points.Count /4; i < ni; ++i)
|
||||
{
|
||||
int ii = (i+1) % ni;
|
||||
bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK);
|
||||
bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER);
|
||||
if (differentRegs || areaBorders)
|
||||
{
|
||||
simplified.Add(points[i*4+0]);
|
||||
simplified.Add(points[i*4+1]);
|
||||
simplified.Add(points[i*4+2]);
|
||||
simplified.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (simplified.Count == 0)
|
||||
{
|
||||
// If there is no connections at all,
|
||||
// create some initial points for the simplification process.
|
||||
// Find lower-left and upper-right vertices of the contour.
|
||||
int llx = points[0];
|
||||
int lly = points[1];
|
||||
int llz = points[2];
|
||||
int lli = 0;
|
||||
int urx = points[0];
|
||||
int ury = points[1];
|
||||
int urz = points[2];
|
||||
int uri = 0;
|
||||
for (int i = 0; i < points.Count; i += 4)
|
||||
{
|
||||
int x = points[i+0];
|
||||
int y = points[i+1];
|
||||
int z = points[i+2];
|
||||
if (x < llx || (x == llx && z < llz))
|
||||
{
|
||||
llx = x;
|
||||
lly = y;
|
||||
llz = z;
|
||||
lli = i/4;
|
||||
}
|
||||
if (x > urx || (x == urx && z > urz))
|
||||
{
|
||||
urx = x;
|
||||
ury = y;
|
||||
urz = z;
|
||||
uri = i/4;
|
||||
}
|
||||
}
|
||||
simplified.Add(llx);
|
||||
simplified.Add(lly);
|
||||
simplified.Add(llz);
|
||||
simplified.Add(lli);
|
||||
|
||||
simplified.Add(urx);
|
||||
simplified.Add(ury);
|
||||
simplified.Add(urz);
|
||||
simplified.Add(uri);
|
||||
}
|
||||
|
||||
// Add points until all raw points are within
|
||||
// error tolerance to the simplified shape.
|
||||
int pn = points.Count/4;
|
||||
for (int i = 0; i < simplified.Count/4; )
|
||||
{
|
||||
int ii = (i+1) % (simplified.Count/4);
|
||||
|
||||
int ax = simplified[i*4+0];
|
||||
int az = simplified[i*4+2];
|
||||
int ai = simplified[i*4+3];
|
||||
|
||||
int bx = simplified[ii*4+0];
|
||||
int bz = simplified[ii*4+2];
|
||||
int bi = simplified[ii*4+3];
|
||||
|
||||
// Find maximum deviation from the segment.
|
||||
float maxd = 0;
|
||||
int maxi = -1;
|
||||
int ci, cinc, endi;
|
||||
|
||||
// Traverse the segment in lexilogical order so that the
|
||||
// max deviation is calculated similarly when traversing
|
||||
// opposite segments.
|
||||
if (bx > ax || (bx == ax && bz > az))
|
||||
{
|
||||
cinc = 1;
|
||||
ci = (ai+cinc) % pn;
|
||||
endi = bi;
|
||||
}
|
||||
else
|
||||
{
|
||||
cinc = pn-1;
|
||||
ci = (bi+cinc) % pn;
|
||||
endi = ai;
|
||||
}
|
||||
|
||||
// Tessellate only outer edges or edges between areas.
|
||||
if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 ||
|
||||
(points[ci*4+3] & RC_AREA_BORDER) != 0)
|
||||
{
|
||||
while (ci != endi)
|
||||
{
|
||||
float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz);
|
||||
if (d > maxd)
|
||||
{
|
||||
maxd = d;
|
||||
maxi = ci;
|
||||
}
|
||||
ci = (ci+cinc) % pn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If the max deviation is larger than accepted error,
|
||||
// add new point, else continue to next segment.
|
||||
if (maxi != -1 && maxd > (maxError*maxError))
|
||||
{
|
||||
// Add space for the new point.
|
||||
//simplified.resize(simplified.Count+4);
|
||||
rccsResizeList(simplified, simplified.Count + 4);
|
||||
int n = simplified.Count/4;
|
||||
for (int j = n-1; j > i; --j)
|
||||
{
|
||||
simplified[j*4+0] = simplified[(j-1)*4+0];
|
||||
simplified[j*4+1] = simplified[(j-1)*4+1];
|
||||
simplified[j*4+2] = simplified[(j-1)*4+2];
|
||||
simplified[j*4+3] = simplified[(j-1)*4+3];
|
||||
}
|
||||
// Add the point.
|
||||
simplified[(i+1)*4+0] = points[maxi*4+0];
|
||||
simplified[(i+1)*4+1] = points[maxi*4+1];
|
||||
simplified[(i+1)*4+2] = points[maxi*4+2];
|
||||
simplified[(i+1)*4+3] = maxi;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Split too long edges.
|
||||
if (maxEdgeLen > 0 && (buildFlags & (int)(rcBuildContoursFlags.RC_CONTOUR_TESS_WALL_EDGES|rcBuildContoursFlags.RC_CONTOUR_TESS_AREA_EDGES)) != 0)
|
||||
{
|
||||
for (int i = 0; i < simplified.Count/4; )
|
||||
{
|
||||
int ii = (i+1) % (simplified.Count/4);
|
||||
|
||||
int ax = simplified[i*4+0];
|
||||
int az = simplified[i*4+2];
|
||||
int ai = simplified[i*4+3];
|
||||
|
||||
int bx = simplified[ii*4+0];
|
||||
int bz = simplified[ii*4+2];
|
||||
int bi = simplified[ii*4+3];
|
||||
|
||||
// Find maximum deviation from the segment.
|
||||
int maxi = -1;
|
||||
int ci = (ai+1) % pn;
|
||||
|
||||
// Tessellate only outer edges or edges between areas.
|
||||
bool tess = false;
|
||||
// Wall edges.
|
||||
if ((buildFlags & (int)rcBuildContoursFlags.RC_CONTOUR_TESS_WALL_EDGES) != 0 && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0)
|
||||
tess = true;
|
||||
// Edges between areas.
|
||||
if ((buildFlags & (int)rcBuildContoursFlags.RC_CONTOUR_TESS_AREA_EDGES) != 0 && (points[ci*4+3] & RC_AREA_BORDER) != 0)
|
||||
tess = true;
|
||||
|
||||
if (tess)
|
||||
{
|
||||
int dx = bx - ax;
|
||||
int dz = bz - az;
|
||||
if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen)
|
||||
{
|
||||
// Round based on the segments in lexilogical order so that the
|
||||
// max tesselation is consistent regardles in which direction
|
||||
// segments are traversed.
|
||||
int n = bi < ai ? (bi+pn - ai) : (bi - ai);
|
||||
if (n > 1)
|
||||
{
|
||||
if (bx > ax || (bx == ax && bz > az))
|
||||
maxi = (ai + n/2) % pn;
|
||||
else
|
||||
maxi = (ai + (n+1)/2) % pn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the max deviation is larger than accepted error,
|
||||
// add new point, else continue to next segment.
|
||||
if (maxi != -1)
|
||||
{
|
||||
// Add space for the new point.
|
||||
rccsResizeList(simplified, simplified.Count + 4);
|
||||
int n = simplified.Count/4;
|
||||
for (int j = n-1; j > i; --j)
|
||||
{
|
||||
simplified[j*4+0] = simplified[(j-1)*4+0];
|
||||
simplified[j*4+1] = simplified[(j-1)*4+1];
|
||||
simplified[j*4+2] = simplified[(j-1)*4+2];
|
||||
simplified[j*4+3] = simplified[(j-1)*4+3];
|
||||
}
|
||||
// Add the point.
|
||||
simplified[(i+1)*4+0] = points[maxi*4+0];
|
||||
simplified[(i+1)*4+1] = points[maxi*4+1];
|
||||
simplified[(i+1)*4+2] = points[maxi*4+2];
|
||||
simplified[(i+1)*4+3] = maxi;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < simplified.Count/4; ++i)
|
||||
{
|
||||
// The edge vertex flag is take from the current raw point,
|
||||
// and the neighbour region is take from the next raw point.
|
||||
int ai = (simplified[i*4+3]+1) % pn;
|
||||
int bi = simplified[i*4+3];
|
||||
simplified[i*4+3] = (points[ai*4+3] & (RC_CONTOUR_REG_MASK|RC_AREA_BORDER)) | (points[bi*4+3] & RC_BORDER_VERTEX);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void removeDegenerateSegments(List<int> simplified)
|
||||
{
|
||||
// Remove adjacent vertices which are equal on xz-plane,
|
||||
// or else the triangulator will get confused.
|
||||
for (int i = 0; i < simplified.Count/4; ++i)
|
||||
{
|
||||
int ni = i+1;
|
||||
if (ni >= (simplified.Count/4))
|
||||
ni = 0;
|
||||
|
||||
if (simplified[i*4+0] == simplified[ni*4+0] &&
|
||||
simplified[i*4+2] == simplified[ni*4+2])
|
||||
{
|
||||
// Degenerate segment, remove.
|
||||
for (int j = i; j < simplified.Count/4-1; ++j)
|
||||
{
|
||||
simplified[j*4+0] = simplified[(j+1)*4+0];
|
||||
simplified[j*4+1] = simplified[(j+1)*4+1];
|
||||
simplified[j*4+2] = simplified[(j+1)*4+2];
|
||||
simplified[j*4+3] = simplified[(j+1)*4+3];
|
||||
}
|
||||
//simplified.Capacity = (simplified.Count-4);
|
||||
rccsResizeList(simplified, simplified.Count - 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int calcAreaOfPolygon2D(int[] verts, int nverts)
|
||||
{
|
||||
int area = 0;
|
||||
for (int i = 0, j = nverts-1; i < nverts; j=i++)
|
||||
{
|
||||
int viStart = i * 4;
|
||||
int vjStart = j * 4;
|
||||
area += verts[viStart + 0] * verts[vjStart + 2] - verts[vjStart + 0] * verts[viStart + 2];
|
||||
}
|
||||
return (area+1) / 2;
|
||||
}
|
||||
|
||||
public static bool ileft(int[] a, int[] b, int[] c)
|
||||
{
|
||||
return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
|
||||
}
|
||||
|
||||
|
||||
public static bool ileft(int[] a,int aStart, int[] b, int bStart, int[] c, int cStart) {
|
||||
return (b[bStart + 0] - a[aStart + 0]) * (c[cStart + 2] - a[aStart + 2]) - (c[cStart + 0] - a[aStart + 0]) * (b[bStart + 2] - a[aStart + 2]) <= 0;
|
||||
}
|
||||
|
||||
public static void getClosestIndices(int[] vertsa, int nvertsa,
|
||||
int[] vertsb, int nvertsb,
|
||||
ref int ia, ref int ib)
|
||||
{
|
||||
int closestDist = 0xfffffff;
|
||||
ia = -1;
|
||||
ib = -1;
|
||||
for (int i = 0; i < nvertsa; ++i)
|
||||
{
|
||||
int i_n = (i+1) % nvertsa;
|
||||
int ip = (i+nvertsa-1) % nvertsa;
|
||||
int vaStart = i * 4;
|
||||
int vanStart = i_n * 4;
|
||||
int vapStart = ip * 4;
|
||||
|
||||
for (int j = 0; j < nvertsb; ++j)
|
||||
{
|
||||
int vbStart = j * 4;
|
||||
// vb must be "infront" of va.
|
||||
if (ileft(vertsa,vapStart,vertsa,vaStart,vertsb,vbStart) && ileft(vertsa,vaStart,vertsa,vanStart,vertsb,vbStart))
|
||||
{
|
||||
int dx = vertsb[vbStart+0] - vertsa[vaStart + 0];
|
||||
int dz = vertsb[vbStart+2] - vertsa[vaStart+2];
|
||||
int d = dx*dx + dz*dz;
|
||||
if (d < closestDist)
|
||||
{
|
||||
ia = i;
|
||||
ib = j;
|
||||
closestDist = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool mergeContours(ref rcContour ca, ref rcContour cb, int ia, int ib)
|
||||
{
|
||||
int maxVerts = ca.nverts + cb.nverts + 2;
|
||||
int[] verts = new int[maxVerts * 4];//(int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
|
||||
if (verts == null)
|
||||
return false;
|
||||
|
||||
int nv = 0;
|
||||
|
||||
// Copy contour A.
|
||||
for (int i = 0; i <= ca.nverts; ++i)
|
||||
{
|
||||
//int* dst = &verts[nv*4];
|
||||
int dstIndex = nv*4;
|
||||
int srcIndex = ((ia+i)%ca.nverts)*4;
|
||||
for (int j=0;i<4;++i){
|
||||
verts[dstIndex + j] = ca.verts[srcIndex + j];
|
||||
}
|
||||
nv++;
|
||||
}
|
||||
|
||||
// Copy contour B
|
||||
for (int i = 0; i <= cb.nverts; ++i)
|
||||
{
|
||||
int dstIndex = nv*4;
|
||||
int srcIndex = ((ib+i)%cb.nverts)*4;
|
||||
//int* dst = &verts[nv*4];
|
||||
//const int* src = &cb.verts[((ib+i)%cb.nverts)*4];
|
||||
for (int j=0;j<4;++j){
|
||||
verts[dstIndex + j] = cb.verts[srcIndex + j];
|
||||
}
|
||||
nv++;
|
||||
}
|
||||
|
||||
ca.verts = verts;
|
||||
ca.nverts = nv;
|
||||
|
||||
cb.verts = null;
|
||||
cb.nverts = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
|
||||
/// parameters control how closely the simplified contours will match the raw contours.
|
||||
///
|
||||
/// Simplified contours are generated such that the vertices for portals between areas match up.
|
||||
/// (They are considered mandatory vertices.)
|
||||
///
|
||||
/// Setting @p maxEdgeLength to zero will disabled the edge length feature.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
|
||||
public static bool rcBuildContours(rcContext ctx, rcCompactHeightfield chf,
|
||||
float maxError, int maxEdgeLen,
|
||||
rcContourSet cset, int buildFlags)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
int w = chf.width;
|
||||
int h = chf.height;
|
||||
int borderSize = chf.borderSize;
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS);
|
||||
|
||||
rcVcopy(cset.bmin, chf.bmin);
|
||||
rcVcopy(cset.bmax, chf.bmax);
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
float pad = borderSize*chf.cs;
|
||||
cset.bmin[0] += pad;
|
||||
cset.bmin[2] += pad;
|
||||
cset.bmax[0] -= pad;
|
||||
cset.bmax[2] -= pad;
|
||||
}
|
||||
cset.cs = chf.cs;
|
||||
cset.ch = chf.ch;
|
||||
cset.width = chf.width - chf.borderSize*2;
|
||||
cset.height = chf.height - chf.borderSize*2;
|
||||
cset.borderSize = chf.borderSize;
|
||||
|
||||
int maxContours = Math.Max((int)chf.maxRegions, 8);
|
||||
//cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
|
||||
cset.conts = new rcContour[maxContours];
|
||||
//if (cset.conts == null)
|
||||
// return false;
|
||||
cset.nconts = 0;
|
||||
|
||||
//rcScopedDelete<byte> flags = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
byte[] flags = new byte[chf.spanCount];
|
||||
if (flags == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' " + chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
// Mark boundaries.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
rcCompactCell c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
byte res = 0;
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
if (chf.spans[i].reg == 0 || (chf.spans[i].reg & RC_BORDER_REG) != 0)
|
||||
{
|
||||
flags[i] = 0;
|
||||
continue;
|
||||
}
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
ushort r = 0;
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = x + rcGetDirOffsetX(dir);
|
||||
int ay = y + rcGetDirOffsetY(dir);
|
||||
int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
r = chf.spans[ai].reg;
|
||||
}
|
||||
if (r == chf.spans[i].reg)
|
||||
res |= (byte)(1 << dir);
|
||||
}
|
||||
flags[i] = (byte)(res ^ 0xf); // Inverse, mark non connected edges.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
//List<int> verts(256);
|
||||
List<int> verts = new List<int>();
|
||||
verts.Capacity = 256;
|
||||
//List<int> simplified(64);
|
||||
List<int> simplified = new List<int>();
|
||||
simplified.Capacity = 64;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
rcCompactCell c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (flags[i] == 0 || flags[i] == 0xf)
|
||||
{
|
||||
flags[i] = 0;
|
||||
continue;
|
||||
}
|
||||
ushort reg = chf.spans[i].reg;
|
||||
if (reg == 0 || (reg & RC_BORDER_REG) != 0) {
|
||||
continue;
|
||||
}
|
||||
byte area = chf.areas[i];
|
||||
|
||||
//verts.resize(0);
|
||||
//simplified.resize(0);
|
||||
verts.Clear();
|
||||
simplified.Clear();
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
walkContour(x, y, i, chf, flags, verts);
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_TRACE);
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
|
||||
removeDegenerateSegments(simplified);
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
|
||||
|
||||
|
||||
// Store region.contour remap info.
|
||||
// Create contour.
|
||||
if (simplified.Count/4 >= 3)
|
||||
{
|
||||
if (cset.nconts >= maxContours)
|
||||
{
|
||||
// Allocate more contours.
|
||||
// This can happen when there are tiny holes in the heightfield.
|
||||
int oldMax = maxContours;
|
||||
maxContours *= 2;
|
||||
rcContour[] newConts = new rcContour[maxContours];// (rcContour*)rcAlloc(sizeof(rcContour) * maxContours, RC_ALLOC_PERM);
|
||||
for (int j = 0; j < cset.nconts; ++j)
|
||||
{
|
||||
newConts[j] = cset.conts[j];
|
||||
// Reset source pointers to prevent data deletion.
|
||||
cset.conts[j].verts = null;
|
||||
cset.conts[j].rverts = null;
|
||||
}
|
||||
//rcFree(cset.conts);
|
||||
cset.conts = newConts;
|
||||
|
||||
ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Expanding max contours from " + oldMax + " to "+ maxContours);
|
||||
}
|
||||
|
||||
int contId = cset.nconts;
|
||||
cset.nconts++;
|
||||
rcContour cont = cset.conts[contId];
|
||||
|
||||
cont.nverts = simplified.Count/4;
|
||||
cont.verts = new int[cont.nverts * 4]; //(int*)rcAlloc(sizeof(int)*cont.nverts*4, RC_ALLOC_PERM);
|
||||
if (cont.verts == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' " + cont.nverts);
|
||||
return false;
|
||||
}
|
||||
//memcpy(cont.verts, &simplified[0], sizeof(int)*cont.nverts*4);
|
||||
for (int j = 0; j < cont.nverts * 4; ++j) {
|
||||
cont.verts[j] = simplified[j];
|
||||
}
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
for (int j = 0; j < cont.nverts; ++j)
|
||||
{
|
||||
//int* v = &cont.verts[j*4];
|
||||
cont.verts[j * 4] -= borderSize;
|
||||
cont.verts[j*4 + 2] -= borderSize;
|
||||
//v[0] -= borderSize;
|
||||
//v[2] -= borderSize;
|
||||
}
|
||||
}
|
||||
|
||||
cont.nrverts = verts.Count/4;
|
||||
cont.rverts = new int[cont.nrverts * 4];//(int*)rcAlloc(sizeof(int)*cont.nrverts*4, RC_ALLOC_PERM);
|
||||
if (cont.rverts == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' " + cont.nrverts);
|
||||
return false;
|
||||
}
|
||||
//memcpy(cont.rverts, &verts[0], sizeof(int)*cont.nrverts*4);
|
||||
for (int j = 0; j < cont.nrverts * 4; ++j) {
|
||||
cont.rverts[j] = verts[j];
|
||||
}
|
||||
if (borderSize > 0)
|
||||
{
|
||||
// If the heightfield was build with bordersize, remove the offset.
|
||||
for (int j = 0; j < cont.nrverts; ++j)
|
||||
{
|
||||
//int* v = &cont.rverts[j*4];
|
||||
cont.rverts[j * 4] -= borderSize;
|
||||
cont.rverts[j * 4 + 2] -= borderSize;
|
||||
}
|
||||
}
|
||||
|
||||
/* cont.cx = cont.cy = cont.cz = 0;
|
||||
for (int i = 0; i < cont.nverts; ++i)
|
||||
{
|
||||
cont.cx += cont.verts[i*4+0];
|
||||
cont.cy += cont.verts[i*4+1];
|
||||
cont.cz += cont.verts[i*4+2];
|
||||
}
|
||||
cont.cx /= cont.nverts;
|
||||
cont.cy /= cont.nverts;
|
||||
cont.cz /= cont.nverts;*/
|
||||
|
||||
cont.reg = reg;
|
||||
cont.area = area;
|
||||
|
||||
cset.conts[contId] = cont;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check and merge droppings.
|
||||
// Sometimes the previous algorithms can fail and create several contours
|
||||
// per area. This pass will try to merge the holes into the main region.
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
rcContour cont = cset.conts[i];
|
||||
// Check if the contour is would backwards.
|
||||
if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
|
||||
{
|
||||
// Find another contour which has the same region ID.
|
||||
int mergeIdx = -1;
|
||||
for (int j = 0; j < cset.nconts; ++j)
|
||||
{
|
||||
if (i == j) continue;
|
||||
if (cset.conts[j].nverts != 0 && cset.conts[j].reg == cont.reg)
|
||||
{
|
||||
// Make sure the polygon is correctly oriented.
|
||||
if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts) != 0)
|
||||
{
|
||||
mergeIdx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mergeIdx == -1)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour " + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
rcContour mcont = cset.conts[mergeIdx];
|
||||
// Merge by closest points.
|
||||
int ia = 0, ib = 0;
|
||||
getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ref ia, ref ib);
|
||||
if (ia == -1 || ib == -1)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for " + i + " and " + mergeIdx);
|
||||
continue;
|
||||
}
|
||||
if (!mergeContours(ref mcont,ref cont, ia, ib))
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_WARNING, "rcBuildContours: Failed to merge contours " + i + " and " + mergeIdx);
|
||||
continue;
|
||||
}
|
||||
cset.conts[mergeIdx] = mcont;
|
||||
cset.conts[i] = cont;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_CONTOURS);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class Recast{
|
||||
/// @par
|
||||
///
|
||||
/// Allows the formation of walkable regions that will flow over low lying
|
||||
/// objects such as curbs, and up structures such as stairways.
|
||||
///
|
||||
/// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
|
||||
///
|
||||
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
|
||||
/// #rcFilterLedgeSpans after calling this filter.
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
public static void rcFilterLowHangingWalkableObstacles(rcContext ctx, int walkableClimb, rcHeightfield solid)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
|
||||
int w = solid.width;
|
||||
int h = solid.height;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
rcSpan ps = null;
|
||||
bool previousWalkable = false;
|
||||
byte previousArea = RC_NULL_AREA;
|
||||
|
||||
for (rcSpan s = solid.spans[x + y*w]; s != null; ps = s, s = s.next)
|
||||
{
|
||||
bool walkable = s.area != RC_NULL_AREA;
|
||||
// If current span is not walkable, but there is walkable
|
||||
// span just below it, mark the span above it walkable too.
|
||||
if (!walkable && previousWalkable)
|
||||
{
|
||||
if (Math.Abs((int)s.smax - (int)ps.smax) <= walkableClimb){
|
||||
s.area = previousArea;
|
||||
}
|
||||
}
|
||||
// Copy walkable flag so that it cannot propagate
|
||||
// past multiple non-walkable objects.
|
||||
previousWalkable = walkable;
|
||||
previousArea = s.area;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
|
||||
/// from the current span's maximum.
|
||||
/// This method removes the impact of the overestimation of conservative voxelization
|
||||
/// so the resulting mesh will not have regions hanging in the air over ledges.
|
||||
///
|
||||
/// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
public static void rcFilterLedgeSpans(rcContext ctx, int walkableHeight, int walkableClimb,
|
||||
rcHeightfield solid)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER);
|
||||
|
||||
int w = solid.width;
|
||||
int h = solid.height;
|
||||
int MAX_HEIGHT = 0xffff;
|
||||
|
||||
// Mark border spans.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next)
|
||||
{
|
||||
// Skip non walkable spans.
|
||||
if (s.area == RC_NULL_AREA){
|
||||
continue;
|
||||
}
|
||||
|
||||
int bot = (int)(s.smax);
|
||||
int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT;
|
||||
|
||||
// Find neighbours minimum height.
|
||||
int minh = MAX_HEIGHT;
|
||||
|
||||
// Min and max height of accessible neighbours.
|
||||
int asmin = s.smax;
|
||||
int asmax = s.smax;
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
int dx = x + rcGetDirOffsetX(dir);
|
||||
int dy = y + rcGetDirOffsetY(dir);
|
||||
// Skip neighbours which are out of bounds.
|
||||
if (dx < 0 || dy < 0 || dx >= w || dy >= h)
|
||||
{
|
||||
minh = Math.Min(minh, -walkableClimb - bot);
|
||||
continue;
|
||||
}
|
||||
|
||||
// From minus infinity to the first span.
|
||||
rcSpan ns = solid.spans[dx + dy*w];
|
||||
int nbot = -walkableClimb;
|
||||
int ntop = ns != null ? (int)ns.smin : MAX_HEIGHT;
|
||||
// Skip neightbour if the gap between the spans is too small.
|
||||
if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight)
|
||||
minh = Math.Min(minh, nbot - bot);
|
||||
|
||||
// Rest of the spans.
|
||||
for (ns = solid.spans[dx + dy*w]; ns != null; ns = ns.next)
|
||||
{
|
||||
nbot = (int)ns.smax;
|
||||
ntop = ns.next != null ? (int)ns.next.smin : MAX_HEIGHT;
|
||||
// Skip neightbour if the gap between the spans is too small.
|
||||
if (Math.Min(top,ntop) - Math.Max(bot,nbot) > walkableHeight)
|
||||
{
|
||||
minh = Math.Min(minh, nbot - bot);
|
||||
|
||||
// Find min/max accessible neighbour height.
|
||||
if (Math.Abs(nbot - bot) <= walkableClimb)
|
||||
{
|
||||
if (nbot < asmin) asmin = nbot;
|
||||
if (nbot > asmax) asmax = nbot;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The current span is close to a ledge if the drop to any
|
||||
// neighbour span is less than the walkableClimb.
|
||||
if (minh < -walkableClimb){
|
||||
s.area = RC_NULL_AREA;
|
||||
}
|
||||
|
||||
// If the difference between all neighbours is too large,
|
||||
// we are at steep slope, mark the span as ledge.
|
||||
if ((asmax - asmin) > walkableClimb)
|
||||
{
|
||||
s.area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_BORDER);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// For this filter, the clearance above the span is the distance from the span's
|
||||
/// maximum to the next higher span's minimum. (Same grid column.)
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
public static void rcFilterWalkableLowHeightSpans(rcContext ctx, int walkableHeight, rcHeightfield solid)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE);
|
||||
|
||||
int w = solid.width;
|
||||
int h = solid.height;
|
||||
int MAX_HEIGHT = 0xffff;
|
||||
|
||||
// Remove walkable flag from spans which do not have enough
|
||||
// space above them for the agent to stand there.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
for (rcSpan s = solid.spans[x + y*w]; s != null; s = s.next)
|
||||
{
|
||||
int bot = (int)(s.smax);
|
||||
int top = s.next != null ? (int)(s.next.smin) : MAX_HEIGHT;
|
||||
if ((top - bot) <= walkableHeight) {
|
||||
s.area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_FILTER_WALKABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class Recast{
|
||||
|
||||
const int RC_MAX_LAYERS = RC_NOT_CONNECTED;
|
||||
const int RC_MAX_NEIS = 16;
|
||||
|
||||
public class rcLayerRegion
|
||||
{
|
||||
public byte[] layers = new byte[RC_MAX_LAYERS];
|
||||
public byte[] neis = new byte[RC_MAX_NEIS];
|
||||
public ushort ymin;
|
||||
public ushort ymax;
|
||||
public byte layerId; // Layer ID
|
||||
public byte nlayers; // Layer count
|
||||
public byte nneis; // Neighbour count
|
||||
public byte baseFlag; // Flag indicating if the region is hte base of merged regions.
|
||||
};
|
||||
|
||||
|
||||
public static void addUnique(byte[] a,ref byte an, byte v)
|
||||
{
|
||||
int n = (int)an;
|
||||
for (int i = 0; i < n; ++i){
|
||||
if (a[i] == v){
|
||||
return;
|
||||
}
|
||||
}
|
||||
a[an] = v;
|
||||
an++;
|
||||
}
|
||||
|
||||
public static bool contains(byte[] a, byte an, byte v)
|
||||
{
|
||||
int n = (int)an;
|
||||
for (int i = 0; i < n; ++i){
|
||||
if (a[i] == v){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool overlapRange( ushort amin, ushort amax,
|
||||
ushort bmin, ushort bmax)
|
||||
{
|
||||
return (amin > bmax || amax < bmin) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class rcLayerSweepSpan
|
||||
{
|
||||
public ushort ns; // number samples
|
||||
public byte id; // region id
|
||||
public byte nei; // neighbour id
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
|
||||
public static bool rcBuildHeightfieldLayers(rcContext ctx, rcCompactHeightfield chf,
|
||||
int borderSize, int walkableHeight,
|
||||
rcHeightfieldLayerSet lset)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
int w = chf.width;
|
||||
int h = chf.height;
|
||||
|
||||
//rcScopedDelete<byte> srcReg = (byte*)rcAlloc(sizeof(byte)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
byte[] srcReg = new byte[chf.spanCount];
|
||||
if (srcReg == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' " + chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
//memset(srcReg,0xff,sizeof(byte)*chf.spanCount);
|
||||
for (int i=0;i<chf.spanCount;++i){
|
||||
srcReg[i] = 0xff;
|
||||
}
|
||||
|
||||
int nsweeps = chf.width;
|
||||
//rcScopedDelete<rcLayerSweepSpan> sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP);
|
||||
rcLayerSweepSpan[] sweeps = new rcLayerSweepSpan[nsweeps];
|
||||
if (sweeps == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' " + nsweeps);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Partition walkable area into monotone regions.
|
||||
int[] prevCount = new int[256];
|
||||
byte regId = 0;
|
||||
|
||||
for (int y = borderSize; y < h-borderSize; ++y)
|
||||
{
|
||||
//memset to 0 is done by C# alloc
|
||||
//memset(prevCount,0,sizeof(int)*regId);
|
||||
|
||||
byte sweepId = 0;
|
||||
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
rcCompactCell c = chf.cells[x+y*w];
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA) continue;
|
||||
|
||||
byte sid = 0xff;
|
||||
|
||||
// -x
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = x + rcGetDirOffsetX(0);
|
||||
int ay = y + rcGetDirOffsetY(0);
|
||||
int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff)
|
||||
sid = srcReg[ai];
|
||||
}
|
||||
|
||||
if (sid == 0xff)
|
||||
{
|
||||
sid = sweepId++;
|
||||
sweeps[sid].nei = (byte)0xff;
|
||||
sweeps[sid].ns = 0;
|
||||
}
|
||||
|
||||
// -y
|
||||
if (rcGetCon(s,3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = x + rcGetDirOffsetX(3);
|
||||
int ay = y + rcGetDirOffsetY(3);
|
||||
int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
byte nr = srcReg[ai];
|
||||
if (nr != 0xff)
|
||||
{
|
||||
// Set neighbour when first valid neighbour is encoutered.
|
||||
if (sweeps[sid].ns == 0)
|
||||
sweeps[sid].nei = nr;
|
||||
|
||||
if (sweeps[sid].nei == nr)
|
||||
{
|
||||
// Update existing neighbour
|
||||
sweeps[sid].ns++;
|
||||
prevCount[nr]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is hit if there is nore than one neighbour.
|
||||
// Invalidate the neighbour.
|
||||
sweeps[sid].nei = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcReg[i] = sid;
|
||||
}
|
||||
}
|
||||
|
||||
// Create unique ID.
|
||||
for (int i = 0; i < sweepId; ++i)
|
||||
{
|
||||
// If the neighbour is set and there is only one continuous connection to it,
|
||||
// the sweep will be merged with the previous one, else new region is created.
|
||||
if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns)
|
||||
{
|
||||
sweeps[i].id = sweeps[i].nei;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (regId == 255)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow.");
|
||||
return false;
|
||||
}
|
||||
sweeps[i].id = regId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remap local sweep ids to region ids.
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
rcCompactCell c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (srcReg[i] != 0xff)
|
||||
srcReg[i] = sweeps[srcReg[i]].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate and init layer regions.
|
||||
int nregs = (int)regId;
|
||||
//rcScopedDelete<rcLayerRegion> regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP);
|
||||
rcLayerRegion[] regs = new rcLayerRegion[nregs];
|
||||
if (regs == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' " + nregs);
|
||||
return false;
|
||||
}
|
||||
//memset(regs, 0, sizeof(rcLayerRegion)*nregs);
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
regs[i].layerId = 0xff;
|
||||
regs[i].ymin = 0xffff;
|
||||
regs[i].ymax = 0;
|
||||
}
|
||||
|
||||
// Find region neighbours and overlapping regions.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
rcCompactCell c = chf.cells[x+y*w];
|
||||
|
||||
byte[] lregs = new byte[RC_MAX_LAYERS];
|
||||
int nlregs = 0;
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan s = chf.spans[i];
|
||||
byte ri = srcReg[i];
|
||||
if (ri == 0xff){
|
||||
continue;
|
||||
}
|
||||
|
||||
regs[ri].ymin = Math.Min(regs[ri].ymin, s.y);
|
||||
regs[ri].ymax = Math.Max(regs[ri].ymax, s.y);
|
||||
|
||||
// Collect all region layers.
|
||||
if (nlregs < RC_MAX_LAYERS)
|
||||
lregs[nlregs++] = ri;
|
||||
|
||||
// Update neighbours
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = x + rcGetDirOffsetX(dir);
|
||||
int ay = y + rcGetDirOffsetY(dir);
|
||||
int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
byte rai = srcReg[ai];
|
||||
if (rai != 0xff && rai != ri){
|
||||
addUnique(regs[ri].neis,ref regs[ri].nneis, rai);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update overlapping regions.
|
||||
for (int i = 0; i < nlregs-1; ++i)
|
||||
{
|
||||
for (int j = i+1; j < nlregs; ++j)
|
||||
{
|
||||
if (lregs[i] != lregs[j])
|
||||
{
|
||||
rcLayerRegion ri = regs[lregs[i]];
|
||||
rcLayerRegion rj = regs[lregs[j]];
|
||||
addUnique(ri.layers,ref ri.nlayers, lregs[j]);
|
||||
addUnique(rj.layers,ref rj.nlayers, lregs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Create 2D layers from regions.
|
||||
byte layerId = 0;
|
||||
|
||||
const int MAX_STACK = 64;
|
||||
byte[] stack = new byte[MAX_STACK];
|
||||
int nstack = 0;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion root = regs[i];
|
||||
// Skip alreadu visited.
|
||||
if (root.layerId != 0xff){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Start search.
|
||||
root.layerId = layerId;
|
||||
root.baseFlag = 1;
|
||||
|
||||
nstack = 0;
|
||||
stack[nstack++] = (byte)i;
|
||||
|
||||
while (nstack != 0)
|
||||
{
|
||||
// Pop front
|
||||
rcLayerRegion reg = regs[stack[0]];
|
||||
nstack--;
|
||||
for (int j = 0; j < nstack; ++j){
|
||||
stack[j] = stack[j+1];
|
||||
}
|
||||
|
||||
int nneis = (int)reg.nneis;
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
byte nei = reg.neis[j];
|
||||
rcLayerRegion regn = regs[nei];
|
||||
// Skip already visited.
|
||||
if (regn.layerId != 0xff){
|
||||
continue;
|
||||
}
|
||||
// Skip if the neighbour is overlapping root region.
|
||||
if (contains(root.layers, root.nlayers, nei)){
|
||||
continue;
|
||||
}
|
||||
// Skip if the height range would become too large.
|
||||
int ymin = Math.Min(root.ymin, regn.ymin);
|
||||
int ymax = Math.Max(root.ymax, regn.ymax);
|
||||
if ((ymax - ymin) >= 255){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nstack < MAX_STACK)
|
||||
{
|
||||
// Deepen
|
||||
stack[nstack++] = (byte)nei;
|
||||
|
||||
// Mark layer id
|
||||
regn.layerId = layerId;
|
||||
// Merge current layers to root.
|
||||
for (int k = 0; k < regn.nlayers; ++k){
|
||||
addUnique(root.layers,ref root.nlayers, regn.layers[k]);
|
||||
}
|
||||
root.ymin = Math.Min(root.ymin, regn.ymin);
|
||||
root.ymax = Math.Max(root.ymax, regn.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layerId++;
|
||||
}
|
||||
|
||||
// Merge non-overlapping regions that are close in height.
|
||||
ushort mergeHeight = (ushort)(walkableHeight * 4);
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion ri = regs[i];
|
||||
if (ri.baseFlag == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
byte newId = ri.layerId;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
byte oldId = 0xff;
|
||||
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (i == j){
|
||||
continue;
|
||||
}
|
||||
rcLayerRegion rj = regs[j];
|
||||
if (rj.baseFlag == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if teh regions are not close to each other.
|
||||
if (!overlapRange(ri.ymin,
|
||||
(ushort)(ri.ymax + mergeHeight),
|
||||
rj.ymin,
|
||||
(ushort)(rj.ymax + mergeHeight))){
|
||||
continue;
|
||||
}
|
||||
// Skip if the height range would become too large.
|
||||
int ymin = Math.Min(ri.ymin, rj.ymin);
|
||||
int ymax = Math.Max(ri.ymax, rj.ymax);
|
||||
if ((ymax - ymin) >= 255){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure that there is no overlap when mergin 'ri' and 'rj'.
|
||||
bool overlap = false;
|
||||
// Iterate over all regions which have the same layerId as 'rj'
|
||||
for (int k = 0; k < nregs; ++k)
|
||||
{
|
||||
if (regs[k].layerId != rj.layerId)
|
||||
continue;
|
||||
// Check if region 'k' is overlapping region 'ri'
|
||||
// Index to 'regs' is the same as region id.
|
||||
if (contains(ri.layers,ri.nlayers, (byte)k))
|
||||
{
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Cannot merge of regions overlap.
|
||||
if (overlap)
|
||||
continue;
|
||||
|
||||
// Can merge i and j.
|
||||
oldId = rj.layerId;
|
||||
break;
|
||||
}
|
||||
|
||||
// Could not find anything to merge with, stop.
|
||||
if (oldId == 0xff)
|
||||
break;
|
||||
|
||||
// Merge
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
rcLayerRegion rj = regs[j];
|
||||
if (rj.layerId == oldId)
|
||||
{
|
||||
rj.baseFlag = 0;
|
||||
// Remap layerIds.
|
||||
rj.layerId = newId;
|
||||
// Add overlaid layers from 'rj' to 'ri'.
|
||||
for (int k = 0; k < rj.nlayers; ++k){
|
||||
addUnique(ri.layers,ref ri.nlayers, rj.layers[k]);
|
||||
}
|
||||
// Update heigh bounds.
|
||||
ri.ymin = Math.Min(ri.ymin, rj.ymin);
|
||||
ri.ymax = Math.Max(ri.ymax, rj.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact layerIds
|
||||
byte[] remap = new byte[256];
|
||||
//memset(remap, 0, 256);
|
||||
|
||||
// Find number of unique layers.
|
||||
layerId = 0;
|
||||
for (int i = 0; i < nregs; ++i){
|
||||
remap[regs[i].layerId] = 1;
|
||||
}
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
if (remap[i] != 0){
|
||||
remap[i] = layerId++;
|
||||
}
|
||||
else{
|
||||
remap[i] = 0xff;
|
||||
}
|
||||
}
|
||||
// Remap ids.
|
||||
for (int i = 0; i < nregs; ++i){
|
||||
regs[i].layerId = remap[regs[i].layerId];
|
||||
}
|
||||
|
||||
// No layers, return empty.
|
||||
if (layerId == 0)
|
||||
{
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create layers.
|
||||
Debug.Assert(lset.layers == null,"Assert lset.layers == 0");
|
||||
|
||||
int lw = w - borderSize*2;
|
||||
int lh = h - borderSize*2;
|
||||
|
||||
// Build contracted bbox for layers.
|
||||
float[] bmin = new float[3];
|
||||
float[] bmax = new float[3];
|
||||
rcVcopy(bmin, chf.bmin);
|
||||
rcVcopy(bmax, chf.bmax);
|
||||
bmin[0] += borderSize*chf.cs;
|
||||
bmin[2] += borderSize*chf.cs;
|
||||
bmax[0] -= borderSize*chf.cs;
|
||||
bmax[2] -= borderSize*chf.cs;
|
||||
|
||||
lset.nlayers = (int)layerId;
|
||||
|
||||
//lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM);
|
||||
lset.layers = new rcHeightfieldLayer[lset.nlayers];
|
||||
if (lset.layers == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' " + lset.nlayers);
|
||||
return false;
|
||||
}
|
||||
//memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers);
|
||||
|
||||
|
||||
// Store layers.
|
||||
for (int i = 0; i < lset.nlayers; ++i)
|
||||
{
|
||||
byte curId = (byte)i;
|
||||
|
||||
// Allocate memory for the current layer.
|
||||
rcHeightfieldLayer layer = lset.layers[i];
|
||||
//memset(layer, 0, sizeof(rcHeightfieldLayer));
|
||||
|
||||
int gridSize = sizeof(byte)*lw*lh;
|
||||
|
||||
layer.heights = new byte[gridSize];//(byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (layer.heights == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' " + gridSize);
|
||||
return false;
|
||||
}
|
||||
//memset(layer.heights, 0xff, gridSize);
|
||||
for (int j=0;j<gridSize;++j){
|
||||
layer.heights[j] = 0xFF;
|
||||
}
|
||||
|
||||
layer.areas = new byte[gridSize];// (byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (layer.areas == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' " + gridSize);
|
||||
return false;
|
||||
}
|
||||
//memset(layer.areas, 0, gridSize);
|
||||
|
||||
layer.cons =new byte[gridSize];// (byte*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (layer.cons == null)
|
||||
{
|
||||
ctx.log(rcLogCategory.RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' " + gridSize);
|
||||
return false;
|
||||
}
|
||||
//memset(layer.cons, 0, gridSize);
|
||||
|
||||
// Find layer height bounds.
|
||||
int hmin = 0, hmax = 0;
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (regs[j].baseFlag != 0 && regs[j].layerId == curId)
|
||||
{
|
||||
hmin = (int)regs[j].ymin;
|
||||
hmax = (int)regs[j].ymax;
|
||||
}
|
||||
}
|
||||
|
||||
layer.width = lw;
|
||||
layer.height = lh;
|
||||
layer.cs = chf.cs;
|
||||
layer.ch = chf.ch;
|
||||
|
||||
// Adjust the bbox to fit the heighfield.
|
||||
rcVcopy(layer.bmin, bmin);
|
||||
rcVcopy(layer.bmax, bmax);
|
||||
layer.bmin[1] = bmin[1] + hmin*chf.ch;
|
||||
layer.bmax[1] = bmin[1] + hmax*chf.ch;
|
||||
layer.hmin = hmin;
|
||||
layer.hmax = hmax;
|
||||
|
||||
// Update usable data region.
|
||||
layer.minx = layer.width;
|
||||
layer.maxx = 0;
|
||||
layer.miny = layer.height;
|
||||
layer.maxy = 0;
|
||||
|
||||
// Copy height and area from compact heighfield.
|
||||
for (int y = 0; y < lh; ++y)
|
||||
{
|
||||
for (int x = 0; x < lw; ++x)
|
||||
{
|
||||
int cx = borderSize+x;
|
||||
int cy = borderSize+y;
|
||||
rcCompactCell c = chf.cells[cx+cy*w];
|
||||
for (int j = (int)c.index, nj = (int)(c.index+c.count); j < nj; ++j)
|
||||
{
|
||||
rcCompactSpan s = chf.spans[j];
|
||||
// Skip unassigned regions.
|
||||
if (srcReg[j] == 0xff){
|
||||
continue;
|
||||
}
|
||||
// Skip of does nto belong to current layer.
|
||||
byte lid = regs[srcReg[j]].layerId;
|
||||
if (lid != curId)
|
||||
continue;
|
||||
|
||||
// Update data bounds.
|
||||
layer.minx = Math.Min(layer.minx, x);
|
||||
layer.maxx = Math.Max(layer.maxx, x);
|
||||
layer.miny = Math.Min(layer.miny, y);
|
||||
layer.maxy = Math.Max(layer.maxy, y);
|
||||
|
||||
// Store height and area type.
|
||||
int idx = x+y*lw;
|
||||
layer.heights[idx] = (byte)(s.y - hmin);
|
||||
layer.areas[idx] = chf.areas[j];
|
||||
|
||||
// Check connection.
|
||||
byte portal = 0;
|
||||
byte con = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
int ax = cx + rcGetDirOffsetX(dir);
|
||||
int ay = cy + rcGetDirOffsetY(dir);
|
||||
int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
byte alid = srcReg[ai] != (byte)0xff ? regs[srcReg[ai]].layerId : (byte)0xff;
|
||||
// Portal mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid != alid)
|
||||
{
|
||||
portal |= (byte)(1<<dir);
|
||||
// Update height so that it matches on both sides of the portal.
|
||||
rcCompactSpan aSpan = chf.spans[ai];
|
||||
if (aSpan.y > hmin)
|
||||
layer.heights[idx] = Math.Max(layer.heights[idx], (byte)(aSpan.y - hmin));
|
||||
}
|
||||
// Valid connection mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid == alid)
|
||||
{
|
||||
int nx = ax - borderSize;
|
||||
int ny = ay - borderSize;
|
||||
if (nx >= 0 && ny >= 0 && nx < lw && ny < lh)
|
||||
con |= (byte)(1<<dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.cons[idx] = (byte)( (portal << 4) | con );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer.minx > layer.maxx)
|
||||
layer.minx = layer.maxx = 0;
|
||||
if (layer.miny > layer.maxy)
|
||||
layer.miny = layer.maxy = 0;
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,430 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
public static partial class Recast{
|
||||
static bool overlapBounds(float[] amin, float[] amax, float[] bmin, float[] bmax)
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
|
||||
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
|
||||
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
static bool overlapInterval(ushort amin, ushort amax,
|
||||
ushort bmin, ushort bmax)
|
||||
{
|
||||
if (amax < bmin) return false;
|
||||
if (amin > bmax) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static rcSpan allocSpan(rcHeightfield hf)
|
||||
{
|
||||
// If running out of memory, allocate new page and update the freelist.
|
||||
if (hf.freelist == null || hf.freelist.next == null)
|
||||
{
|
||||
// Create new page.
|
||||
// Allocate memory for the new pool.
|
||||
//rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
|
||||
rcSpanPool pool = new rcSpanPool();
|
||||
if (pool == null)
|
||||
return null;
|
||||
pool.next = null;
|
||||
// Add the pool into the list of pools.
|
||||
pool.next = hf.pools;
|
||||
hf.pools = pool;
|
||||
// Add new items to the free list.
|
||||
rcSpan freelist = hf.freelist;
|
||||
//rcSpan head = pool.items[0];
|
||||
//rcSpan it = pool.items[RC_SPANS_PER_POOL];
|
||||
int itIndex = RC_SPANS_PER_POOL;
|
||||
do
|
||||
{
|
||||
--itIndex;
|
||||
pool.items[itIndex].next = freelist;
|
||||
freelist = pool.items[itIndex];
|
||||
}
|
||||
while (itIndex != 0);
|
||||
hf.freelist = pool.items[itIndex];
|
||||
}
|
||||
|
||||
// Pop item from in front of the free list.
|
||||
rcSpan it = hf.freelist;
|
||||
hf.freelist = hf.freelist.next;
|
||||
return it;
|
||||
}
|
||||
|
||||
static void freeSpan(rcHeightfield hf, rcSpan ptr)
|
||||
{
|
||||
if (ptr == null) {
|
||||
return;
|
||||
}
|
||||
// Add the node in front of the free list.
|
||||
ptr.next = hf.freelist;
|
||||
hf.freelist = ptr;
|
||||
}
|
||||
|
||||
static void addSpan(rcHeightfield hf, int x, int y,
|
||||
ushort smin, ushort smax,
|
||||
byte area, int flagMergeThr)
|
||||
{
|
||||
|
||||
int idx = x + y*hf.width;
|
||||
|
||||
rcSpan s = allocSpan(hf);
|
||||
s.smin = smin;
|
||||
s.smax = smax;
|
||||
s.area = area;
|
||||
s.next = null;
|
||||
|
||||
// Empty cell, add the first span.
|
||||
if (hf.spans[idx] == null)
|
||||
{
|
||||
hf.spans[idx] = s;
|
||||
return;
|
||||
}
|
||||
rcSpan prev = null;
|
||||
rcSpan cur = hf.spans[idx];
|
||||
|
||||
// Insert and merge spans.
|
||||
while (cur != null)
|
||||
{
|
||||
if (cur.smin > s.smax)
|
||||
{
|
||||
// Current span is further than the new span, break.
|
||||
break;
|
||||
}
|
||||
else if (cur.smax < s.smin)
|
||||
{
|
||||
// Current span is before the new span advance.
|
||||
prev = cur;
|
||||
cur = cur.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Merge spans.
|
||||
if (cur.smin < s.smin)
|
||||
s.smin = cur.smin;
|
||||
if (cur.smax > s.smax)
|
||||
s.smax = cur.smax;
|
||||
|
||||
// Merge flags.
|
||||
if (Math.Abs((int)s.smax - (int)cur.smax) <= flagMergeThr){
|
||||
s.area = Math.Max(s.area, cur.area);
|
||||
}
|
||||
|
||||
// Remove current span.
|
||||
rcSpan next = cur.next;
|
||||
freeSpan(hf, cur);
|
||||
if (prev != null)
|
||||
prev.next = next;
|
||||
else
|
||||
hf.spans[idx] = next;
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new span.
|
||||
if (prev != null)
|
||||
{
|
||||
s.next = prev.next;
|
||||
prev.next = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
s.next = hf.spans[idx];
|
||||
hf.spans[idx] = s;
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The span addition can be set to favor flags. If the span is merged to
|
||||
/// another span and the new @p smax is within @p flagMergeThr units
|
||||
/// from the existing span, the span flags are merged.
|
||||
///
|
||||
/// @see rcHeightfield, rcSpan.
|
||||
static void rcAddSpan(rcContext ctx, rcHeightfield hf, int x, int y,
|
||||
ushort smin, ushort smax,
|
||||
byte area, int flagMergeThr)
|
||||
{
|
||||
// Debug.Assert(ctx != null, "rcContext is null");
|
||||
addSpan(hf, x,y, smin, smax, area, flagMergeThr);
|
||||
}
|
||||
|
||||
// divides a convex polygons into two convex polygons on both sides of a line
|
||||
static void dividePoly(float[] _in, int nin,
|
||||
float[] out1, ref int nout1,
|
||||
float[] out2, ref int nout2,
|
||||
float x, int axis)
|
||||
{
|
||||
float[] d = new float[12];
|
||||
for (int i = 0; i < nin; ++i){
|
||||
d[i] = x - _in[i*3+axis];
|
||||
}
|
||||
|
||||
int m = 0, n = 0;
|
||||
for (int i = 0, j = nin-1; i < nin; j=i, ++i)
|
||||
{
|
||||
bool ina = d[j] >= 0;
|
||||
bool inb = d[i] >= 0;
|
||||
if (ina != inb)
|
||||
{
|
||||
float s = d[j] / (d[j] - d[i]);
|
||||
out1[m*3+0] = _in[j*3+0] + (_in[i*3+0] - _in[j*3+0])*s;
|
||||
out1[m*3+1] = _in[j*3+1] + (_in[i*3+1] - _in[j*3+1])*s;
|
||||
out1[m*3+2] = _in[j*3+2] + (_in[i*3+2] - _in[j*3+2])*s;
|
||||
rcVcopy(out2, n*3, out1, m*3);
|
||||
m++;
|
||||
n++;
|
||||
// add the i'th point to the right polygon. Do NOT add points that are on the dividing line
|
||||
// since these were already added above
|
||||
if (d[i] > 0)
|
||||
{
|
||||
rcVcopy(out1,m*3, _in, i*3);
|
||||
m++;
|
||||
}
|
||||
else if (d[i] < 0)
|
||||
{
|
||||
rcVcopy(out2,n*3, _in, i*3);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else // same side
|
||||
{
|
||||
// add the i'th point to the right polygon. Addition is done even for points on the dividing line
|
||||
if (d[i] >= 0)
|
||||
{
|
||||
rcVcopy(out1, m*3, _in, i*3);
|
||||
m++;
|
||||
if (d[i] != 0)
|
||||
continue;
|
||||
}
|
||||
rcVcopy(out2, n*3, _in, i*3);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
nout1 = m;
|
||||
nout2 = n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void rasterizeTri(float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start,
|
||||
byte area, rcHeightfield hf,
|
||||
float[] bmin, float[] bmax,
|
||||
float cs, float ics, float ich,
|
||||
int flagMergeThr)
|
||||
{
|
||||
int w = hf.width;
|
||||
int h = hf.height;
|
||||
float[] tmin = new float[3];
|
||||
float[] tmax = new float[3];
|
||||
float by = bmax[1] - bmin[1];
|
||||
|
||||
// Calculate the bounding box of the triangle.
|
||||
rcVcopy(tmin, 0, v0, v0Start);
|
||||
rcVcopy(tmax, 0, v0, v0Start);
|
||||
rcVmin(tmin, 0, v1, v1Start);
|
||||
rcVmin(tmin, 0, v2, v2Start);
|
||||
rcVmax(tmax, 0, v1, v1Start);
|
||||
rcVmax(tmax, 0, v2, v2Start);
|
||||
|
||||
// If the triangle does not touch the bbox of the heightfield, skip the triagle.
|
||||
if (!overlapBounds(bmin, bmax, tmin, tmax))
|
||||
return;
|
||||
|
||||
// Calculate the footprint of the triangle on the grid's y-axis
|
||||
int y0 = (int)((tmin[2] - bmin[2])*ics);
|
||||
int y1 = (int)((tmax[2] - bmin[2])*ics);
|
||||
y0 = rcClamp(y0, 0, h-1);
|
||||
y1 = rcClamp(y1, 0, h-1);
|
||||
|
||||
// Clip the triangle into all grid cells it touches.
|
||||
//float[] buf = new float[7*3*4];
|
||||
|
||||
float[] _in = new float[7*3];
|
||||
float[] inrow = new float[7*3];
|
||||
float[] p1 = new float[7*3];
|
||||
float[] p2 = new float[7*3];
|
||||
|
||||
rcVcopy(_in,0 , v0, v0Start);
|
||||
rcVcopy(_in,1*3, v1, v1Start);
|
||||
rcVcopy(_in,2*3, v2, v2Start);
|
||||
|
||||
int nvrow = 0;
|
||||
int nvIn = 3;
|
||||
|
||||
for (int y = y0; y <= y1; ++y)
|
||||
{
|
||||
// Clip polygon to row. Store the remaining polygon as well
|
||||
float cz = bmin[2] + y*cs;
|
||||
dividePoly(_in, nvIn, inrow, ref nvrow, p1, ref nvIn, cz+cs, 2);
|
||||
//rcSwap(_in, p1);
|
||||
float[] tmp = _in;
|
||||
_in = p1;
|
||||
p1 = tmp;
|
||||
|
||||
if (nvrow < 3)
|
||||
continue;
|
||||
|
||||
// find the horizontal bounds in the row
|
||||
float minX = inrow[0], maxX = inrow[0];
|
||||
for (int i=1; i<nvrow; ++i)
|
||||
{
|
||||
if (minX > inrow[i*3]) minX = inrow[i*3];
|
||||
if (maxX < inrow[i*3]) maxX = inrow[i*3];
|
||||
}
|
||||
int x0 = (int)((minX - bmin[0])*ics);
|
||||
int x1 = (int)((maxX - bmin[0])*ics);
|
||||
x0 = rcClamp(x0, 0, w-1);
|
||||
x1 = rcClamp(x1, 0, w-1);
|
||||
|
||||
int nv = 0;
|
||||
int nv2 = nvrow;
|
||||
|
||||
for (int x = x0; x <= x1; ++x)
|
||||
{
|
||||
// Clip polygon to column. store the remaining polygon as well
|
||||
float cx = bmin[0] + x*cs;
|
||||
dividePoly(inrow, nv2, p1, ref nv, p2, ref nv2, cx+cs, 0);
|
||||
//rcSwap(inrow, p2);
|
||||
tmp = inrow;
|
||||
inrow = p2;
|
||||
p2 = tmp;
|
||||
if (nv < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate min and max of the span.
|
||||
float smin = p1[1], smax = p1[1];
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
smin = Math.Min(smin, p1[i*3+1]);
|
||||
smax = Math.Max(smax, p1[i*3+1]);
|
||||
}
|
||||
smin -= bmin[1];
|
||||
smax -= bmin[1];
|
||||
// Skip the span if it is outside the heightfield bbox
|
||||
if (smax < 0.0f) continue;
|
||||
if (smin > by) continue;
|
||||
// Clamp the span to the heightfield bbox.
|
||||
if (smin < 0.0f) smin = 0;
|
||||
if (smax > by) smax = by;
|
||||
|
||||
// Snap the span to the heightfield height grid.
|
||||
ushort ismin = (ushort)rcClamp((int)Math.Floor(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
|
||||
ushort ismax = (ushort)rcClamp((int)Math.Ceiling(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
|
||||
|
||||
addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// No spans will be added if the triangle does not overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
public static void rcRasterizeTriangle(rcContext ctx, float[] v0, int v0Start, float[] v1, int v1Start, float[] v2, int v2Start,
|
||||
byte area, rcHeightfield solid,
|
||||
int flagMergeThr)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
float ics = 1.0f/solid.cs;
|
||||
float ich = 1.0f/solid.ch;
|
||||
rasterizeTri(v0, v0Start, v1, v1Start, v2, v2Start, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv,
|
||||
int[] tris, byte[] areas, int nt,
|
||||
rcHeightfield solid, int flagMergeThr)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
float ics = 1.0f/solid.cs;
|
||||
float ich = 1.0f/solid.ch;
|
||||
// Rasterize triangles.
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
int v0Start = tris[i*3+0]*3;
|
||||
int v1Start = tris[i*3+1]*3;
|
||||
int v2Start = tris[i*3+2]*3;
|
||||
// Rasterize.
|
||||
rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
public static void rcRasterizeTriangles(rcContext ctx, float[] verts, int nv,
|
||||
ushort[] tris, byte[] areas, int nt,
|
||||
rcHeightfield solid, int flagMergeThr)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
float ics = 1.0f/solid.cs;
|
||||
float ich = 1.0f/solid.ch;
|
||||
// Rasterize triangles.
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
int v0Start = tris[i*3+0]*3;
|
||||
int v1Start = tris[i*3+1]*3;
|
||||
int v2Start = tris[i*3+2]*3;
|
||||
|
||||
// Rasterize.
|
||||
rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
public static void rcRasterizeTriangles(rcContext ctx, float[] verts, byte[] areas, int nt,
|
||||
rcHeightfield solid, int flagMergeThr)
|
||||
{
|
||||
Debug.Assert(ctx != null, "rcContext is null");
|
||||
|
||||
ctx.startTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
float ics = 1.0f/solid.cs;
|
||||
float ich = 1.0f/solid.ch;
|
||||
// Rasterize triangles.
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
int v0Start = (i*3+0)*3;
|
||||
int v1Start = (i*3+1)*3;
|
||||
int v2Start = (i*3+2)*3;
|
||||
// Rasterize.
|
||||
rasterizeTri(verts, v0Start, verts, v1Start, verts, v2Start, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx.stopTimer(rcTimerLabel.RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user