diff --git a/packages/ludic.nav/README.md b/packages/ludic.nav/README.md index 70d99108..59c4541d 100644 --- a/packages/ludic.nav/README.md +++ b/packages/ludic.nav/README.md @@ -71,3 +71,13 @@ across the seams, a tile under water with no polygons, and the set saved and loa `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//` - the same script on the Mac and on the PC (Git Bash, the LLVM installer's clang). `native/LICENSE-recastnavigation` ships with it. + +## Crowds (phase 18) + +`nav_crowd_start(kind, max, max_radius)` puts a DetourCrowd on a kind's mesh, with the mesh's tastes as +its filters. `nav_crowd_add(kind, x, y, z, radius, height, speed, filter)` adds a walker (snapped to +the mesh, -1 off it), `nav_crowd_target` and `nav_crowd_speed` say where and how fast, and +`nav_crowd_step(dt)` steps every crowd. `nav_crowd_read` puts a walker into `nav_agent_x/y/z/vx/vz`, +and `nav_crowd_us()` is what the stepping has cost. The mechanic decides and the crowd moves, and +dropping a mesh drops its crowd first. On the test meadow, twenty walkers crossing head-on never come +closer than their two radii, and a step costs about 12 us. diff --git a/packages/ludic.nav/crowd.ludic b/packages/ludic.nav/crowd.ludic new file mode 100644 index 00000000..f0930676 --- /dev/null +++ b/packages/ludic.nav/crowd.ludic @@ -0,0 +1,61 @@ +# crowd.ludic - a crowd per kind of walker (phase 18): many walkers steered along their paths at +# once, each kept clear of the others. The mechanic decides where and how fast; the crowd moves. + +# a crowd on kind's mesh for at most max walkers, none wider than max_radius; false with no mesh +export function nav_crowd_start(nav_st: mut NavState, kind: int, max: int, max_radius: float) -> bool { + let h = nv_mesh(nav_st, kind) + if h == null { return false } + nv_crowd_drop(nav_st, kind) + nav_st.nv_crowds[kind] = nvc_crowd_new(h, max, max_radius) + return nav_st.nv_crowds[kind] != null +} +function nv_crowd(nav_st: NavState, kind: int) -> pointer { + if kind < 0 or kind >= NAV_KINDS { return null } + return nav_st.nv_crowds[kind] +} +# let kind's crowd go (before its mesh, which it walks on) +function nv_crowd_drop(nav_st: mut NavState, kind: int) -> void { + if kind < 0 or kind >= NAV_KINDS or nav_st.nv_crowds[kind] == null { return } + nvc_crowd_free(nav_st.nv_crowds[kind]) + nav_st.nv_crowds[kind] = null +} + +# a walker at (x, y, z) with its width, height, top speed and taste: its index, or -1 +export function nav_crowd_add(nav_st: NavState, kind: int, x: float, y: float, z: float, radius: float, height: float, speed: float, filter: int) -> int { + let c = nv_crowd(nav_st, kind) + if c == null { return -1 } + return nvc_crowd_add(c, x, y, z, radius, height, speed, filter) +} +export function nav_crowd_remove(nav_st: NavState, kind: int, i: int) -> void { + let c = nv_crowd(nav_st, kind) + if c != null and i >= 0 { nvc_crowd_remove(c, i) } +} +# where walker i is going, snapped to the mesh; false off it +export function nav_crowd_target(nav_st: NavState, kind: int, i: int, x: float, y: float, z: float) -> bool { + let c = nv_crowd(nav_st, kind) + return c != null and i >= 0 and nvc_crowd_target(c, i, x, y, z) == 1 +} +export function nav_crowd_speed(nav_st: NavState, kind: int, i: int, speed: float) -> void { + let c = nv_crowd(nav_st, kind) + if c != null and i >= 0 { nvc_crowd_speed(c, i, speed) } +} +# every crowd one step on +export function nav_crowd_step(nav_st: NavState, dt: float) -> void { + for k in 0 .. NAV_KINDS { if nav_st.nv_crowds[k] != null { nvc_crowd_update(nav_st.nv_crowds[k], dt) } } +} +# walker i read into nav_agent_*; false when there is none +export function nav_crowd_read(nav_st: NavState, kind: int, i: int) -> bool { + let c = nv_crowd(nav_st, kind) + return c != null and i >= 0 and nvc_crowd_read(c, i, nav_st.nv_agent) == 1 +} +export function nav_agent_x(nav_st: NavState) -> float { return nav_st.nv_agent[0] } +export function nav_agent_y(nav_st: NavState) -> float { return nav_st.nv_agent[1] } +export function nav_agent_z(nav_st: NavState) -> float { return nav_st.nv_agent[2] } +export function nav_agent_vx(nav_st: NavState) -> float { return nav_st.nv_agent[3] } +export function nav_agent_vz(nav_st: NavState) -> float { return nav_st.nv_agent[5] } +# microseconds the crowds have spent stepping +export function nav_crowd_us(nav_st: NavState) -> int { + var us = 0 + for k in 0 .. NAV_KINDS { if nav_st.nv_crowds[k] != null { us += nvc_crowd_us(nav_st.nv_crowds[k]) } } + return us +} diff --git a/packages/ludic.nav/index.ludic b/packages/ludic.nav/index.ludic index 3f380a8d..59dfbfc5 100644 --- a/packages/ludic.nav/index.ludic +++ b/packages/ludic.nav/index.ludic @@ -8,3 +8,4 @@ import "native.ludic" import "state.ludic" import "build.ludic" import "query.ludic" +import "crowd.ludic" diff --git a/packages/ludic.nav/lib/macos-arm64/libludicnav.dylib b/packages/ludic.nav/lib/macos-arm64/libludicnav.dylib index 7d84075a..7314e9b3 100755 --- a/packages/ludic.nav/lib/macos-arm64/libludicnav.dylib +++ b/packages/ludic.nav/lib/macos-arm64/libludicnav.dylib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:969c1ee0010c0fe88abcdfceb1a166031a8f6519b9e77a27c27331cbe2036e0b -size 176912 +oid sha256:f373d9a1d4d37d907d7df0869a2ba4e8b2a70de55cb33c6f635cea49486efe2f +size 198240 diff --git a/packages/ludic.nav/lib/windows-x64/ludicnav.dll b/packages/ludic.nav/lib/windows-x64/ludicnav.dll index 0bdc9341..b0c6761f 100644 --- a/packages/ludic.nav/lib/windows-x64/ludicnav.dll +++ b/packages/ludic.nav/lib/windows-x64/ludicnav.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ef49349d93dbc3b94c3743f80fa56f2acf4f5e02f16faba6ec13823b5705aaa -size 337920 +oid sha256:a7626d464f2588054610dc27a655d67acf66649ea120120705cdf5f1fca5fe34 +size 374784 diff --git a/packages/ludic.nav/lib/windows-x64/ludicnav.lib b/packages/ludic.nav/lib/windows-x64/ludicnav.lib index 6bcfe136..32d4c3af 100644 --- a/packages/ludic.nav/lib/windows-x64/ludicnav.lib +++ b/packages/ludic.nav/lib/windows-x64/ludicnav.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ea766bfe59613b41a1566f41de772d49602191ad7e188bef3bcf453c55d6114 -size 4022 +oid sha256:754f4ea0ce90f7b512d9e6e12f59c5babaf1d071dfd8a30faff93bba8af52cee +size 5804 diff --git a/packages/ludic.nav/native.ludic b/packages/ludic.nav/native.ludic index 190af731..7414cfb5 100644 --- a/packages/ludic.nav/native.ludic +++ b/packages/ludic.nav/native.ludic @@ -15,4 +15,13 @@ extern function nvc_us(h: pointer) -> int = "nav_us" extern function nvc_raycast(h: pointer, f: int, sx: float, sy: float, sz: float, ex: float, ez: float) -> int = "nav_raycast" extern function nvc_random_near(h: pointer, f: int, x: float, y: float, z: float, r: float, seed: int, out: pointer) -> int = "nav_random_near" extern function nvc_stage(size: int) -> pointer = "nav_stage" +extern function nvc_crowd_new(h: pointer, max: int, max_radius: float) -> pointer = "nav_crowd_new" +extern function nvc_crowd_free(c: pointer) -> void = "nav_crowd_free" +extern function nvc_crowd_add(c: pointer, x: float, y: float, z: float, radius: float, height: float, speed: float, filter: int) -> int = "nav_crowd_add" +extern function nvc_crowd_remove(c: pointer, i: int) -> void = "nav_crowd_remove" +extern function nvc_crowd_target(c: pointer, i: int, x: float, y: float, z: float) -> int = "nav_crowd_target" +extern function nvc_crowd_speed(c: pointer, i: int, speed: float) -> void = "nav_crowd_speed" +extern function nvc_crowd_update(c: pointer, dt: float) -> void = "nav_crowd_update" +extern function nvc_crowd_read(c: pointer, i: int, out: pointer) -> int = "nav_crowd_read" +extern function nvc_crowd_us(c: pointer) -> int = "nav_crowd_us" extern function nvc_load_staged(buf: pointer, size: int) -> pointer = "nav_load_staged" diff --git a/packages/ludic.nav/native/build.sh b/packages/ludic.nav/native/build.sh index 6bc4323a..bf963d02 100755 --- a/packages/ludic.nav/native/build.sh +++ b/packages/ludic.nav/native/build.sh @@ -1,7 +1,7 @@ #!/bin/sh # builds lib// for ludic.nav: Recast & Detour at a pinned tag, and the shim over them. -# Recast builds the mesh, Detour answers it; DetourTileCache and DetourCrowd come with phases 17.8 -# and 18. Polygon refs are 64-bit (DT_POLYREF64): an 8 km map in 64 m tiles is 16384 tiles, which +# Recast builds the mesh, Detour answers it, DetourCrowd steers many walkers at once (phase 18); +# DetourTileCache would come with 17.8. Polygon refs are 64-bit (DT_POLYREF64): an 8 km map in 64 m tiles is 16384 tiles, which # 32-bit refs would leave 256 polygons each. Objects go to build/native; a few seconds on eight cores. set -eu PKG="$(cd "$(dirname "$0")/.." && pwd)" @@ -14,12 +14,12 @@ native_fetch "$SRC" "https://github.com/recastnavigation/recastnavigation/archiv OBJ="$PKG/build/native/$(native_target)" mkdir -p "$OBJ" CXX="$(native_cxx)" -INC="-I$SRC/Recast/Include -I$SRC/Detour/Include" +INC="-I$SRC/Recast/Include -I$SRC/Detour/Include -I$SRC/DetourCrowd/Include" FLAGS="$(native_cflags) -std=c++17 -ffp-contract=off -fno-exceptions -fno-rtti -DNDEBUG -DDT_POLYREF64 $INC" case "$(native_target)" in windows-*) FLAGS="$FLAGS -D_CRT_SECURE_NO_WARNINGS" ;; esac JOBS="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" -( cd "$SRC" && ls Recast/Source/*.cpp Detour/Source/*.cpp ) | while read -r f; do +( cd "$SRC" && ls Recast/Source/*.cpp Detour/Source/*.cpp DetourCrowd/Source/*.cpp ) | while read -r f; do o="$OBJ/$(basename "$f" .cpp).o" [ "$o" -nt "$SRC/$f" ] || echo "$f $o" done | xargs -P "$JOBS" -n 2 sh -c '"$0" '"$FLAGS"' -c "'"$SRC"'/$1" -o "$2"' "$CXX" diff --git a/packages/ludic.nav/native/shim/nav_crowd.inl b/packages/ludic.nav/native/shim/nav_crowd.inl new file mode 100644 index 00000000..2a81400c --- /dev/null +++ b/packages/ludic.nav/native/shim/nav_crowd.inl @@ -0,0 +1,87 @@ +// nav_crowd.inl - DetourCrowd over a navmesh: many walkers steered along their paths at once, each +// kept clear of the others. The mechanic decides where and how fast; the crowd only moves them. + +struct Crowd { + dtCrowd *crowd = nullptr; + Nav *nav = nullptr; + long long ns = 0; // time spent stepping it, for the measure (18.8) +}; + +// a crowd of at most max walkers, none wider than max_radius, with the mesh's tastes as its filters +NAV_SHIM void *nav_crowd_new(void *h, int max, float max_radius) { + Nav *n = static_cast