// Copyright (c) 2014-2015 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE using System; using SharpNav.Geometry; using SharpNav.Pathfinding; #if MONOGAME using Vector3 = Microsoft.Xna.Framework.Vector3; #elif OPENTK using Vector3 = OpenTK.Vector3; #elif SHARPDX using Vector3 = SharpDX.Vector3; #endif namespace SharpNav.Crowds { /// /// The LocalBoundary class stores segments and polygon indices for temporary use. /// public class LocalBoundary { #region Fields private const int MaxLocalSegs = 8; private const int MaxLocalPolys = 16; private Vector3 center; private Segment[] segs; private int segCount; private int[] polys; private int numPolys; #endregion #region Constructors /// /// Initializes a new instance of the class. /// public LocalBoundary() { Reset(); segs = new Segment[MaxLocalSegs]; polys = new int[MaxLocalPolys]; } #endregion #region Properties /// /// Gets the center /// public Vector3 Center { get { return center; } } /// /// Gets the segments /// public Segment[] Segs { get { return segs; } } /// /// Gets the number of segments /// public int SegCount { get { return segCount; } } #endregion #region Methods /// /// Reset all the internal data /// public void Reset() { center = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); segCount = 0; numPolys = 0; } /// /// Add a line segment /// /// The distance /// The line segment public void AddSegment(float dist, Segment s) { //insert neighbour based on distance int segPos = 0; if (segCount == 0) { segPos = 0; } else if (dist >= segs[segCount - 1].Dist) { //further than the last segment, skip if (segCount >= MaxLocalSegs) return; //last, trivial accept segPos = segCount; } else { //insert inbetween int i; for (i = 0; i < segCount; i++) if (dist <= segs[i].Dist) break; int tgt = i + 1; int n = Math.Min(segCount - i, MaxLocalSegs - tgt); if (n > 0) { for (int j = 0; j < n; j++) segs[tgt + j] = segs[i + j]; } segPos = i; } segs[segPos].Dist = dist; segs[segPos].Start = s.Start; segs[segPos].End = s.End; if (segCount < MaxLocalSegs) segCount++; } /// /// Examine polygons in the NavMeshQuery and add polygon edges /// /// The starting polygon reference /// Current position /// Range to query /// The NavMeshQuery public void Update(int reference, Vector3 pos, float collisionQueryRange, NavMeshQuery navquery) { const int MAX_SEGS_PER_POLY = PathfindingCommon.VERTS_PER_POLYGON; if (reference == 0) { this.center = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); this.segCount = 0; this.numPolys = 0; return; } this.center = pos; //first query non-overlapping polygons int[] tempArray = new int[polys.Length]; navquery.FindLocalNeighbourhood(new NavPoint(reference, pos), collisionQueryRange, polys, tempArray, ref numPolys, MaxLocalPolys); //secondly, store all polygon edges this.segCount = 0; Segment[] segs = new Segment[MAX_SEGS_PER_POLY]; int numSegs = 0; for (int j = 0; j < numPolys; j++) { tempArray = new int[segs.Length]; navquery.GetPolyWallSegments(polys[j], segs, tempArray, ref numSegs, MAX_SEGS_PER_POLY); for (int k = 0; k < numSegs; k++) { //skip too distant segments float tseg; float distSqr = Distance.PointToSegment2DSquared(ref pos, ref segs[k].Start, ref segs[k].End, out tseg); if (distSqr > collisionQueryRange * collisionQueryRange) continue; AddSegment(distSqr, segs[k]); } } } /// /// Determines whether the polygon reference is a part of the NavMeshQuery. /// /// The NavMeshQuery /// True if valid, false if not public bool IsValid(NavMeshQuery navquery) { if (numPolys == 0) return false; for (int i = 0; i < numPolys; i++) { if (!navquery.IsValidPolyRef(polys[i])) return false; } return true; } #endregion /// /// A line segment contains two points /// public struct Segment { /// /// Start and end points /// public Vector3 Start, End; /// /// Distance for pruning /// public float Dist; } } }