/// <summary> /// Voxelizes the triangles from the provided <see cref="ChunkyTriMesh"/> into the /// heightfield. /// </summary> /// <remarks> /// <para> /// The chunks that are voxelized is controled by the bounds parameters. /// </para> /// </remarks> /// <param name="context">The build context.</param> /// <param name="mesh">The mesh.</param> /// <param name="boundsMin">The minimum bounds for the mesh query.</param> /// <param name="boundsMax">The maximum bounds for the mesh query.</param> /// <param name="flagMergeThreshold"> /// The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Normal: 1] /// </param> /// <returns>True if the operation was successful.</returns> public bool AddTriangles(BuildContext context, ChunkyTriMesh mesh , Vector3 boundsMin, Vector3 boundsMax , int flagMergeThreshold) { if (IsDisposed || mesh == null || mesh.IsDisposed) { return(false); } List <ChunkyTriMeshNode> nodeList = new List <ChunkyTriMeshNode>(); int triCount = mesh.GetChunks(boundsMin.x, boundsMin.z , boundsMax.x, boundsMax.z , nodeList); if (triCount == 0) { return(true); } return(HeightfieldEx.nmhfRasterizeNodes(context.root , mesh.verts , mesh.tris , mesh.areas , nodeList.ToArray() , nodeList.Count , root , flagMergeThreshold)); }
/// <summary> /// Creates a new heightfield object. /// </summary> /// <param name="width">The width of the field. [Limit: >= 1] [Units: Cells]</param> /// <param name="depth">The depth of the field. [Limit: >= 1] [Units: Cells]</param> /// <param name="boundsMin">The minimum bounds of the field's AABB. [Units: World]</param> /// <param name="boundsMax">The maximum bounds of the field's AABB. [Units: World]</param> /// <param name="xzCellSize"> /// The xz-plane cell size. [Limit:>= <see cref="NMGen.MinCellSize"/>] [Units: World] /// </param> /// <param name="yCellSize"> /// The y-axis span increments. [Limit:>= <see cref="NMGen.MinCellSize"/>] [Units: World] /// </param> /// <returns>The heightfield, or null on error.</returns> public static Heightfield Create(int width, int depth , Vector3 boundsMin, Vector3 boundsMax , float xzCellSize, float yCellSize) { if (width < 1 || depth < 1 || !TriangleMesh.IsBoundsValid(boundsMin, boundsMax) || xzCellSize < NMGen.MinCellSize || yCellSize < NMGen.MinCellSize) { return(null); } IntPtr root = HeightfieldEx.nmhfAllocField(width, depth , ref boundsMin, ref boundsMax, xzCellSize, yCellSize); if (root == IntPtr.Zero) { return(null); } return(new Heightfield(root , width, depth , boundsMin, boundsMax , xzCellSize, yCellSize)); }
private static float[] GetVector(Vector3 vector, float[] buffer) { buffer[0] = vector.x; buffer[1] = vector.y; buffer[2] = vector.z; return(buffer); }
private AreaBoxMarker(string name, int priority, byte area , Vector3 boundsMin, Vector3 boundsMax) : base(name, priority, area) { mBoundsMin = boundsMin; mBoundsMax = boundsMax; }
/// <summary> /// Moves over an off-mesh connection. /// </summary> /// <remarks> /// <para> /// This method is minimally tested and documented. /// </para> /// </remarks> /// <param name="connectionRef">The connection polygon reference.</param> /// <param name="endpointRefs">Polygon endpoint references. [Length: 2]</param> /// <param name="startPosition">The start position.</param> /// <param name="endPosition">The end position.</param> /// <returns>True if the operation succeeded.</returns> public bool MoveOverConnection(uint connectionRef, uint[] endpointRefs , Vector3 startPosition, Vector3 endPosition) { return(PathCorridorEx.dtpcMoveOverOffmeshConnection(mRoot , connectionRef, endpointRefs, ref startPosition, ref endPosition, ref mPosition , mQuery.root)); }
public static extern NavStatus dtqQueryPolygons(IntPtr query , ref Vector3 position , ref Vector3 extents , IntPtr filter , [In, Out] uint[] resultPolyRefs , ref int resultCount , int maxResult);
/// <summary> /// Tests that vector interop behaves as expected. /// </summary> /// <remarks> /// <para> /// If the test is successful the input and return vectors will be equal in value. /// </para> /// <para> /// This method is used to validate that builds using custom vectors behaves correctly /// with interop. (I.e. The custom vector is data compatible.) /// </para> /// </remarks> /// <param name="v">The input vector.</param> /// <returns> /// A vector equal to the input vector if interop is functioning as expected. /// </returns> public static Vector3 TestVector(Vector3 v) { Vector3 result = new Vector3(); rcn.InteropUtil.dtvlVectorTest(ref v, ref result); return(result); }
public static extern bool dtpcMoveOverOffmeshConnection(IntPtr corridor , uint offMeshConRef , [In, Out] uint[] refs // size 2 , ref Vector3 startPos , ref Vector3 endPos , ref NavmeshPoint resultPos , IntPtr navquery);
/// <summary> /// Creates a new tile set. /// </summary> /// <remarks> /// <para> /// The bounds is normally based on the desired origin of the navigation mesh /// and the maximum bounds of the input geometry. /// </para> /// </remarks> /// <param name="boundsMin">The minimum AABB bounds of the set.</param> /// <param name="boundsMax">The maximum AABB counds of the set.</param> /// <param name="config">The shared NMGen configuration.</param> /// <param name="geom">The input geometry.</param> /// <returns>A new tile set, or null on error.</returns> public static TileSetDefinition Create(Vector3 boundsMin, Vector3 boundsMax , NMGenParams config , InputGeometry geom) { if (config == null || !config.IsValid() || !TriangleMesh.IsBoundsValid(boundsMin, boundsMax) || geom == null || config.tileSize <= 0) { return(null); } int w; int d; NMGen.DeriveSizeOfTileGrid(boundsMin, boundsMax , config.XZCellSize , config.tileSize , out w, out d); if (w < 1 || d < 1) { return(null); } return(new TileSetDefinition(w, d, boundsMin, boundsMax, config, geom)); }
public static extern NavStatus dtqFindDistanceToWall(IntPtr query , NavmeshPoint position , float searchRadius , IntPtr filter , ref float distance , ref Vector3 closestPoint , ref Vector3 normal);
/// <summary> /// Normalizes the specified vector such that its length is equal to one. (Costly method!) /// </summary> /// <param name="v">The vector.</param> /// <returns>A normalized vector.</returns> public static Vector3 Normalize(Vector3 v) { float m = (float)Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z); if (m < MathUtil.Tolerance) { m = 1; } v.x /= m; v.y /= m; v.z /= m; if (Math.Abs(v.x) < MathUtil.Tolerance) { v.x = 0; } if (Math.Abs(v.y) < MathUtil.Tolerance) { v.y = 0; } if (Math.Abs(v.z) < MathUtil.Tolerance) { v.z = 0; } return(v); }
/// <summary> /// Derive the width and depth of a cell grid based on the provided parameters. /// </summary> /// <remarks> /// <para> /// This method does not validate the parameters. /// </para> /// </remarks> /// <param name="boundsMin">The minimum bounds.</param> /// <param name="boundsMax">The maximum bounds.</param> /// <param name="xzCellSize"> /// The cell size on the xz-plane. (<see cref="NMGenParams.XZCellSize"/>) /// </param> /// <param name="width">The number of cells along the x-axis.</param> /// <param name="depth">The number of cells along the z-axis.</param> public static void DeriveSizeOfCellGrid(Vector3 boundsMin, Vector3 boundsMax , float xzCellSize , out int width, out int depth) { width = (int)((boundsMax.x - boundsMin.x) / xzCellSize + 0.5f); depth = (int)((boundsMax.z - boundsMin.z) / xzCellSize + 0.5f); }
/// <summary> /// Gets the distance between the specified points on the xz-plane. (Ignores y-axis.) /// </summary> /// <param name="u">Vector u.</param> /// <param name="v">Vector v.</param> /// <returns>The distance between the specified points on the xz-plane.</returns> public static float GetDistance2D(Vector3 u, Vector3 v) { float dx = v.x - u.x; float dz = v.z - u.z; return((float)Math.Sqrt(dx * dx + dz * dz)); }
/// <summary> /// Determines whether or not the two points are within range of each other based on a /// xz-plane radius and a y-axis height. /// </summary> /// <remarks> /// <para> /// Essentially, one point defines the centroid of the cylinder and the other is /// tested for inclusion. The height test is <c>(Math.Abs(deltaY) < height)</c> /// </para> /// </remarks> /// <param name="a">Point A.</param> /// <param name="b">Point B.</param> /// <param name="radius">The allowed radius on the xz-plane.</param> /// <param name="height">The allowed y-axis delta.</param> /// <returns> /// True if the two vectors are within the xz-radius and y-height of each other. /// </returns> public static bool IsInRange(Vector3 a, Vector3 b, float radius, float height) { Vector3 d = b - a; return((d.x * d.x + d.z * d.z) < radius * radius && Math.Abs(d.y) < height); }
/// <summary> /// Moves the position and target from their curent locations to the desired locations. /// </summary> /// <remarks> /// <para> /// Performs an aggregrate operation in the following order: /// </para> /// <ol> /// <li><see cref="MoveTarget"/></li> /// <li><see cref="MovePosition"/></li> /// </ol> /// <para> /// See the documentation of the related functions for details on behavior. /// </para> /// <para> /// This method is more efficient than calling the other methods individually. /// </para> /// </remarks> /// <param name="desiredPosition">The desired position.</param> /// <param name="desiredTarget">The desired target.</param> public void Move(Vector3 desiredPosition, Vector3 desiredTarget) { mCorners.cornerCount = PathCorridorEx.dtpcMove(mRoot , ref desiredPosition, ref desiredTarget, ref mPosition, ref mTarget , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); }
/// <summary> /// Adds an agent to the manager. /// </summary> /// <param name="position"> /// The current position of the agent within the navigation mesh. /// </param> /// <param name="agentParams">The agent configuration.</param> /// <returns> /// A reference to the agent object created by the manager, or null on error. /// </returns> public CrowdAgent AddAgent(Vector3 position , CrowdAgentParams agentParams) { if (IsDisposed) { return(null); } IntPtr ptr = IntPtr.Zero; CrowdAgentCoreState initialState = new CrowdAgentCoreState(); int index = CrowdManagerEx.dtcAddAgent(root , ref position , ref agentParams , ref ptr , ref initialState); if (index == -1) { return(null); } mAgents[index] = new CrowdAgent(this, ptr, index); agentStates[index] = initialState; return(mAgents[index]); }
/// <summary> /// Derives the tile grid location based on the provided world space position. /// </summary> /// <param name="position">Position</param> /// <param name="x">The tile's grid x-location.</param> /// <param name="z">The tiles's grid z-location.</param> public void DeriveTileLocation(Vector3 position , out int x , out int z) { x = 0; z = 0; NavmeshEx.dtnmCalcTileLoc(root, ref position, ref x, ref z); }
public static extern NavStatus dtqFindPathExt(IntPtr query , ref NavmeshPoint startPosition , ref NavmeshPoint endPosition , [In] ref Vector3 extents , IntPtr filter , [In, Out] uint[] resultPath , ref int pathCount , int maxPath);
/// <summary> /// Constructor. /// </summary> /// <param name="tx">The x-index of the tile within the tile grid. [Limit: >= 0]</param> /// <param name="tz">The z-index of the tile within the tile grid. [Limit: >= 0]</param> /// <param name="boundsMin">The minimum bounds of the tile.</param> /// <param name="boundsMax">The maximum bounds of the tile.</param> public NMGenTileParams(int tx, int tz, Vector3 boundsMin, Vector3 boundsMax) { // Note: Use properties for auto-clamping. TileX = tx; TileZ = tz; BoundsMin = boundsMin; BoundsMax = boundsMax; }
private AreaCylinderMarker(string name, int priority, byte area , Vector3 centerBase, float radius, float height) : base(name, priority, area) { mCenterBase = centerBase; mRadius = Math.Max(0, radius); mHeight = Math.Max(0, height); }
public static extern NavStatus dtqMoveAlongSurface(IntPtr query , NavmeshPoint startPosition , [In] ref Vector3 endPosition , IntPtr filter , ref Vector3 resultPosition , [In, Out] uint[] visitedPolyRefs , ref int visitedCount , int maxVisited);
/// <summary> /// Returns the normal for the triangle. (Costly method!) /// </summary> /// <remarks> /// <para> /// The normal of a triangle is the vector perpendicular to the triangle's plane /// with the direction determined by the /// <a href="http://en.wikipedia.org/wiki/Right-hand_rule" target="_blank"> /// right-handed rule</a>. /// </para> /// </remarks> /// <param name="a">Vertex A of triangle ABC.</param> /// <param name="b">Vertex B of triangle ABC.</param> /// <param name="c">Vertex C of triangle ABC.</param> /// <returns>The normal of the triangle.</returns> public static Vector3 GetNormal(Vector3 a, Vector3 b, Vector3 c) { // Reference: // http://en.wikipedia.org/wiki/Surface_normal#Calculating_a_surface_normal // N = (B - A) x (C - A) with the final result normalized. return(Vector3Util.Normalize(Vector3Util.Cross(b - a, c - a))); }
public static extern int dtpcMoveTargetPosition(IntPtr corridor , [In] ref Vector3 npos , ref NavmeshPoint pos , [In, Out] Vector3[] cornerVerts , [In, Out] WaypointFlag[] cornerFlags , [In, Out] uint[] cornerPolys , int maxCorners , IntPtr navquery , IntPtr filter);
public static extern int dtpcOptimizePathVisibilityExt(IntPtr corridor , [In] ref Vector3 next , float pathOptimizationRange , [In, Out] Vector3[] cornerVerts , [In, Out] WaypointFlag[] cornerFlags , [In, Out] uint[] cornerPolys , int maxCorners , IntPtr navquery , IntPtr filter);
/// <summary> /// Moves the target from its curent location to the desired location, adjusting the /// corridor as needed to reflect the change. /// </summary> /// <remarks> /// <para> /// Behavior: /// </para> /// <ul> /// <li>The movement is constrained to the surface of the navigation mesh.</li> /// <li>The corridor is automatically adjusted (shorted or lengthened) and /// <see cref="Corners"/> updated in order to remain valid.</li> /// <li>The new position will be located in the adjusted corridor's last polygon.</li> /// </ul> /// <para> /// The expected use case: The desired target will be 'near' the corridor. What is /// considered 'near' depends on local polygon density, query search extents, etc. /// </para> /// <para> /// The resulting target will differ from the desired target if the desired target is /// not on the navigation mesh, or it can't be reached using a local search. /// </para> /// </remarks> /// <param name="desiredTarget">The desired target.</param> /// <returns>The result of the move.</returns> public NavmeshPoint MoveTarget(Vector3 desiredTarget) { mCorners.cornerCount = PathCorridorEx.dtpcMoveTargetPosition(mRoot , ref desiredTarget, ref mTarget , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); return(mTarget); }
/// <summary> /// Loads a new path and target into the corridor. /// </summary> /// <remarks> /// <para> /// The current position is expected to be within the first /// polygon in the path. The target is expected to be in the last /// polygon. /// </para> /// </remarks> /// <param name="target">The target location within the last polygon of the path.</param> /// <param name="path"> /// The path corridor. [(polyRef) * <paramref name="pathCount"/>] /// </param> /// <param name="pathCount"> /// The number of polygons in the path. /// [Limits: 0 <= value <= <see cref="MaxPathSize"/>] /// </param> public void SetCorridor(Vector3 target , uint[] path , int pathCount) { mCorners.cornerCount = PathCorridorEx.dtpcSetCorridor(mRoot , ref target, path, pathCount, ref mTarget , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); }
public static extern NavStatus dtqRaycast(IntPtr query , NavmeshPoint startPosition , [In] ref Vector3 endPosition , IntPtr filter , ref float hitParameter , ref Vector3 hitNormal , [In, Out] uint[] path , ref int pathCount , int maxPath);
public static extern NavStatus dtqFindLocalNeighbourhood(IntPtr query , uint startPolyRef , [In] ref Vector3 position , float radius , IntPtr filter , [In, Out] uint[] resultPolyRefs , [In, Out] uint[] resultParentRefs , ref int resultCount , int maxResult);
private ConnectionSet(Vector3[] verts, float[] radii , byte[] dirs, byte[] areas, ushort[] flags, uint[] userIds) { this.verts = verts; this.radii = radii; this.dirs = dirs; this.areas = areas; this.flags = flags; this.userIds = userIds; }
private InputGeometryBuilder(ChunkyTriMeshBuilder builder , Vector3 boundsMin , Vector3 boundsMax , bool isThreadSafe) { mBuilder = builder; mBoundsMin = boundsMin; mBoundsMax = boundsMax; mIsThreadSafe = isThreadSafe; }
/// <summary> /// Constructs and initializes the structure. /// </summary> /// <param name="origin">The tile space origin.</param> /// <param name="tileWidth">The width of each tile. (Along the x-axis.)</param> /// <param name="tileDepth">The depth of each tile. (Along the z-axis.)</param> /// <param name="maxTiles"> /// The maximum number of tiles the navigation mesh can contain. /// </param> /// <param name="maxPolysPerTile"> /// The maximum number of polygons each tile can contain. /// </param> public NavmeshParams(Vector3 origin , float tileWidth, float tileDepth , int maxTiles, int maxPolysPerTile) { this.origin = origin; this.tileWidth = Math.Max(MinTileSize, tileWidth); this.tileDepth = Math.Max(MinTileSize, tileDepth); this.maxTiles = Math.Max(1, maxTiles); this.maxPolysPerTile = Math.Max(1, maxPolysPerTile); }
private AreaConvexMarker(string name , int priority , byte area , Vector3[] verts , float ymin , float ymax) : base(name, priority, area) { this.verts = verts; this.ymin = ymin; this.ymax = ymax; }
public void Reset() { contourCount = 0; contours = IntPtr.Zero; boundsMax = Vector3Util.Zero; boundsMin = Vector3Util.Zero; xzCellSize = 0; yCellSize = 0; width = 0; depth = 0; borderSize = 0; }
/// <summary> /// Adds a single triangle. /// </summary> /// <param name="vertA">Vertex A of triangle ABC.</param> /// <param name="vertB">Vertex B of triangle ABC.</param> /// <param name="vertC">Vertex C of triangle ABC.</param> /// <param name="area">The triangle area.</param> public void AddTriangle(Vector3 vertA, Vector3 vertB, Vector3 vertC, byte area) { mTris.Add(mVerts.Count); mVerts.Add(vertA); mTris.Add(mVerts.Count); mVerts.Add(vertB); mTris.Add(mVerts.Count); mVerts.Add(vertC); mAreas.Add(area); }
private TileSetDefinition(int width, int depth , Vector3 boundsMin, Vector3 boundsMax , NMGenParams config , InputGeometry geom) { // Note: The constructor is private, which is why // the references are being stored. mBaseConfig = config.Clone(); mGeometry = geom; mWidth = width; mDepth = depth; mBoundsMin = boundsMin; mBoundsMax = boundsMax; }
/// <summary> /// Returns a value suitable for comparing the relative area of two triangles. (E.g. /// Is triangle A larger than triangle B.) /// </summary> /// <remarks> /// <para> /// The value returned by this method can be converted to an area as follows: /// <c>Area = Math.sqrt(value) / 2</c> /// </para> /// <para> /// Useful for cheaply comparing the size of triangles. /// </para> /// </remarks> /// <param name="a">Vertex A of triangle ABC.</param> /// <param name="b">Vertex B of triangle ABC.</param> /// <param name="c">Vertex C of triangle ABC.</param> /// <returns>A value suitable for comparing the relative area of two triangles.</returns> public static float GetAreaComp(Vector3 a, Vector3 b, Vector3 c) { // References: // http://softsurfer.com/Archive/algorithm_0101/algorithm_0101.htm#Modern%20Triangles // Get directional vectors. Vector3 u = b - a; // A -> B Vector3 v = c - a; // A -> C // Cross product. Vector3 n = new Vector3(u.y * v.z - u.z * v.y , -u.x * v.z + u.z * v.x , u.x * v.y - u.y * v.x); return Vector3Util.GetLengthSq(n); }
/// <summary> /// Returns the <a href="http://en.wikipedia.org/wiki/Centroid" target="_blank"> /// centroid</a> of a convex polygon. /// </summary> /// <remarks> /// <para> /// Behavior is undefined if the polygon is not convex. /// </para> /// <para> /// Behavior is undefined if the vector being overwritten in the out array is a vertex /// in the polygon. (Can only happen if the vertices and result arrays are the same object.) /// </para> /// </remarks> /// <param name="vertices"> /// An array of vertices that contains a representation of a polygon with an arbitrary /// number of sides. Wrap direction does not matter. /// </param> /// <param name="startVert">The index of the first vertex in the polygon.</param> /// <param name="vertCount">The number of vertices in the polygon.</param> /// <param name="result">The array to store the result in.</param> /// <param name="resultVert">The index in the result array to store the result.</param> /// <returns>A reference to the result argument.</returns> public static Vector3[] GetCentroid(Vector3[] vertices , int startVert , int vertCount , Vector3[] result , int resultVert) { // Reference: // http://en.wikipedia.org/wiki/Centroid#Of_a_finite_set_of_points result[resultVert] = new Vector3(0, 0, 0); int length = (startVert+vertCount); for (int i = startVert; i < length; i++) { result[resultVert] += vertices[i]; } result[resultVert].x /= vertCount; result[resultVert].y /= vertCount; result[resultVert].z /= vertCount; return result; }
/// <summary> /// Moves the target from its curent location to the desired location, adjusting the /// corridor as needed to reflect the change. /// </summary> /// <remarks> /// <para> /// Behavior: /// </para> /// <ul> /// <li>The movement is constrained to the surface of the navigation mesh.</li> /// <li>The corridor is automatically adjusted (shorted or lengthened) and /// <see cref="Corners"/> updated in order to remain valid.</li> /// <li>The new position will be located in the adjusted corridor's last polygon.</li> /// </ul> /// <para> /// The expected use case: The desired target will be 'near' the corridor. What is /// considered 'near' depends on local polygon density, query search extents, etc. /// </para> /// <para> /// The resulting target will differ from the desired target if the desired target is /// not on the navigation mesh, or it can't be reached using a local search. /// </para> /// </remarks> /// <param name="desiredTarget">The desired target.</param> /// <returns>The result of the move.</returns> public NavmeshPoint MoveTarget(Vector3 desiredTarget) { mCorners.cornerCount = PathCorridorEx.dtpcMoveTargetPosition(mRoot , ref desiredTarget, ref mTarget , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); return mTarget; }
/// <summary> /// Moves over an off-mesh connection. /// </summary> /// <remarks> /// <para> /// This method is minimally tested and documented. /// </para> /// </remarks> /// <param name="connectionRef">The connection polygon reference.</param> /// <param name="endpointRefs">Polygon endpoint references. [Length: 2]</param> /// <param name="startPosition">The start position.</param> /// <param name="endPosition">The end position.</param> /// <returns>True if the operation succeeded.</returns> public bool MoveOverConnection(uint connectionRef, uint[] endpointRefs , Vector3 startPosition, Vector3 endPosition) { return PathCorridorEx.dtpcMoveOverOffmeshConnection(mRoot , connectionRef, endpointRefs, ref startPosition, ref endPosition, ref mPosition , mQuery.root); }
/// <summary> /// Attempts to optimize the path if the specified point is visible from the current /// position. /// </summary> /// <remarks> /// <para> /// Improves pathfinding appearance when using meshes that contain non-border. /// vertices. (E.g. Tiled meshes and meshes constructed using multiple areas.) /// </para> /// <para> /// The only time <paramref name="updateCorners"/> should be set to false is if a move /// or other optimization method is to be called next. Otherwise the corner data may /// become invalid. /// </para> /// <para> /// Inaccurate locomotion or dynamic obstacle avoidance can force the agent position /// significantly outside the original corridor. Over time this can result in the /// formation of a non-optimal corridor. A non-optimal corridor can also form near /// non-border vertices. (I.e. At tile corners or area transitions.) /// </para> /// <para> /// This function uses an efficient local visibility search to try to optimize the corridor /// between the current position and <paramref name="next"/>. /// </para> /// <para> /// The corridor will change only if <paramref name="next"/> is visible from the /// current position and moving directly toward the point is better than following the /// existing path. /// </para> /// <para> /// The more inaccurate the client movement, the more beneficial this method becomes. /// Simply adjust the frequency of the call to match the needs to the client. /// </para> /// <para> /// This method is not suitable for long distance searches. /// </para> /// </remarks> /// <param name="next">The point to search toward.</param> /// <param name="optimizationRange">The maximum range to search. [Limit: > 0]</param> /// <param name="updateCorners">True if the corners data should be refreshed.</param> public void OptimizePathVisibility(Vector3 next, float optimizationRange , bool updateCorners) { if (updateCorners) { mCorners.cornerCount = PathCorridorEx.dtpcOptimizePathVisibilityExt(mRoot , ref next, optimizationRange , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); } else { PathCorridorEx.dtpcOptimizePathVisibility( mRoot, ref next, optimizationRange, mQuery.root, mFilter.root); } }
/// <summary> /// Constructor. /// </summary> /// <param name="polyRef"> /// The reference of the polygon that contains the point. (Or zero if not known.) /// </param> /// <param name="point">The location of the point.</param> public NavmeshPoint(uint polyRef, Vector3 point) { this.polyRef = polyRef; this.point = point; }
/// <summary> /// Validates the structure and, optionally, the content of the mesh. /// </summary> /// <remarks> /// <para> /// The basic structural validation includes null checks, array size checks, etc. /// </para> /// <para> /// The optional content validation checks that the indices refer to valid vertices /// and that triangles do not contain duplicate vertices. /// </para> /// </remarks> /// <param name="verts">The mesh vertices.</param> /// <param name="vertCount">The vertex count.</param> /// <param name="tris">The triangle indices.</param> /// <param name="triCount">The triangle count.</param> /// <param name="includeContent"> /// If true, the content will be checked. Otherwise only the structure will be checked. /// </param> /// <returns>True if the validation tests pass.</returns> public static bool IsValid(Vector3[] verts, int vertCount , int[] tris, int triCount , bool includeContent) { if (tris == null || verts == null || triCount * 3 > tris.Length || vertCount > verts.Length || triCount < 0 || vertCount < 0) { return false; } if (includeContent) { int length = triCount * 3; for (int p = 0; p < length; p += 3) { int a = tris[p + 0]; int b = tris[p + 1]; int c = tris[p + 2]; if (a < 0 || a >= vertCount || b < 0 || b >= vertCount || c < 0 || c >= vertCount || a == b || b == c || c == a) { return false; } } } return true; }
/// <summary> /// Add a connection. /// </summary> /// <remarks> /// <para> /// All values are auto-clamped to valid values. /// </para> /// </remarks> /// <param name="start">The connection start point.</param> /// <param name="end">The connection end point.</param> /// <param name="radius">The radius of the connection vertices.</param> /// <param name="isBidirectional">True if the connection can be traversed in both /// directions. (Start to end, end to start.)</param> /// <param name="area">The connection area id.</param> /// <param name="flags">The connection flags.</param> /// <param name="userId">The connection user id.</param> public void Add(Vector3 start, Vector3 end, float radius , bool isBidirectional, byte area, ushort flags, uint userId) { mVerts.Add(start); mVerts.Add(end); mRadii.Add(System.Math.Max(MathUtil.Epsilon, radius)); mDirs.Add((byte)(isBidirectional ? 1 : 0)); mAreas.Add(NMGen.ClampArea(area)); mFlags.Add(flags); mUserIds.Add(userId); }
/// <summary> /// Constructor. /// </summary> /// <param name="start">The start point.</param> /// <param name="end">The end point.</param> /// <param name="radius">The radius of the start and end points. [Limit: >0]</param> /// <param name="isBidDrectional">True if the connection is bi-directional.</param> /// <param name="area">The area. [Limit: <= <see cref="Navmesh.MaxArea"/>].</param> /// <param name="flags">The connection flags.</param> /// <param name="userId">The id of the off-mesh connection. (User defined.)</param> public OffMeshConnection(Vector3 start, Vector3 end ,float radius, bool isBidDrectional, byte area, ushort flags, uint userId) { this.start = start; this.end = end; this.radius = MathUtil.ClampToPositiveNonZero(radius); direction = (byte)(isBidDrectional ? 1 : 0); this.area = NavUtil.ClampArea(area); this.flags = flags; this.userId = userId; }
/// <summary> /// Creates a connection set. /// </summary> /// <remarks> /// <para> /// Connection sets created using this method are not guarenteed to be valid or /// safe for threaded builds. /// </para> /// <para> /// The generated connection set will directly reference the construction /// parameters. /// </para> /// </remarks> /// <param name="verts">The connection vertices. [(start, end) * connCount]</param> /// <param name="radii">The connection radii. [Length: connCount]</param> /// <param name="dirs">The connection direction flags. [Length: connCount]</param> /// <param name="areas">The connection areas. [Length: connCount]</param> /// <param name="flags">The connection flags. [Length: connCount]</param> /// <param name="userIds">The connection user ids. [Length: connCount]</param> /// <returns>An unsafe connection set.</returns> public static ConnectionSet UnsafeCreate(Vector3[] verts, float[] radii , byte[] dirs, byte[] areas, ushort[] flags, uint[] userIds) { return new ConnectionSet(verts , radii , dirs , areas , flags , userIds); }
/// <summary> /// Gets a copy of the vertex buffer. /// </summary> /// <param name="buffer"> /// The buffer to load the results into. /// [Length: >= <see cref="NavmeshTileHeader.vertCount"/>] /// </param> /// <returns>The number of vertices returned.</returns> public int GetVerts(Vector3[] buffer) { if (mOwner.IsDisposed || buffer == null) return 0; return NavmeshTileEx.dtnmGetTileVerts(mTile , buffer , buffer.Length); }
internal InputGeometry(ChunkyTriMesh mesh, Vector3 boundsMin, Vector3 boundsMax) { mBoundsMin = boundsMin; mBoundsMax = boundsMax; mMesh = mesh; }
/// <summary> /// Gets the connections whose start vertex is within the specified bounds. /// </summary> /// <remarks> /// <para> /// The out parameters will be null if the return result is zero. /// </para> /// </remarks> /// <param name="xmin">The minimum x-axis bounds.</param> /// <param name="zmin">The minimum z-axis bounds.</param> /// <param name="xmax">The maximum x-axis bounds.</param> /// <param name="zmax">The maximum z-axis bounds.</param> /// <param name="rverts">The connection vertices. [(start, end) * connCount]</param> /// <param name="rradii">The connection radii. [Length: connCount]</param> /// <param name="rdirs">The connection direction flags. [Length: connCount]</param> /// <param name="rareas">The connection areas. [Length: connCount]</param> /// <param name="rflags">The connection flags. [Length: connCount]</param> /// <param name="ruserIds">The connection user ids. [Length: connCount]</param> /// <returns>The number of connection returned.</returns> public int GetConnections(float xmin, float zmin, float xmax, float zmax , out Vector3[] rverts, out float[] rradii , out byte[] rdirs, out byte[] rareas, out ushort[] rflags, out uint[] ruserIds) { rverts = null; rradii = null; rdirs = null; rareas = null; rflags = null; ruserIds = null; if (radii.Length == 0) return 0; List<Vector3> rlverts = new List<Vector3>(); List<float> rlradii = new List<float>(); List<byte> rldirs = new List<byte>(); List<byte> rlareas = new List<byte>(); List<ushort> rlflags = new List<ushort>(); List<uint> rluserIds = new List<uint>(); for (int i = 0; i < radii.Length; i++) { Vector3 v = verts[i * 2 + 0]; if (Rectangle2.Contains(xmin, zmin, xmax, zmax, v.x, v.z)) { rlverts.Add(v); rlverts.Add(verts[i * 2 + 1]); rlradii.Add(radii[i]); rldirs.Add(dirs[i]); rlareas.Add(areas[i]); rlflags.Add(flags[i]); rluserIds.Add(userIds[i]); } } if (rlradii.Count == 0) return 0; rverts = rlverts.ToArray(); rradii = rlradii.ToArray(); rdirs = rldirs.ToArray(); rareas = rlareas.ToArray(); rflags = rlflags.ToArray(); ruserIds = rluserIds.ToArray(); return rradii.Length; }
/// <summary> /// Adds an arbitrary group of triangles. /// </summary> /// <remarks> /// <para> /// All triangles will default to <see cref="NMGen.MaxArea"/> if the /// <paramref name="areas"/> parameter is null. /// </para> /// </remarks> /// <param name="verts"> /// The triangle vertices. [Length: >= <paramref name="vertCount"/>] /// </param> /// <param name="vertCount">The number of vertices. [Length: >= 3]</param> /// <param name="tris"> /// The triangles. [(vertAIndex, vertBIndex, vertCIndex) * triCount] /// [Length: >= 3 * <paramref name="triCount"/>] /// </param> /// <param name="areas"> /// The triangle areas. (Optional) [Length: >= <paramref name="triCount"/>] /// </param> /// <param name="triCount">The number of triangles. [Limit: > 0]</param> /// <returns>True if the triangles were successfully added.</returns> public bool AddTriangles(Vector3[] verts, int vertCount , int[] tris, byte[] areas, int triCount) { if (triCount < 1 || vertCount < 3 || verts == null || verts.Length < vertCount || tris == null || tris.Length < triCount * 3 || areas != null && areas.Length < triCount) { return false; } if (areas == null) areas = NMGen.CreateDefaultAreaBuffer(triCount); int iVertOffset = mVerts.Count; if (vertCount == verts.Length) mVerts.AddRange(verts); else { mVerts.Capacity += vertCount; for (int p = 0; p < vertCount; p++) { mVerts.Add(verts[p]); } } int length = triCount * 3; mTris.Capacity += length; for (int p = 0; p < length; p++) { mTris.Add(tris[p] + iVertOffset); } if (areas.Length == triCount) mAreas.AddRange(areas); else { mAreas.Capacity += triCount; for (int i = 0; i < triCount; i++) { mAreas.Add(areas[i]); } } return true; }
/// <summary> /// Constructor. /// </summary> /// <remarks> /// <para> /// This constructor assigns the provided arrays to the object. (No copying.) /// </para> /// </remarks> /// <param name="verts"> /// The vertices. [Length: >= <typeparamref name="vertCount"/>] /// </param> /// <param name="vertCount">The number of vertices.</param> /// <param name="tris"> /// The triangles. [(vertAIndex, vertBIndex, vertCIndex) * triCount] /// [Length: >= (<typeparamref name="triCount"/>) * 3] /// </param> /// <param name="triCount">The number of triangles.</param> public TriangleMesh(Vector3[] verts, int vertCount, int[] tris, int triCount) { this.verts = verts; this.vertCount = vertCount; this.tris = tris; this.triCount = triCount; }
/// <summary> /// True if the minimum bounds is less than the maximum bounds on all axes. /// </summary> /// <param name="boundsMin">The minimum AABB bounds.</param> /// <param name="boundsMax">The maximum AABB bounds.</param> /// <returns> /// True if the minimum bounds is less than the maximum bounds on all axes. /// </returns> public static bool IsBoundsValid(Vector3 boundsMin, Vector3 boundsMax) { return !(boundsMax.x < boundsMin.x || boundsMax.y < boundsMin.y || boundsMax.z < boundsMin.z); }
private void Resize() { vertCount = 0; polyCount = 0; maxVertsPerPoly = 0; boundsMin = Vector3Util.Zero; boundsMax = Vector3Util.Zero; xzCellSize = 0; yCellSize = 0; borderSize = 0; walkableHeight = 0; walkableStep = 0; walkableRadius = 0; polys = null; verts = null; areas = null; flags = null; regions = null; }
/// <summary> /// Gets the AABB bounds of the mesh. /// </summary> /// <remarks> /// <para> /// Do not call this method on an uninitialized mesh. /// </para> /// </remarks> /// <param name="boundsMin">The minimum bounds of the mesh.</param> /// <param name="boundsMax">The maximum bounds of the mesh.</param> public void GetBounds(out Vector3 boundsMin, out Vector3 boundsMax) { boundsMin = verts[tris[0]]; boundsMax = verts[tris[0]]; for (int i = 1; i < triCount * 3; i++) { Vector3 v = verts[tris[i]]; boundsMin.x = Math.Min(boundsMin.x, v.x); boundsMin.y = Math.Min(boundsMin.y, v.y); boundsMin.z = Math.Min(boundsMin.z, v.z); boundsMax.x = Math.Max(boundsMax.x, v.x); boundsMax.y = Math.Max(boundsMax.y, v.y); boundsMax.z = Math.Max(boundsMax.z, v.z); } }
/// <summary> /// Creates an array of vectors from the provided navmesh points. /// </summary> /// <remarks> /// <para> /// A new array will be created if the <paramref name="target"/> array is null. /// </para> /// </remarks> /// <param name="source">The source array.</param> /// <param name="sourceIndex">The start of the copy in the source.</param> /// <param name="target">The target of the copy. (Optional)</param> /// <param name="targetIndex">The start copy location within the target.</param> /// <param name="count">The number of vectors to copy.</param> /// <returns> /// An array containing the copied vectors. (A reference to <paramref name="target"/> /// if it was non-null.) /// </returns> public static Vector3[] GetPoints(NavmeshPoint[] source, int sourceIndex , Vector3[] target, int targetIndex , int count) { if (target == null) target = new Vector3[source.Length + targetIndex]; for (int i = 0; i < count; i++) { target[targetIndex + i] = source[sourceIndex + i].point; } return target; }