| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- using System;
- using DotRecast.Core;
- namespace DotRecast.Detour
- {
- public static class DtUtils
- {
- private static readonly float EQUAL_THRESHOLD = RcMath.Sqr(1.0f / 16384.0f);
- public static int NextPow2(int v)
- {
- v--;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- v++;
- return v;
- }
- public static int Ilog2(int v)
- {
- 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 |= (v >> 1);
- return r;
- }
- /// 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 VEqual(RcVec3f p0, RcVec3f p1)
- {
- return VEqual(p0, p1, EQUAL_THRESHOLD);
- }
- public static bool VEqual(RcVec3f p0, RcVec3f p1, float thresholdSqr)
- {
- float d = RcVec3f.DistSqr(p0, p1);
- return d < thresholdSqr;
- }
- /// 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 OverlapQuantBounds(int[] amin, int[] amax, int[] bmin, int[] 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 OverlapBounds(RcVec3f amin, RcVec3f amax, RcVec3f bmin, RcVec3f bmax)
- {
- bool overlap = true;
- overlap = (amin.x > bmax.x || amax.x < bmin.x) ? false : overlap;
- overlap = (amin.y > bmax.y || amax.y < bmin.y) ? false : overlap;
- overlap = (amin.z > bmax.z || amax.z < bmin.z) ? false : overlap;
- return overlap;
- }
- public static bool OverlapRange(float amin, float amax, float bmin, float bmax, float eps)
- {
- return ((amin + eps) > bmax || (amax - eps) < bmin) ? false : true;
- }
- /// @par
- ///
- /// All vertices are projected onto the xz-plane, so the y-values are ignored.
- public static bool OverlapPolyPoly2D(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 va = j * 3;
- int vb = i * 3;
- RcVec3f n = RcVec3f.Of(polya[vb + 2] - polya[va + 2], 0, -(polya[vb + 0] - polya[va + 0]));
- RcVec2f aminmax = ProjectPoly(n, polya, npolya);
- RcVec2f bminmax = ProjectPoly(n, polyb, npolyb);
- if (!OverlapRange(aminmax.x, aminmax.y, bminmax.x, bminmax.y, eps))
- {
- // Found separating axis
- return false;
- }
- }
- for (int i = 0, j = npolyb - 1; i < npolyb; j = i++)
- {
- int va = j * 3;
- int vb = i * 3;
- RcVec3f n = RcVec3f.Of(polyb[vb + 2] - polyb[va + 2], 0, -(polyb[vb + 0] - polyb[va + 0]));
- RcVec2f aminmax = ProjectPoly(n, polya, npolya);
- RcVec2f bminmax = ProjectPoly(n, polyb, npolyb);
- if (!OverlapRange(aminmax.x, aminmax.y, bminmax.x, bminmax.y, eps))
- {
- // Found separating axis
- return false;
- }
- }
- return true;
- }
- /// @}
- /// @name Computational geometry helper functions.
- /// @{
- /// 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 TriArea2D(float[] verts, int a, int b, int c)
- {
- float abx = verts[b] - verts[a];
- float abz = verts[b + 2] - verts[a + 2];
- float acx = verts[c] - verts[a];
- float acz = verts[c + 2] - verts[a + 2];
- return acx * abz - abx * acz;
- }
- public static float TriArea2D(RcVec3f a, RcVec3f b, RcVec3f c)
- {
- float abx = b.x - a.x;
- float abz = b.z - a.z;
- float acx = c.x - a.x;
- float acz = c.z - a.z;
- return acx * abz - abx * acz;
- }
- // Returns a random point in a convex polygon.
- // Adapted from Graphics Gems article.
- public static RcVec3f RandomPointInConvexPoly(float[] pts, int npts, float[] areas, float s, float t)
- {
- // Calc triangle araes
- float areasum = 0.0f;
- for (int i = 2; i < npts; i++)
- {
- areas[i] = TriArea2D(pts, 0, (i - 1) * 3, 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 = 1.0f;
- int tri = npts - 1;
- 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 pa = 0;
- int pb = (tri - 1) * 3;
- int pc = tri * 3;
- return new RcVec3f()
- {
- x = a * pts[pa] + b * pts[pb] + c * pts[pc],
- y = a * pts[pa + 1] + b * pts[pb + 1] + c * pts[pc + 1],
- z = a * pts[pa + 2] + b * pts[pb + 2] + c * pts[pc + 2]
- };
- }
- public static bool ClosestHeightPointTriangle(RcVec3f p, RcVec3f a, RcVec3f b, RcVec3f c, out float h)
- {
- const float EPS = 1e-6f;
- h = 0;
- RcVec3f v0 = c.Subtract(a);
- RcVec3f v1 = b.Subtract(a);
- RcVec3f v2 = p.Subtract(a);
- // Compute scaled barycentric coordinates
- float denom = v0.x * v1.z - v0.z * v1.x;
- if (Math.Abs(denom) < EPS)
- {
- return false;
- }
- float u = v1.z * v2.x - v1.x * v2.z;
- float v = v0.x * v2.z - v0.z * v2.x;
- if (denom < 0)
- {
- denom = -denom;
- u = -u;
- v = -v;
- }
- // If point lies inside the triangle, return interpolated ycoord.
- if (u >= 0.0f && v >= 0.0f && (u + v) <= denom)
- {
- h = a.y + (v0.y * u + v1.y * v) / denom;
- return true;
- }
- return false;
- }
- public static RcVec2f ProjectPoly(RcVec3f axis, float[] poly, int npoly)
- {
- float rmin, rmax;
- rmin = rmax = axis.Dot2D(poly, 0);
- for (int i = 1; i < npoly; ++i)
- {
- float d = axis.Dot2D(poly, i * 3);
- rmin = Math.Min(rmin, d);
- rmax = Math.Max(rmax, d);
- }
- return new RcVec2f
- {
- x = rmin,
- y = rmax,
- };
- }
- /// @par
- ///
- /// All points are projected onto the xz-plane, so the y-values are ignored.
- public static bool PointInPolygon(RcVec3f 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 vi = i * 3;
- int vj = j * 3;
- if (((verts[vi + 2] > pt.z) != (verts[vj + 2] > pt.z)) && (pt.x < (verts[vj + 0] - verts[vi + 0])
- * (pt.z - verts[vi + 2]) / (verts[vj + 2] - verts[vi + 2]) + verts[vi + 0]))
- {
- c = !c;
- }
- }
- return c;
- }
- public static bool DistancePtPolyEdgesSqr(RcVec3f pt, float[] verts, 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 (((verts[vi + 2] > pt.z) != (verts[vj + 2] > pt.z)) &&
- (pt.x < (verts[vj + 0] - verts[vi + 0]) * (pt.z - verts[vi + 2]) / (verts[vj + 2] - verts[vi + 2]) + verts[vi + 0]))
- {
- c = !c;
- }
- ed[j] = DistancePtSegSqr2D(pt, verts, vj, vi, out et[j]);
- }
- return c;
- }
- public static float DistancePtSegSqr2D(RcVec3f pt, float[] verts, int p, int q, out float t)
- {
- var vp = RcVec3f.Of(verts, p);
- var vq = RcVec3f.Of(verts, q);
- return DistancePtSegSqr2D(pt, vp, vq, out t);
- }
- public static float DistancePtSegSqr2D(RcVec3f pt, RcVec3f p, RcVec3f q, out float t)
- {
- float pqx = q.x - p.x;
- float pqz = q.z - p.z;
- float dx = pt.x - p.x;
- float dz = pt.z - p.z;
- 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.x + t * pqx - pt.x;
- dz = p.z + t * pqz - pt.z;
- return dx * dx + dz * dz;
- }
- public static bool IntersectSegmentPoly2D(RcVec3f p0, RcVec3f p1,
- RcVec3f[] verts, int nverts,
- out float tmin, out float tmax,
- out int segMin, out int segMax)
- {
- const float EPS = 0.000001f;
- tmin = 0;
- tmax = 1;
- segMin = -1;
- segMax = -1;
- var dir = p1.Subtract(p0);
- var p0v = p0;
- for (int i = 0, j = nverts - 1; i < nverts; j = i++)
- {
- RcVec3f vpj = verts[j];
- RcVec3f vpi = verts[i];
- var edge = vpi.Subtract(vpj);
- var diff = p0v.Subtract(vpj);
- float n = RcVec3f.Perp2D(edge, diff);
- float d = RcVec3f.Perp2D(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 int OppositeTile(int side)
- {
- return (side + 4) & 0x7;
- }
- public static bool IntersectSegSeg2D(RcVec3f ap, RcVec3f aq, RcVec3f bp, RcVec3f bq, out float s, out float t)
- {
- s = 0;
- t = 0;
- RcVec3f u = aq.Subtract(ap);
- RcVec3f v = bq.Subtract(bp);
- RcVec3f w = ap.Subtract(bp);
- float d = RcVec3f.PerpXZ(u, v);
- if (Math.Abs(d) < 1e-6f)
- {
- return false;
- }
- s = RcVec3f.PerpXZ(v, w) / d;
- t = RcVec3f.PerpXZ(u, w) / d;
- return true;
- }
- }
- }
|