#ifndef NAVMESHWALKRANDOM_H #define NAVMESHWALKRANDOM_H #include "../NavMesh.h" #include "../NavMeshLocation.h" #include "../../geo/Heading.h" #include "NavMeshSub.h" #include "NavMeshWalkParams.h" #include "NavMeshWalkEval.h" namespace NM { /** * pick a truely random destination within the reachable area * weight this area (evaluators) * repeat this several times to find a robus destination */ template class NavMeshWalkRandom { private: const NavMesh& mesh; std::vector*> evals; public: struct ResultEntry { NavMeshLocation location; Heading heading; double probability; ResultEntry() : heading(0) {;} }; struct ResultList : std::vector {}; public: /** ctor */ NavMeshWalkRandom(const NavMesh& mesh) : mesh(mesh) { } /** add a new evaluator to the walker */ void addEvaluator(NavMeshWalkEval* eval) { this->evals.push_back(eval); } ResultEntry getOne(const NavMeshWalkParams& params) const { // sanity checks params.check(); ResultEntry res; res.probability = 0; // to-be-walked distance; const float toBeWalkedDist = params.getToBeWalkedDistance(); const float toBeWalkedDistSafe = 1.0 + toBeWalkedDist * 1.1; // construct reachable region const NavMeshSub reachable(params.start, toBeWalkedDistSafe); NavMeshRandom rnd = reachable.getRandom(); NavMeshPotentialWalk pwalk(params); // improve quality (the higher, the better) for (int i = 0; i < 50; ++i) { PERF_REGION(1, "NavMeshWalkRandom::SampleLoop"); // draw a random destination // is this destination within the reachable area? (triangles might be larger!) pwalk.end = rnd.draw(); if (pwalk.end.pos.getDistance(params.start.pos) > toBeWalkedDistSafe) { --i; continue; } // calculate the probability for this destination const double p = eval(pwalk); // better? if (p > res.probability) { res.location = pwalk.end; res.probability = p; } } // destination is known. update the heading res.heading = Heading(params.start.pos.xy(), res.location.pos.xy()); return res; } ResultList getMany(const NavMeshWalkParams& params) const { // sanity checks params.check(); ResultList res; // to-be-walked distance; const float toBeWalkedDist = params.getToBeWalkedDistance(); const float toBeWalkedDistSafe = 1.0 + toBeWalkedDist * 1.1; // construct reachable region const NavMeshSub reachable(params.start, toBeWalkedDistSafe); NavMeshRandom rnd = reachable.getRandom(); NavMeshPotentialWalk pwalk(params); // improve quality (the higher, the better) for (int i = 0; i < 50; ++i) { PERF_REGION(1, "NavMeshWalkRandom::SampleLoop"); pwalk.end = rnd.drawWithin(params.start.pos, toBeWalkedDistSafe); // calculate the probability for this destination const double p = eval(pwalk); ResultEntry re; re.heading = Heading(params.start.pos.xy(), pwalk.end.pos.xy()); re.location = pwalk.end; re.probability = p; res.push_back(re); } return res; } double eval(const NM::NavMeshPotentialWalk& pwalk) const { PERF_REGION(2, "NavMeshWalkRandom::EvalLoop"); double p = 1.0; for (const NavMeshWalkEval* eval : evals) { const double p1 = eval->getProbability(pwalk); p *= p1; } return p; } }; } #endif // NAVMESHWALKRANDOM_H