Baking

A bake turns geometry into navmesh tiles. The world is cut into square tiles of tileCells cells of cellSize meters, counted from the def's origin (by default 128 cells of 0.25 m, so 32 m tiles), and each tile bakes on its own.

mnavBakeDef def = mnavDefaultBakeDef();   // agent, cells, limits
mnavBaker* baker = NULL;
mnavBakeDefResult made = mnavCreateBaker(&def, &baker);
// made.result; on a refused def, made.setting names the bad setting.

mnavTriangleMesh mesh = {vertices, vertexCount, indices, triangleCount, NULL};
mnavBakeInput input = {&mesh, 1, NULL, 0, NULL, 0, NULL};   // meshes, terrains, volumes, index
mnavBakeReport report;
size_t size = 0;
mnavResult baked = mnavBaker_BakeTile(baker, &input, tileX, tileZ, &report);
if (baked == mnav_success)
{
    baked = mnavBaker_CopyTile(baker, bytes, capacity, &size);   // the tile's bytes
}

The bytes are the tile: store them as cooked content, and load them into a navmesh at run time. The same def and input give the same bytes on every platform.

The def

Input

mnavBaker_BakeTile takes meshes, terrains and volumes together in an mnavBakeInput, and mnavBaker_BakeTile2D takes outlines in an mnavBake2DInput.

Input is hostile: a NaN coordinate, an index out of range or an area past 63 is mnav_errorInvalid, a point past the extent, an infinity included, is mnav_errorLimit, and the report's input names the first input refused, by its kind (mnav_inputMesh, mnav_inputTerrain, mnav_inputVolume, mnav_inputOutline or mnav_inputObstacle) and its index in that kind's array, with the vertex, triangle, point, sample or cell refused. mnavValidateBakeInput and mnavValidateBake2DInput run the same checks on a whole input without baking, for an import step that wants to catch bad geometry early; input they accept, every bake with the def accepts at its input stage. A bake may still refuse it later, for a tile past the extent, a triangle cut into a piece of more than MNAV_MAX_PIECE_CORNERS corners, or a named limit or the memory limit reached; the return values of mnavBaker_BakeTile and mnavBaker_BakeTile2D list these. Degenerate and duplicate triangles are accepted and contribute nothing.

Many tiles

The report

mnavBakeReport gives the stage a bake ended in, the tile's counts and size, the memory peak, and the work it gave up on rather than fail: holes no bridge reached, triangulations cut short (partialRingCount, usually from a maxEdgeError of a cell or more, whose walls can cross), detail samples with no height, polygons whose detail reached MNAV_MAX_DETAIL_VERTICES (cappedDetailCount), edges sampled more widely than detailSampleDistance (coarseEdgeCount: an edge takes at most MNAV_MAX_DETAIL_EDGE_SAMPLES samples, each on a whole sixteenth of a cell, and edges between polygons or along a tile's side are not shortened by maxEdgeLength), and walkable islands smaller than minRegionArea dropped (regionsDropped), which usually point at geometry the host did not mean to be walkable. With a clock in the def (mnavClock), it also gives each stage's ticks. The clock never shapes the tile, and may call nothing of the baker: such a call is refused with mnav_errorBusy.

Obstacles

A crate or a barricade that stands still changes the tiles it stands on. Bake those tiles through a tile cache, which keeps each tile's open space as it stands before bake volumes apply, and then rebuild a tile with obstacles, given as bake volumes, without its triangles:

mnavTileCacheDef cacheDef = mnavDefaultTileCacheDef();   // 4096 tiles in 64 MiB
mnavTileCache* cache = NULL;
mnavResult cached = mnavCreateTileCache(&cacheDef, &cache);
mnavBakeInput input = {&mesh, 1, NULL, 0, NULL, 0, NULL};
if (cached == mnav_success)
{
    cached = mnavBaker_BakeTileCached(baker, cache, &input, tileX, tileZ, &report);
}

// A crate on the tile: a ring on the ground and a height range.
const mnavVec2 ring[4] = {{36, 4}, {38, 4}, {38, 6}, {36, 6}};
const mnavBakeVolume crate = {ring, 4, -1.0f, 2.0f, mnav_volumeExclude, 0};
mnavResult rebuilt = mnavBaker_RebuildTile(baker, cache, tileX, tileZ, &crate, 1, &report);