DtUtils.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. using System;
  2. using DotRecast.Core;
  3. namespace DotRecast.Detour
  4. {
  5. public static class DtUtils
  6. {
  7. private static readonly float EQUAL_THRESHOLD = RcMath.Sqr(1.0f / 16384.0f);
  8. public static int NextPow2(int v)
  9. {
  10. v--;
  11. v |= v >> 1;
  12. v |= v >> 2;
  13. v |= v >> 4;
  14. v |= v >> 8;
  15. v |= v >> 16;
  16. v++;
  17. return v;
  18. }
  19. public static int Ilog2(int v)
  20. {
  21. int r;
  22. int shift;
  23. r = (v > 0xffff ? 1 : 0) << 4;
  24. v >>= r;
  25. shift = (v > 0xff ? 1 : 0) << 3;
  26. v >>= shift;
  27. r |= shift;
  28. shift = (v > 0xf ? 1 : 0) << 2;
  29. v >>= shift;
  30. r |= shift;
  31. shift = (v > 0x3 ? 1 : 0) << 1;
  32. v >>= shift;
  33. r |= shift;
  34. r |= (v >> 1);
  35. return r;
  36. }
  37. /// Performs a 'sloppy' colocation check of the specified points.
  38. /// @param[in] p0 A point. [(x, y, z)]
  39. /// @param[in] p1 A point. [(x, y, z)]
  40. /// @return True if the points are considered to be at the same location.
  41. ///
  42. /// Basically, this function will return true if the specified points are
  43. /// close enough to eachother to be considered colocated.
  44. public static bool VEqual(RcVec3f p0, RcVec3f p1)
  45. {
  46. return VEqual(p0, p1, EQUAL_THRESHOLD);
  47. }
  48. public static bool VEqual(RcVec3f p0, RcVec3f p1, float thresholdSqr)
  49. {
  50. float d = RcVec3f.DistSqr(p0, p1);
  51. return d < thresholdSqr;
  52. }
  53. /// Determines if two axis-aligned bounding boxes overlap.
  54. /// @param[in] amin Minimum bounds of box A. [(x, y, z)]
  55. /// @param[in] amax Maximum bounds of box A. [(x, y, z)]
  56. /// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
  57. /// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
  58. /// @return True if the two AABB's overlap.
  59. /// @see dtOverlapBounds
  60. public static bool OverlapQuantBounds(int[] amin, int[] amax, int[] bmin, int[] bmax)
  61. {
  62. bool overlap = true;
  63. overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
  64. overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
  65. overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
  66. return overlap;
  67. }
  68. /// Determines if two axis-aligned bounding boxes overlap.
  69. /// @param[in] amin Minimum bounds of box A. [(x, y, z)]
  70. /// @param[in] amax Maximum bounds of box A. [(x, y, z)]
  71. /// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
  72. /// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
  73. /// @return True if the two AABB's overlap.
  74. /// @see dtOverlapQuantBounds
  75. public static bool OverlapBounds(RcVec3f amin, RcVec3f amax, RcVec3f bmin, RcVec3f bmax)
  76. {
  77. bool overlap = true;
  78. overlap = (amin.x > bmax.x || amax.x < bmin.x) ? false : overlap;
  79. overlap = (amin.y > bmax.y || amax.y < bmin.y) ? false : overlap;
  80. overlap = (amin.z > bmax.z || amax.z < bmin.z) ? false : overlap;
  81. return overlap;
  82. }
  83. public static bool OverlapRange(float amin, float amax, float bmin, float bmax, float eps)
  84. {
  85. return ((amin + eps) > bmax || (amax - eps) < bmin) ? false : true;
  86. }
  87. /// @par
  88. ///
  89. /// All vertices are projected onto the xz-plane, so the y-values are ignored.
  90. public static bool OverlapPolyPoly2D(float[] polya, int npolya, float[] polyb, int npolyb)
  91. {
  92. const float eps = 1e-4f;
  93. for (int i = 0, j = npolya - 1; i < npolya; j = i++)
  94. {
  95. int va = j * 3;
  96. int vb = i * 3;
  97. RcVec3f n = RcVec3f.Of(polya[vb + 2] - polya[va + 2], 0, -(polya[vb + 0] - polya[va + 0]));
  98. RcVec2f aminmax = ProjectPoly(n, polya, npolya);
  99. RcVec2f bminmax = ProjectPoly(n, polyb, npolyb);
  100. if (!OverlapRange(aminmax.x, aminmax.y, bminmax.x, bminmax.y, eps))
  101. {
  102. // Found separating axis
  103. return false;
  104. }
  105. }
  106. for (int i = 0, j = npolyb - 1; i < npolyb; j = i++)
  107. {
  108. int va = j * 3;
  109. int vb = i * 3;
  110. RcVec3f n = RcVec3f.Of(polyb[vb + 2] - polyb[va + 2], 0, -(polyb[vb + 0] - polyb[va + 0]));
  111. RcVec2f aminmax = ProjectPoly(n, polya, npolya);
  112. RcVec2f bminmax = ProjectPoly(n, polyb, npolyb);
  113. if (!OverlapRange(aminmax.x, aminmax.y, bminmax.x, bminmax.y, eps))
  114. {
  115. // Found separating axis
  116. return false;
  117. }
  118. }
  119. return true;
  120. }
  121. /// @}
  122. /// @name Computational geometry helper functions.
  123. /// @{
  124. /// Derives the signed xz-plane area of the triangle ABC, or the
  125. /// relationship of line AB to point C.
  126. /// @param[in] a Vertex A. [(x, y, z)]
  127. /// @param[in] b Vertex B. [(x, y, z)]
  128. /// @param[in] c Vertex C. [(x, y, z)]
  129. /// @return The signed xz-plane area of the triangle.
  130. public static float TriArea2D(float[] verts, int a, int b, int c)
  131. {
  132. float abx = verts[b] - verts[a];
  133. float abz = verts[b + 2] - verts[a + 2];
  134. float acx = verts[c] - verts[a];
  135. float acz = verts[c + 2] - verts[a + 2];
  136. return acx * abz - abx * acz;
  137. }
  138. public static float TriArea2D(RcVec3f a, RcVec3f b, RcVec3f c)
  139. {
  140. float abx = b.x - a.x;
  141. float abz = b.z - a.z;
  142. float acx = c.x - a.x;
  143. float acz = c.z - a.z;
  144. return acx * abz - abx * acz;
  145. }
  146. // Returns a random point in a convex polygon.
  147. // Adapted from Graphics Gems article.
  148. public static RcVec3f RandomPointInConvexPoly(float[] pts, int npts, float[] areas, float s, float t)
  149. {
  150. // Calc triangle araes
  151. float areasum = 0.0f;
  152. for (int i = 2; i < npts; i++)
  153. {
  154. areas[i] = TriArea2D(pts, 0, (i - 1) * 3, i * 3);
  155. areasum += Math.Max(0.001f, areas[i]);
  156. }
  157. // Find sub triangle weighted by area.
  158. float thr = s * areasum;
  159. float acc = 0.0f;
  160. float u = 1.0f;
  161. int tri = npts - 1;
  162. for (int i = 2; i < npts; i++)
  163. {
  164. float dacc = areas[i];
  165. if (thr >= acc && thr < (acc + dacc))
  166. {
  167. u = (thr - acc) / dacc;
  168. tri = i;
  169. break;
  170. }
  171. acc += dacc;
  172. }
  173. float v = (float)Math.Sqrt(t);
  174. float a = 1 - v;
  175. float b = (1 - u) * v;
  176. float c = u * v;
  177. int pa = 0;
  178. int pb = (tri - 1) * 3;
  179. int pc = tri * 3;
  180. return new RcVec3f()
  181. {
  182. x = a * pts[pa] + b * pts[pb] + c * pts[pc],
  183. y = a * pts[pa + 1] + b * pts[pb + 1] + c * pts[pc + 1],
  184. z = a * pts[pa + 2] + b * pts[pb + 2] + c * pts[pc + 2]
  185. };
  186. }
  187. public static bool ClosestHeightPointTriangle(RcVec3f p, RcVec3f a, RcVec3f b, RcVec3f c, out float h)
  188. {
  189. const float EPS = 1e-6f;
  190. h = 0;
  191. RcVec3f v0 = c.Subtract(a);
  192. RcVec3f v1 = b.Subtract(a);
  193. RcVec3f v2 = p.Subtract(a);
  194. // Compute scaled barycentric coordinates
  195. float denom = v0.x * v1.z - v0.z * v1.x;
  196. if (Math.Abs(denom) < EPS)
  197. {
  198. return false;
  199. }
  200. float u = v1.z * v2.x - v1.x * v2.z;
  201. float v = v0.x * v2.z - v0.z * v2.x;
  202. if (denom < 0)
  203. {
  204. denom = -denom;
  205. u = -u;
  206. v = -v;
  207. }
  208. // If point lies inside the triangle, return interpolated ycoord.
  209. if (u >= 0.0f && v >= 0.0f && (u + v) <= denom)
  210. {
  211. h = a.y + (v0.y * u + v1.y * v) / denom;
  212. return true;
  213. }
  214. return false;
  215. }
  216. public static RcVec2f ProjectPoly(RcVec3f axis, float[] poly, int npoly)
  217. {
  218. float rmin, rmax;
  219. rmin = rmax = axis.Dot2D(poly, 0);
  220. for (int i = 1; i < npoly; ++i)
  221. {
  222. float d = axis.Dot2D(poly, i * 3);
  223. rmin = Math.Min(rmin, d);
  224. rmax = Math.Max(rmax, d);
  225. }
  226. return new RcVec2f
  227. {
  228. x = rmin,
  229. y = rmax,
  230. };
  231. }
  232. /// @par
  233. ///
  234. /// All points are projected onto the xz-plane, so the y-values are ignored.
  235. public static bool PointInPolygon(RcVec3f pt, float[] verts, int nverts)
  236. {
  237. // TODO: Replace pnpoly with triArea2D tests?
  238. int i, j;
  239. bool c = false;
  240. for (i = 0, j = nverts - 1; i < nverts; j = i++)
  241. {
  242. int vi = i * 3;
  243. int vj = j * 3;
  244. if (((verts[vi + 2] > pt.z) != (verts[vj + 2] > pt.z)) && (pt.x < (verts[vj + 0] - verts[vi + 0])
  245. * (pt.z - verts[vi + 2]) / (verts[vj + 2] - verts[vi + 2]) + verts[vi + 0]))
  246. {
  247. c = !c;
  248. }
  249. }
  250. return c;
  251. }
  252. public static bool DistancePtPolyEdgesSqr(RcVec3f pt, float[] verts, int nverts, float[] ed, float[] et)
  253. {
  254. // TODO: Replace pnpoly with triArea2D tests?
  255. int i, j;
  256. bool c = false;
  257. for (i = 0, j = nverts - 1; i < nverts; j = i++)
  258. {
  259. int vi = i * 3;
  260. int vj = j * 3;
  261. if (((verts[vi + 2] > pt.z) != (verts[vj + 2] > pt.z)) &&
  262. (pt.x < (verts[vj + 0] - verts[vi + 0]) * (pt.z - verts[vi + 2]) / (verts[vj + 2] - verts[vi + 2]) + verts[vi + 0]))
  263. {
  264. c = !c;
  265. }
  266. ed[j] = DistancePtSegSqr2D(pt, verts, vj, vi, out et[j]);
  267. }
  268. return c;
  269. }
  270. public static float DistancePtSegSqr2D(RcVec3f pt, float[] verts, int p, int q, out float t)
  271. {
  272. var vp = RcVec3f.Of(verts, p);
  273. var vq = RcVec3f.Of(verts, q);
  274. return DistancePtSegSqr2D(pt, vp, vq, out t);
  275. }
  276. public static float DistancePtSegSqr2D(RcVec3f pt, RcVec3f p, RcVec3f q, out float t)
  277. {
  278. float pqx = q.x - p.x;
  279. float pqz = q.z - p.z;
  280. float dx = pt.x - p.x;
  281. float dz = pt.z - p.z;
  282. float d = pqx * pqx + pqz * pqz;
  283. t = pqx * dx + pqz * dz;
  284. if (d > 0)
  285. {
  286. t /= d;
  287. }
  288. if (t < 0)
  289. {
  290. t = 0;
  291. }
  292. else if (t > 1)
  293. {
  294. t = 1;
  295. }
  296. dx = p.x + t * pqx - pt.x;
  297. dz = p.z + t * pqz - pt.z;
  298. return dx * dx + dz * dz;
  299. }
  300. public static bool IntersectSegmentPoly2D(RcVec3f p0, RcVec3f p1,
  301. RcVec3f[] verts, int nverts,
  302. out float tmin, out float tmax,
  303. out int segMin, out int segMax)
  304. {
  305. const float EPS = 0.000001f;
  306. tmin = 0;
  307. tmax = 1;
  308. segMin = -1;
  309. segMax = -1;
  310. var dir = p1.Subtract(p0);
  311. var p0v = p0;
  312. for (int i = 0, j = nverts - 1; i < nverts; j = i++)
  313. {
  314. RcVec3f vpj = verts[j];
  315. RcVec3f vpi = verts[i];
  316. var edge = vpi.Subtract(vpj);
  317. var diff = p0v.Subtract(vpj);
  318. float n = RcVec3f.Perp2D(edge, diff);
  319. float d = RcVec3f.Perp2D(dir, edge);
  320. if (Math.Abs(d) < EPS)
  321. {
  322. // S is nearly parallel to this edge
  323. if (n < 0)
  324. {
  325. return false;
  326. }
  327. else
  328. {
  329. continue;
  330. }
  331. }
  332. float t = n / d;
  333. if (d < 0)
  334. {
  335. // segment S is entering across this edge
  336. if (t > tmin)
  337. {
  338. tmin = t;
  339. segMin = j;
  340. // S enters after leaving polygon
  341. if (tmin > tmax)
  342. {
  343. return false;
  344. }
  345. }
  346. }
  347. else
  348. {
  349. // segment S is leaving across this edge
  350. if (t < tmax)
  351. {
  352. tmax = t;
  353. segMax = j;
  354. // S leaves before entering polygon
  355. if (tmax < tmin)
  356. {
  357. return false;
  358. }
  359. }
  360. }
  361. }
  362. return true;
  363. }
  364. public static int OppositeTile(int side)
  365. {
  366. return (side + 4) & 0x7;
  367. }
  368. public static bool IntersectSegSeg2D(RcVec3f ap, RcVec3f aq, RcVec3f bp, RcVec3f bq, out float s, out float t)
  369. {
  370. s = 0;
  371. t = 0;
  372. RcVec3f u = aq.Subtract(ap);
  373. RcVec3f v = bq.Subtract(bp);
  374. RcVec3f w = ap.Subtract(bp);
  375. float d = RcVec3f.PerpXZ(u, v);
  376. if (Math.Abs(d) < 1e-6f)
  377. {
  378. return false;
  379. }
  380. s = RcVec3f.PerpXZ(v, w) / d;
  381. t = RcVec3f.PerpXZ(u, w) / d;
  382. return true;
  383. }
  384. }
  385. }