ludic/packages/ludic.nav
2026-09-27 21:57:53 +03:00
..
lib build(nav): the Windows library with the staged file read, rebuilt on the PC - 11 tests there 2026-09-27 21:57:53 +03:00
native feat(npc, nav): 17.5 - a person walks by the world's way where it has one (NpcWorld.way: the next corner toward the target, asked every half second, on reaching it or for a new target; the errand begins only at the target itself; the step round and the give-up stay the fallback), way_test round a wall; and ludic.nav reads a mesh file into the shim's own memory and frees it once parsed, so loading a map leaves nothing in Ludic's never-freed heap 2026-09-27 21:57:34 +03:00
tests feat(nav): 17.4 - a mesh has eight filters (a cost per kind of ground each) and every path, straight line and random point names the one it walks by, so a deer, a marmot and a person each take their own way over the same mesh; tested with a band of thicket walked round by the filter that dislikes it 2026-09-27 21:34:23 +03:00
build.ludic feat(npc, nav): 17.5 - a person walks by the world's way where it has one (NpcWorld.way: the next corner toward the target, asked every half second, on reaching it or for a new target; the errand begins only at the target itself; the step round and the give-up stay the fallback), way_test round a wall; and ludic.nav reads a mesh file into the shim's own memory and frees it once parsed, so loading a map leaves nothing in Ludic's never-freed heap 2026-09-27 21:57:34 +03:00
index.ludic feat(nav): 17.1/17.3 - ludic.nav over Recast & Detour v1.6.0 (zlib), built here from the pinned tag on the Mac and the PC (the DLL imports KERNEL32 alone): a navmesh per kind of walker from triangles with an area byte each and cylinders nothing stands in, saved and loaded as bytes; the nearest point, a path as corners (partial when the end cannot be reached), whether a straight line stays walkable, a cost per kind of ground. Tests on a hand-built meadow: round a post, over a ford, stopped at a bank, a saved mesh answering alike - Mac and PC 2026-09-27 15:29:11 +03:00
native.ludic feat(npc, nav): 17.5 - a person walks by the world's way where it has one (NpcWorld.way: the next corner toward the target, asked every half second, on reaching it or for a new target; the errand begins only at the target itself; the step round and the give-up stay the fallback), way_test round a wall; and ludic.nav reads a mesh file into the shim's own memory and frees it once parsed, so loading a map leaves nothing in Ludic's never-freed heap 2026-09-27 21:57:34 +03:00
package.ludic feat(nav): 17.1/17.3 - ludic.nav over Recast & Detour v1.6.0 (zlib), built here from the pinned tag on the Mac and the PC (the DLL imports KERNEL32 alone): a navmesh per kind of walker from triangles with an area byte each and cylinders nothing stands in, saved and loaded as bytes; the nearest point, a path as corners (partial when the end cannot be reached), whether a straight line stays walkable, a cost per kind of ground. Tests on a hand-built meadow: round a post, over a ford, stopped at a bank, a saved mesh answering alike - Mac and PC 2026-09-27 15:29:11 +03:00
query.ludic feat(nav): 17.4 - a mesh has eight filters (a cost per kind of ground each) and every path, straight line and random point names the one it walks by, so a deer, a marmot and a person each take their own way over the same mesh; tested with a band of thicket walked round by the filter that dislikes it 2026-09-27 21:34:23 +03:00
README.md feat(nav): 17.4 - a mesh has eight filters (a cost per kind of ground each) and every path, straight line and random point names the one it walks by, so a deer, a marmot and a person each take their own way over the same mesh; tested with a band of thicket walked round by the filter that dislikes it 2026-09-27 21:34:23 +03:00
state.ludic feat(nav): 17.4 - a mesh has eight filters (a cost per kind of ground each) and every path, straight line and random point names the one it walks by, so a deer, a marmot and a person each take their own way over the same mesh; tested with a band of thicket walked round by the filter that dislikes it 2026-09-27 21:34:23 +03:00

ludic.nav

A walkable mesh of the world and the ways across it, over Recast & Detour (zlib) built here from a pinned tag (phase 17). Uses ludic.base and nothing else.

import "ludic.nav"

The rules it keeps

  • One mesh per kind of walker. NAV_PERSON (0.35 m wide, the hiker's 0.55 m step), NAV_LARGE (an elk, a bear, a horse) and NAV_SMALL (a hare, a marmot) each have their own, because what a hare slips between a bear walks round. A NavConfig says what a mesh is built for: its voxels and its walker's height, radius, step and steepest slope.
  • Built from ground, or loaded from a bake. A NavGround is triangles - each with an area byte, 0 not walkable and 1..62 a kind of ground - and what nothing stands in: cylinders (a trunk, a post) and convex footprints (a boulder). Ground steeper than the slope is cleared whatever its area. nav_build makes one mesh of it; a map is square tiles (nav_tiled, then nav_tile_build for each tile from the ground under it and a few metres round), and a tile must be a whole number of cells across or its seams never join - the shim refuses one that is not. A game bakes its maps once and loads the bytes (nav_save_file / nav_load_file, one format for one tile or many): a map is never built at start-up. Polygon refs are 64-bit, so a map may have 16384 tiles.
  • A path is corners. nav_path answers the way from one point to another as the corners where it turns, the first the start and the last the end. When the end cannot be reached the path stops as near it as the mesh allows and nav_partial says so - a walker goes there and gives up, never through a river. Either end off the mesh is -1.
  • A wander's goal is somewhere it can go. nav_random_near answers a point about r away that a walker standing at the start can reach - never across a river without a ford - from a seed the caller draws from its own Rng, so the same seed is the same point on every machine.
  • A kind of ground has a cost, and a walker has a taste. A mesh carries NAV_FILTERS (8) sets of costs per area (nav_area_cost(kind, filter, area, cost)), and every path, straight line and random point names the filter it walks by: a deer's dislikes scree, a marmot's likes it, a person's prefers the town. The nearest point uses filter 0.
  • The same question gets the same answer. Detour is deterministic for the same mesh and the same points, so co-op's order of dice is untouched by asking it.

API

NAV_PERSON, NAV_LARGE, NAV_SMALL, NAV_FILTERS, NAV_CORNERS the kinds of walker, the filters a mesh has (8), and a path's most corners (256)
NavConfig { cell, cell_h, height, radius, climb, slope } what a mesh is built for (a person by default, 0.25 m cells)
NavGround { v, nv, t, nt, area, cyl, nc, foot, nf } what it is built from: triangles, an area byte each, cylinders (x, y, z, r, h) and footprints (n, n points x z, ymin, ymax)
nav_build(kind, ground, config) -> bool, nav_polygons(kind) one mesh over all of it
nav_tiled(kind, ox, oz, tile, max_tiles, max_polys) -> bool, nav_tile_build(kind, tx, tz, ground, config) -> int a map of square tiles, a tile at a time (its polygons, 0 none, -1 failed)
nav_save_file(kind, path), nav_load_file(kind, path), nav_reset() a baked mesh written and read; every mesh let go
nav_nearest(kind, x, y, z) -> bool, nav_near_x/y/z() the nearest walkable point within a couple of metres
nav_path(kind, filter, sx, sy, sz, ex, ey, ez) -> int, nav_corners(), nav_corner_x/y/z(i), nav_partial(kind) a way as corners
nav_straight(kind, filter, sx, sy, sz, ex, ez) -> bool does the straight line stay walkable
nav_random_near(kind, filter, x, y, z, r, seed) -> bool, nav_near_x/y/z() a reachable point about r away, from the caller's seed
nav_area_cost(kind, filter, area, cost) how dear a kind of ground is to cross by that filter

Tests

ludic test packages/ludic.nav

A 40 m meadow built by hand: a path round a post and a boulder's footprint, round a band of thicket by a filter that dislikes it and straight through by the plain one, over a river's ford, stopping at the bank of a river with none, the nearest point, random points that never cross the river, and a saved mesh answering as the built one did. And a 128 m meadow as four tiles: a path across the seams, a tile under water with no polygons, and the set saved and loaded.

The native library

native/build.sh fetches Recast & Detour v1.6.0, checks its SHA-256, and builds Recast, Detour and the shim (native/shim/nav_shim.cpp) into lib/<target>/ - the same script on the Mac and on the PC (Git Bash, the LLVM installer's clang). native/LICENSE-recastnavigation ships with it.