PolyUtils.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /*
  2. Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
  3. recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org
  4. DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
  5. This software is provided 'as-is', without any express or implied
  6. warranty. In no event will the authors be held liable for any damages
  7. arising from the use of this software.
  8. Permission is granted to anyone to use this software for any purpose,
  9. including commercial applications, and to alter it and redistribute it
  10. freely, subject to the following restrictions:
  11. 1. The origin of this software must not be misrepresented; you must not
  12. claim that you wrote the original software. If you use this software
  13. in a product, an acknowledgment in the product documentation would be
  14. appreciated but is not required.
  15. 2. Altered source versions must be plainly marked as such, and must not be
  16. misrepresented as being the original software.
  17. 3. This notice may not be removed or altered from any source distribution.
  18. */
  19. using System;
  20. using DotRecast.Core;
  21. namespace DotRecast.Recast
  22. {
  23. public static class PolyUtils
  24. {
  25. // public static bool PointInPoly(float[] verts, RcVec3f p)
  26. // {
  27. // bool c = false;
  28. // int i, j;
  29. // for (i = 0, j = verts.Length - 3; i < verts.Length; j = i, i += 3)
  30. // {
  31. // int vi = i;
  32. // int vj = j;
  33. // if (((verts[vi + 2] > p.z) != (verts[vj + 2] > p.z))
  34. // && (p.x < (verts[vj] - verts[vi]) * (p.z - verts[vi + 2]) / (verts[vj + 2] - verts[vi + 2])
  35. // + verts[vi]))
  36. // c = !c;
  37. // }
  38. //
  39. // return c;
  40. // }
  41. // TODO (graham): This is duplicated in the ConvexVolumeTool in RecastDemo
  42. /// Checks if a point is contained within a polygon
  43. ///
  44. /// @param[in] numVerts Number of vertices in the polygon
  45. /// @param[in] verts The polygon vertices
  46. /// @param[in] point The point to check
  47. /// @returns true if the point lies within the polygon, false otherwise.
  48. public static bool PointInPoly(float[] verts, RcVec3f point)
  49. {
  50. bool inPoly = false;
  51. for (int i = 0, j = verts.Length / 3 - 1; i < verts.Length / 3; j = i++)
  52. {
  53. RcVec3f vi = RcVec3f.Of(verts[i * 3], verts[i * 3 + 1], verts[i * 3 + 2]);
  54. RcVec3f vj = RcVec3f.Of(verts[j * 3], verts[j * 3 + 1], verts[j * 3 + 2]);
  55. if (vi.z > point.z == vj.z > point.z)
  56. {
  57. continue;
  58. }
  59. if (point.x >= (vj.x - vi.x) * (point.z - vi.z) / (vj.z - vi.z) + vi.x)
  60. {
  61. continue;
  62. }
  63. inPoly = !inPoly;
  64. }
  65. return inPoly;
  66. }
  67. /// Expands a convex polygon along its vertex normals by the given offset amount.
  68. /// Inserts extra vertices to bevel sharp corners.
  69. ///
  70. /// Helper function to offset convex polygons for rcMarkConvexPolyArea.
  71. ///
  72. /// @ingroup recast
  73. ///
  74. /// @param[in] verts The vertices of the polygon [Form: (x, y, z) * @p numVerts]
  75. /// @param[in] numVerts The number of vertices in the polygon.
  76. /// @param[in] offset How much to offset the polygon by. [Units: wu]
  77. /// @param[out] outVerts The offset vertices (should hold up to 2 * @p numVerts) [Form: (x, y, z) * return value]
  78. /// @param[in] maxOutVerts The max number of vertices that can be stored to @p outVerts.
  79. /// @returns Number of vertices in the offset polygon or 0 if too few vertices in @p outVerts.
  80. public static int OffsetPoly(float[] verts, int numVerts, float offset, float[] outVerts, int maxOutVerts)
  81. {
  82. // Defines the limit at which a miter becomes a bevel.
  83. // Similar in behavior to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit
  84. const float MITER_LIMIT = 1.20f;
  85. int numOutVerts = 0;
  86. for (int vertIndex = 0; vertIndex < numVerts; vertIndex++)
  87. {
  88. int vertIndexA = (vertIndex + numVerts - 1) % numVerts;
  89. int vertIndexB = vertIndex;
  90. int vertIndexC = (vertIndex + 1) % numVerts;
  91. RcVec3f vertA = RcVec3f.Of(verts, vertIndexA * 3);
  92. RcVec3f vertB = RcVec3f.Of(verts, vertIndexB * 3);
  93. RcVec3f vertC = RcVec3f.Of(verts, vertIndexC * 3);
  94. // From A to B on the x/z plane
  95. RcVec3f prevSegmentDir = vertB.Subtract(vertA);
  96. prevSegmentDir.y = 0; // Squash onto x/z plane
  97. prevSegmentDir.SafeNormalize();
  98. // From B to C on the x/z plane
  99. RcVec3f currSegmentDir = vertC.Subtract(vertB);
  100. currSegmentDir.y = 0; // Squash onto x/z plane
  101. currSegmentDir.SafeNormalize();
  102. // The y component of the cross product of the two normalized segment directions.
  103. // The X and Z components of the cross product are both zero because the two
  104. // segment direction vectors fall within the x/z plane.
  105. float cross = currSegmentDir.x * prevSegmentDir.z - prevSegmentDir.x * currSegmentDir.z;
  106. // CCW perpendicular vector to AB. The segment normal.
  107. float prevSegmentNormX = -prevSegmentDir.z;
  108. float prevSegmentNormZ = prevSegmentDir.x;
  109. // CCW perpendicular vector to BC. The segment normal.
  110. float currSegmentNormX = -currSegmentDir.z;
  111. float currSegmentNormZ = currSegmentDir.x;
  112. // Average the two segment normals to get the proportional miter offset for B.
  113. // This isn't normalized because it's defining the distance and direction the corner will need to be
  114. // adjusted proportionally to the edge offsets to properly miter the adjoining edges.
  115. float cornerMiterX = (prevSegmentNormX + currSegmentNormX) * 0.5f;
  116. float cornerMiterZ = (prevSegmentNormZ + currSegmentNormZ) * 0.5f;
  117. float cornerMiterSqMag = RcMath.Sqr(cornerMiterX) + RcMath.Sqr(cornerMiterZ);
  118. // If the magnitude of the segment normal average is less than about .69444,
  119. // the corner is an acute enough angle that the result should be beveled.
  120. bool bevel = cornerMiterSqMag * MITER_LIMIT * MITER_LIMIT < 1.0f;
  121. // Scale the corner miter so it's proportional to how much the corner should be offset compared to the edges.
  122. if (cornerMiterSqMag > RcVec3f.EPSILON)
  123. {
  124. float scale = 1.0f / cornerMiterSqMag;
  125. cornerMiterX *= scale;
  126. cornerMiterZ *= scale;
  127. }
  128. if (bevel && cross < 0.0f) // If the corner is convex and an acute enough angle, generate a bevel.
  129. {
  130. if (numOutVerts + 2 > maxOutVerts)
  131. {
  132. return 0;
  133. }
  134. // Generate two bevel vertices at a distances from B proportional to the angle between the two segments.
  135. // Move each bevel vertex out proportional to the given offset.
  136. float d = (1.0f - (prevSegmentDir.x * currSegmentDir.x + prevSegmentDir.z * currSegmentDir.z)) * 0.5f;
  137. outVerts[numOutVerts * 3 + 0] = vertB.x + (-prevSegmentNormX + prevSegmentDir.x * d) * offset;
  138. outVerts[numOutVerts * 3 + 1] = vertB.y;
  139. outVerts[numOutVerts * 3 + 2] = vertB.z + (-prevSegmentNormZ + prevSegmentDir.z * d) * offset;
  140. numOutVerts++;
  141. outVerts[numOutVerts * 3 + 0] = vertB.x + (-currSegmentNormX - currSegmentDir.x * d) * offset;
  142. outVerts[numOutVerts * 3 + 1] = vertB.y;
  143. outVerts[numOutVerts * 3 + 2] = vertB.z + (-currSegmentNormZ - currSegmentDir.z * d) * offset;
  144. numOutVerts++;
  145. }
  146. else
  147. {
  148. if (numOutVerts + 1 > maxOutVerts)
  149. {
  150. return 0;
  151. }
  152. // Move B along the miter direction by the specified offset.
  153. outVerts[numOutVerts * 3 + 0] = vertB.x - cornerMiterX * offset;
  154. outVerts[numOutVerts * 3 + 1] = vertB.y;
  155. outVerts[numOutVerts * 3 + 2] = vertB.z - cornerMiterZ * offset;
  156. numOutVerts++;
  157. }
  158. }
  159. return numOutVerts;
  160. }
  161. }
  162. }