The navmesh

A navmesh holds the committed tiles and off-mesh links that queries read. Changes are staged, then committed together at a point the host chooses, so a query never sees half of a change.

mnavNavmeshDef def = mnavDefaultNavmeshDef();   // tier, limits, allocator
def.bake = bakeDef;                              // the def the tiles are baked with
mnavNavmesh* navmesh = NULL;
mnavNavmeshDefResult created = mnavCreateNavmesh(&def, &navmesh);
mnavTileResult staged = mnavNavmesh_StageTile(navmesh, bytes, size);     // copied
// staged.result; refused bytes are named by section and element
mnavResult committed = mnavNavmesh_Commit(navmesh);

Streaming

Tiles load and unload at run time (mnavNavmesh_StageTile, mnavNavmesh_StageTileRemoval). A place with no committed tile is not a wall: a search that runs into one ends with mnav_pathNotLoaded, a ray with mnav_rayNotLoaded, and mnavNavmesh_GetTile there returns mnav_errorNotLoaded. An agent waiting on such a path waits for the tile, then searches again.

Ids

Tiles, polygons and off-mesh links are named by ids that carry a generation. Replacing or removing a tile moves its slot to the next generation, so an id handed out before is refused with mnav_errorStale instead of naming whatever now stands there. A host that keeps polygon ids (in corridors, in its own maps) checks them after each commit; corridors do it with mnavNavmesh_CheckCorridor.

Tiers

The navmesh def's tier declares what may change once tiles are loaded. Tiles and off-mesh links load and unload in every tier.

Tier Also allows
mnav_tierStatic Nothing more.
mnav_tierModifiers A polygon's area changed (mnavPolygon_StageArea, mnav_areaNone to block it) and links enabled or disabled (mnavLink_StageEnable, which mnavLink_IsEnabled reads back after the commit).
mnav_tierDynamic A loaded tile replaced in one commit, as after a rebuild with obstacles.

A change the tier does not allow is mnav_errorUnsupported. An area change lasts while its tile is loaded and goes when the tile is replaced or removed. To change the area of part of a polygon, mark the shape on the input at bake time with an area volume, so that polygons end where the change does.

mnavPolygon_GetArea reads a polygon's committed area, mnavNavmesh_GetTier the navmesh's tier.

An off-mesh link joins two points the ground does not join: a jump, a drop, a climb, a ladder, a door, a teleport, or one of the host's own kinds (6 to 63).

mnavQuery_GenerateLinks proposes drops and jumps along the open edges of an mnavTileRange of tiles (NULL for every tile), sampled every spacing meters, into a caller buffer. A drop lands below the edge, down to dropMax, and goes both ways when no deeper than climbMax; a jump lands across a gap, up to jumpMax. A link is kept only when the navmesh does not already walk there within detour times the straight distance, when it is not within filterDistance of an earlier one, and when the host's clearance test, if the def has one, passes it. A buffer too short is refused with mnav_errorCapacity and holds the first links; the count it gives is room enough for every link, though links past the buffer were not filtered against each other, so a second call with that capacity may keep fewer.

The navmesh holds no geometry, so without a clearance test a jump over a wall between two areas the navmesh does not join is proposed too. Give the generator the host's collision test to rule those out, and stage the links worth keeping once generation returns. The test may read the navmesh, such as a ray or a nearest point, with a query context of its own, and may stage a tile or a tile's removal, which changes nothing generation reads. Any other stage call, a commit or destroy call on the navmesh, or any call on the generating context, made from the test is refused with mnav_errorBusy and counted as misuse (mnavNavmesh_GetMisuse, mnavQuery_GetMisuse).