ludic.nav (18.1): a DetourCrowd per kind of walker
The shim builds DetourCrowd from the same pinned Recast & Detour tag. nav_crowd_start puts a crowd on a kind's mesh, with the mesh's tastes as its filters. Walkers are added (snapped to the mesh), sent and sped through verbs, stepped together, and read back into nav_agent_*. nav_crowd_us times the stepping with the OS clock. Dropping a mesh drops its crowd first. On the test meadow, twenty walkers crossing head-on never come closer than their two radii (0.7003 m), all arrive, and a step costs about 12 us. nav 15/15 on the Mac and the PC; the DLL still imports KERNEL32 alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c2902a2246
commit
521efe67db
12 changed files with 267 additions and 10 deletions
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
# builds lib/<target>/ 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"
|
||||
|
|
|
|||
87
packages/ludic.nav/native/shim/nav_crowd.inl
Normal file
87
packages/ludic.nav/native/shim/nav_crowd.inl
Normal file
|
|
@ -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<Nav *>(h);
|
||||
Crowd *c = new Crowd();
|
||||
c->nav = n;
|
||||
c->crowd = dtAllocCrowd();
|
||||
if (!c->crowd || !c->crowd->init(max, max_radius, n->mesh)) { dtFreeCrowd(c->crowd); delete c; return nullptr; }
|
||||
for (int f = 0; f < NAV_FILTERS && f < DT_CROWD_MAX_QUERY_FILTER_TYPE; ++f) *c->crowd->getEditableFilter(f) = n->filters[f];
|
||||
return c;
|
||||
}
|
||||
NAV_SHIM void nav_crowd_free(void *p) {
|
||||
Crowd *c = static_cast<Crowd *>(p);
|
||||
if (!c) return;
|
||||
dtFreeCrowd(c->crowd);
|
||||
delete c;
|
||||
}
|
||||
|
||||
// a walker at (x, y, z): its index, or -1 when the crowd is full or the point is off the mesh
|
||||
NAV_SHIM int nav_crowd_add(void *p, float x, float y, float z, float radius, float height, float speed, int filter) {
|
||||
Crowd *c = static_cast<Crowd *>(p);
|
||||
dtCrowdAgentParams ap;
|
||||
memset(&ap, 0, sizeof(ap));
|
||||
ap.radius = radius;
|
||||
ap.height = height;
|
||||
ap.maxSpeed = speed;
|
||||
ap.maxAcceleration = speed * 4.0f;
|
||||
ap.collisionQueryRange = radius * 12.0f;
|
||||
ap.pathOptimizationRange = radius * 30.0f;
|
||||
ap.separationWeight = 2.0f;
|
||||
ap.updateFlags = DT_CROWD_ANTICIPATE_TURNS | DT_CROWD_OBSTACLE_AVOIDANCE | DT_CROWD_SEPARATION | DT_CROWD_OPTIMIZE_TOPO | DT_CROWD_OPTIMIZE_VIS;
|
||||
ap.obstacleAvoidanceType = 3;
|
||||
ap.queryFilterType = (unsigned char)(filter >= 0 && filter < NAV_FILTERS ? filter : 0);
|
||||
float pos[3] = {x, y, z}, near[3];
|
||||
dtPolyRef ref = 0;
|
||||
c->nav->query->findNearestPoly(pos, c->nav->ext, c->crowd->getFilter(ap.queryFilterType), &ref, near);
|
||||
if (!ref) return -1;
|
||||
return c->crowd->addAgent(near, &ap);
|
||||
}
|
||||
NAV_SHIM void nav_crowd_remove(void *p, int i) { static_cast<Crowd *>(p)->crowd->removeAgent(i); }
|
||||
|
||||
// send walker i toward (x, y, z), snapped to the mesh; 0 when the point is off it
|
||||
NAV_SHIM int nav_crowd_target(void *p, int i, float x, float y, float z) {
|
||||
Crowd *c = static_cast<Crowd *>(p);
|
||||
const dtCrowdAgent *a = c->crowd->getAgent(i);
|
||||
if (!a || !a->active) return 0;
|
||||
float e[3] = {x, y, z}, near[3];
|
||||
dtPolyRef ref = 0;
|
||||
c->nav->query->findNearestPoly(e, c->nav->ext, c->crowd->getFilter(a->params.queryFilterType), &ref, near);
|
||||
if (!ref) return 0;
|
||||
return c->crowd->requestMoveTarget(i, ref, near) ? 1 : 0;
|
||||
}
|
||||
// a new top speed, as a walker goes from a walk to a run
|
||||
NAV_SHIM void nav_crowd_speed(void *p, int i, float speed) {
|
||||
Crowd *c = static_cast<Crowd *>(p);
|
||||
const dtCrowdAgent *a = c->crowd->getAgent(i);
|
||||
if (!a || !a->active) return;
|
||||
dtCrowdAgentParams ap = a->params;
|
||||
ap.maxSpeed = speed;
|
||||
ap.maxAcceleration = speed * 4.0f;
|
||||
c->crowd->updateAgentParameters(i, &ap);
|
||||
}
|
||||
|
||||
NAV_SHIM void nav_crowd_update(void *p, float dt) {
|
||||
Crowd *c = static_cast<Crowd *>(p);
|
||||
long long t0 = nav_now_ns();
|
||||
c->crowd->update(dt, nullptr);
|
||||
c->ns += nav_now_ns() - t0;
|
||||
}
|
||||
|
||||
// walker i's position and velocity into out[0..5]; 0 when there is no such walker
|
||||
NAV_SHIM int nav_crowd_read(void *p, int i, float *out) {
|
||||
const dtCrowdAgent *a = static_cast<Crowd *>(p)->crowd->getAgent(i);
|
||||
if (!a || !a->active) return 0;
|
||||
dtVcopy(out, a->npos);
|
||||
dtVcopy(out + 3, a->vel);
|
||||
return 1;
|
||||
}
|
||||
NAV_SHIM int nav_crowd_us(void *p) { return static_cast<int>(static_cast<Crowd *>(p)->ns / 1000); }
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include <DetourNavMeshBuilder.h>
|
||||
#include <DetourNavMeshQuery.h>
|
||||
#include <DetourCommon.h>
|
||||
#include <DetourCrowd.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
|
@ -77,3 +78,4 @@ const dtQueryFilter *nav_filter(Nav *n, int f) { return &n->filters[f >= 0 && f
|
|||
#include "nav_build.inl"
|
||||
#include "nav_tiles.inl"
|
||||
#include "nav_query.inl"
|
||||
#include "nav_crowd.inl"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue