Paths and queries

Queries read a committed navmesh. A query context holds a search's scratch memory, sized by its limits (8,192 nodes and paths up to 1,000 m by default); each thread that queries uses a context of its own, and any number of contexts read one navmesh at once between commits.

mnavQueryDef queryDef = mnavDefaultQueryDef();
mnavQuery* query = NULL;
mnavResult opened = mnavCreateQuery(&queryDef, &query);

mnavNearest a, b;
const mnavVec3 box = {1.0f, 2.0f, 1.0f};      // half extents of the search
mnavResult nearA = mnavNavmesh_FindNearest(navmesh, (mnavPos3){8.0, 0.0, 8.0}, box, NULL, &a);
mnavResult nearB = mnavNavmesh_FindNearest(navmesh, (mnavPos3){8.0, 0.0, 56.0}, box, NULL, &b);
// a.polygon.slot is 0 when no polygon lies in the box

mnavPath path;
mnavResult searched =
    mnavQuery_FindPath(query, navmesh, a.polygon, a.point, b.polygon, b.point, NULL, &path);
// path.end: found, partial (out of nodes or too long), no path, not loaded
// path.points: the straight path; path.polygons: the corridor; path.links

Filters

mnavQueryFilter, from mnavDefaultQueryFilter, is an agent's capabilities: a cost per meter for each area type, the area types it may enter, and the link kinds it may cross. NULL uses every walkable area at cost 1 and every link kind. Keep one filter per kind of agent; it is plain data and can be shared by any number of queries.

The shortest way

mnavQuery_FindPath picks its corridor by costs through the midpoints of polygon edges, then pulls the path tight within it. Among pillars and clutter the corridor can weave, and the path come out a tenth or more longer than it needs to be. mnavQuery_FindShortestPath takes the same arguments and gives the same kind of result, but its path is the shortest the ground allows, and it often costs fewer instructions. It needs every area the filter includes at one cost (mnav_errorInvalid otherwise), so use it for agents with no area preferences.

Spreading searches over frames

Spatial queries

Question Function
Is the way straight to a point clear? mnavQuery_Raycast, which stops at a wall (with its normal), a place with no tile, or the end.
Where does a move along the surface end? mnavQuery_MoveAlongSurface, which slides along the walls it meets.
How high is the ground here? mnavPolygon_GetHeight, on a polygon's detail surface.
How far is the nearest wall? mnavQuery_FindWallDistance.
Which walls are near, for avoidance? mnavQuery_FindWalls, nearest first, into a caller buffer.
Which polygons lie in a box? mnavNavmesh_FindPolygons.
Where can something spawn? mnavNavmesh_FindRandomPoint over the whole navmesh, mnavQuery_FindRandomPointAround within a circle, both from a seed.
Can a point be reached at all? mnavQuery_CheckReachable, a path search without the straight path.

Grids

For tile maps, mnavQuery_FindGridPath searches an mnavGrid of area types in a query context, under the same filters: eight neighbors, no corner cut past a blocked cell. It runs jump point search when every included area costs the same and A* otherwise.

Following a path

A path corridor keeps an agent's path valid as the agent and its target move. It is a plain struct over a polygon buffer the caller owns.

mnavPolygonId buffer[256];
mnavCorridor corridor;
mnavResult reset = mnavResetCorridor(&corridor, buffer, 256, a.polygon, a.point);
mnavResult set = mnavSetCorridor(&corridor, &path);   // mnav_errorCapacity past 256

// Each tick:
mnavCorners corners;
mnavResult cornered = mnavQuery_FindCorridorCorners(query, &corridor, navmesh, &corners);
// head for corners.points[1]
mnavResult moved = mnavQuery_MoveCorridor(query, &corridor, navmesh, wanted, NULL, NULL);

A buffer too small for a path is refused, never cut: mnavSetCorridor returns mnav_errorCapacity and leaves the corridor as it was. A path's arrays live in its context's memory until that context's next search; the context's other calls, such as corners, raycasts and moves, leave them whole. Load it before the next search with the same context: a path a later search has replaced is refused with mnav_errorStale, and a path whose arrays or counts were changed with mnav_errorInvalid.

The library never moves an agent. It returns where to go, and the host applies it.