| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- /*
- Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
- recast4j copyright (c) 2021 Piotr Piastucki piotr@jtilia.org
- DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- */
- using System;
- using DotRecast.Core;
- namespace DotRecast.Recast
- {
- public static class PolyUtils
- {
- // public static bool PointInPoly(float[] verts, RcVec3f p)
- // {
- // bool c = false;
- // int i, j;
- // for (i = 0, j = verts.Length - 3; i < verts.Length; j = i, i += 3)
- // {
- // int vi = i;
- // int vj = j;
- // if (((verts[vi + 2] > p.z) != (verts[vj + 2] > p.z))
- // && (p.x < (verts[vj] - verts[vi]) * (p.z - verts[vi + 2]) / (verts[vj + 2] - verts[vi + 2])
- // + verts[vi]))
- // c = !c;
- // }
- //
- // return c;
- // }
- // TODO (graham): This is duplicated in the ConvexVolumeTool in RecastDemo
- /// Checks if a point is contained within a polygon
- ///
- /// @param[in] numVerts Number of vertices in the polygon
- /// @param[in] verts The polygon vertices
- /// @param[in] point The point to check
- /// @returns true if the point lies within the polygon, false otherwise.
- public static bool PointInPoly(float[] verts, RcVec3f point)
- {
- bool inPoly = false;
- for (int i = 0, j = verts.Length / 3 - 1; i < verts.Length / 3; j = i++)
- {
- RcVec3f vi = RcVec3f.Of(verts[i * 3], verts[i * 3 + 1], verts[i * 3 + 2]);
- RcVec3f vj = RcVec3f.Of(verts[j * 3], verts[j * 3 + 1], verts[j * 3 + 2]);
- if (vi.z > point.z == vj.z > point.z)
- {
- continue;
- }
- if (point.x >= (vj.x - vi.x) * (point.z - vi.z) / (vj.z - vi.z) + vi.x)
- {
- continue;
- }
- inPoly = !inPoly;
- }
- return inPoly;
- }
- /// Expands a convex polygon along its vertex normals by the given offset amount.
- /// Inserts extra vertices to bevel sharp corners.
- ///
- /// Helper function to offset convex polygons for rcMarkConvexPolyArea.
- ///
- /// @ingroup recast
- ///
- /// @param[in] verts The vertices of the polygon [Form: (x, y, z) * @p numVerts]
- /// @param[in] numVerts The number of vertices in the polygon.
- /// @param[in] offset How much to offset the polygon by. [Units: wu]
- /// @param[out] outVerts The offset vertices (should hold up to 2 * @p numVerts) [Form: (x, y, z) * return value]
- /// @param[in] maxOutVerts The max number of vertices that can be stored to @p outVerts.
- /// @returns Number of vertices in the offset polygon or 0 if too few vertices in @p outVerts.
- public static int OffsetPoly(float[] verts, int numVerts, float offset, float[] outVerts, int maxOutVerts)
- {
- // Defines the limit at which a miter becomes a bevel.
- // Similar in behavior to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit
- const float MITER_LIMIT = 1.20f;
- int numOutVerts = 0;
- for (int vertIndex = 0; vertIndex < numVerts; vertIndex++)
- {
- int vertIndexA = (vertIndex + numVerts - 1) % numVerts;
- int vertIndexB = vertIndex;
- int vertIndexC = (vertIndex + 1) % numVerts;
-
- RcVec3f vertA = RcVec3f.Of(verts, vertIndexA * 3);
- RcVec3f vertB = RcVec3f.Of(verts, vertIndexB * 3);
- RcVec3f vertC = RcVec3f.Of(verts, vertIndexC * 3);
-
- // From A to B on the x/z plane
- RcVec3f prevSegmentDir = vertB.Subtract(vertA);
- prevSegmentDir.y = 0; // Squash onto x/z plane
- prevSegmentDir.SafeNormalize();
-
- // From B to C on the x/z plane
- RcVec3f currSegmentDir = vertC.Subtract(vertB);
- currSegmentDir.y = 0; // Squash onto x/z plane
- currSegmentDir.SafeNormalize();
-
- // The y component of the cross product of the two normalized segment directions.
- // The X and Z components of the cross product are both zero because the two
- // segment direction vectors fall within the x/z plane.
- float cross = currSegmentDir.x * prevSegmentDir.z - prevSegmentDir.x * currSegmentDir.z;
- // CCW perpendicular vector to AB. The segment normal.
- float prevSegmentNormX = -prevSegmentDir.z;
- float prevSegmentNormZ = prevSegmentDir.x;
- // CCW perpendicular vector to BC. The segment normal.
- float currSegmentNormX = -currSegmentDir.z;
- float currSegmentNormZ = currSegmentDir.x;
- // Average the two segment normals to get the proportional miter offset for B.
- // This isn't normalized because it's defining the distance and direction the corner will need to be
- // adjusted proportionally to the edge offsets to properly miter the adjoining edges.
- float cornerMiterX = (prevSegmentNormX + currSegmentNormX) * 0.5f;
- float cornerMiterZ = (prevSegmentNormZ + currSegmentNormZ) * 0.5f;
- float cornerMiterSqMag = RcMath.Sqr(cornerMiterX) + RcMath.Sqr(cornerMiterZ);
-
- // If the magnitude of the segment normal average is less than about .69444,
- // the corner is an acute enough angle that the result should be beveled.
- bool bevel = cornerMiterSqMag * MITER_LIMIT * MITER_LIMIT < 1.0f;
-
- // Scale the corner miter so it's proportional to how much the corner should be offset compared to the edges.
- if (cornerMiterSqMag > RcVec3f.EPSILON)
- {
- float scale = 1.0f / cornerMiterSqMag;
- cornerMiterX *= scale;
- cornerMiterZ *= scale;
- }
-
- if (bevel && cross < 0.0f) // If the corner is convex and an acute enough angle, generate a bevel.
- {
- if (numOutVerts + 2 > maxOutVerts)
- {
- return 0;
- }
- // Generate two bevel vertices at a distances from B proportional to the angle between the two segments.
- // Move each bevel vertex out proportional to the given offset.
- float d = (1.0f - (prevSegmentDir.x * currSegmentDir.x + prevSegmentDir.z * currSegmentDir.z)) * 0.5f;
- outVerts[numOutVerts * 3 + 0] = vertB.x + (-prevSegmentNormX + prevSegmentDir.x * d) * offset;
- outVerts[numOutVerts * 3 + 1] = vertB.y;
- outVerts[numOutVerts * 3 + 2] = vertB.z + (-prevSegmentNormZ + prevSegmentDir.z * d) * offset;
- numOutVerts++;
- outVerts[numOutVerts * 3 + 0] = vertB.x + (-currSegmentNormX - currSegmentDir.x * d) * offset;
- outVerts[numOutVerts * 3 + 1] = vertB.y;
- outVerts[numOutVerts * 3 + 2] = vertB.z + (-currSegmentNormZ - currSegmentDir.z * d) * offset;
- numOutVerts++;
- }
- else
- {
- if (numOutVerts + 1 > maxOutVerts)
- {
- return 0;
- }
- // Move B along the miter direction by the specified offset.
- outVerts[numOutVerts * 3 + 0] = vertB.x - cornerMiterX * offset;
- outVerts[numOutVerts * 3 + 1] = vertB.y;
- outVerts[numOutVerts * 3 + 2] = vertB.z - cornerMiterZ * offset;
- numOutVerts++;
- }
- }
- return numOutVerts;
- }
- }
- }
|