Fixed findStraightPath() to return partial results. Fixed main.cpp FPS throttling. Added initial support for path replanning in DetourCrowd.
This commit is contained in:
parent
2c9a3a0b4f
commit
1b6ca5a94a
@ -35,6 +35,7 @@ void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& m
|
||||
void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query);
|
||||
void duDebugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh);
|
||||
void duDebugDrawNavMeshPortals(struct duDebugDraw* dd, const dtNavMesh& mesh);
|
||||
void duDebugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh, const unsigned short polyFlags, const unsigned int col);
|
||||
void duDebugDrawNavMeshPoly(struct duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col);
|
||||
|
||||
void duDebugDrawTileCacheLayer(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch);
|
||||
|
@ -424,6 +424,26 @@ void duDebugDrawNavMeshPortals(duDebugDraw* dd, const dtNavMesh& mesh)
|
||||
}
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh,
|
||||
const unsigned short polyFlags, const unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh.getTile(i);
|
||||
if (!tile->header) continue;
|
||||
dtPolyRef base = mesh.getPolyRefBase(tile);
|
||||
|
||||
for (int j = 0; j < tile->header->polyCount; ++j)
|
||||
{
|
||||
const dtPoly* p = &tile->polys[j];
|
||||
if ((p->flags & polyFlags) == 0) continue;
|
||||
duDebugDrawNavMeshPoly(dd, mesh, base|(dtPolyRef)j, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshPoly(duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
@ -328,7 +328,10 @@ public:
|
||||
/// @returns true if over polygon.
|
||||
dtStatus getPolyHeight(dtPolyRef ref, const float* pos, float* height) const;
|
||||
|
||||
/// Returns true if poly reference ins in closed list.
|
||||
// Returns true if polygon reference points to valid data and passes the filter.
|
||||
bool isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const;
|
||||
|
||||
// Returns true if poly reference ins in closed list.
|
||||
bool isInClosedList(dtPolyRef ref) const;
|
||||
|
||||
class dtNodePool* getNodePool() const { return m_nodePool; }
|
||||
|
@ -1200,8 +1200,14 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
|
||||
// Next portal.
|
||||
if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType)))
|
||||
{
|
||||
// Failed to get portal points, in practice this means that path[i+1] is invalid polygon.
|
||||
// Clamp the end point to path[i], and return the path so far.
|
||||
|
||||
if (dtStatusFailed(closestPointOnPolyBoundary(path[i], endPos, closestEndPos)))
|
||||
{
|
||||
// This should only happen when the first polygon is invalid.
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
dtVcopy(&straightPath[n*3], closestEndPos);
|
||||
if (straightPathFlags)
|
||||
@ -1210,7 +1216,9 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
|
||||
straightPathRefs[n] = path[i];
|
||||
n++;
|
||||
|
||||
return DT_SUCCESS;
|
||||
*straightPathCount = n;
|
||||
|
||||
return DT_SUCCESS | DT_PARTIAL_RESULT | ((n >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
|
||||
}
|
||||
|
||||
// If starting really close the portal, advance.
|
||||
@ -2706,6 +2714,20 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
|
||||
return status;
|
||||
}
|
||||
|
||||
bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const
|
||||
{
|
||||
const dtMeshTile* tile = 0;
|
||||
const dtPoly* poly = 0;
|
||||
dtStatus status = m_nav->getTileAndPolyByRef(ref, &tile, &poly);
|
||||
// If cannot get polygon, assume it does not exists and boundary is invalid.
|
||||
if (dtStatusFailed(status))
|
||||
return false;
|
||||
// If cannot pass filter, assume flags has changed and boundary is invalid.
|
||||
if (!filter->passFilter(ref, tile, poly))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const
|
||||
{
|
||||
if (!m_nodePool) return false;
|
||||
|
@ -38,6 +38,7 @@ struct dtCrowdNeighbour
|
||||
|
||||
enum CrowdAgentState
|
||||
{
|
||||
DT_CROWDAGENT_STATE_INVALID,
|
||||
DT_CROWDAGENT_STATE_WALKING,
|
||||
DT_CROWDAGENT_STATE_OFFMESH,
|
||||
};
|
||||
@ -138,6 +139,7 @@ class dtCrowd
|
||||
|
||||
enum MoveRequestState
|
||||
{
|
||||
MR_TARGET_NONE,
|
||||
MR_TARGET_FAILED,
|
||||
MR_TARGET_VALID,
|
||||
MR_TARGET_REQUESTING,
|
||||
@ -158,6 +160,7 @@ class dtCrowd
|
||||
float apos[3]; ///< Goal adjustment pos
|
||||
dtPolyRef temp[MAX_TEMP_PATH]; ///< Adjusted path to the goal
|
||||
int ntemp;
|
||||
bool replan;
|
||||
};
|
||||
MoveRequest* m_moveRequests;
|
||||
int m_moveRequestCount;
|
||||
@ -166,9 +169,13 @@ class dtCrowd
|
||||
|
||||
void updateTopologyOptimization(dtCrowdAgent** agents, const int nagents, const float dt);
|
||||
void updateMoveRequest(const float dt);
|
||||
void checkPathValidty(dtCrowdAgent** agents, const int nagents, const float dt);
|
||||
|
||||
inline int getAgentIndex(const dtCrowdAgent* agent) const { return agent - m_agents; }
|
||||
|
||||
const MoveRequest* getActiveMoveTarget(const int idx) const;
|
||||
|
||||
bool requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos);
|
||||
|
||||
void purge();
|
||||
|
||||
public:
|
||||
@ -194,6 +201,7 @@ public:
|
||||
void update(const float dt, dtCrowdAgentDebugInfo* debug);
|
||||
|
||||
const dtQueryFilter* getFilter() const { return &m_filter; }
|
||||
dtQueryFilter* getEditableFilter() { return &m_filter; }
|
||||
const float* getQueryExtents() const { return m_ext; }
|
||||
|
||||
inline int getVelocitySampleCount() const { return m_velocitySampleCount; }
|
||||
@ -203,5 +211,8 @@ public:
|
||||
const dtNavMeshQuery* getNavMeshQuery() const { return m_navquery; }
|
||||
};
|
||||
|
||||
dtCrowd* dtAllocCrowd();
|
||||
void dtFreeCrowd(dtCrowd* ptr);
|
||||
|
||||
#endif // CROWDMANAGER_H
|
||||
|
||||
#endif // DETOURCROWD_H
|
||||
|
@ -24,7 +24,8 @@
|
||||
|
||||
class dtLocalBoundary
|
||||
{
|
||||
static const int MAX_SEGS = 8;
|
||||
static const int MAX_LOCAL_SEGS = 8;
|
||||
static const int MAX_LOCAL_POLYS = 16;
|
||||
|
||||
struct Segment
|
||||
{
|
||||
@ -33,9 +34,12 @@ class dtLocalBoundary
|
||||
};
|
||||
|
||||
float m_center[3];
|
||||
Segment m_segs[MAX_SEGS];
|
||||
Segment m_segs[MAX_LOCAL_SEGS];
|
||||
int m_nsegs;
|
||||
|
||||
dtPolyRef m_polys[MAX_LOCAL_POLYS];
|
||||
int m_npolys;
|
||||
|
||||
void addSegment(const float dist, const float* seg);
|
||||
|
||||
public:
|
||||
@ -47,6 +51,8 @@ public:
|
||||
void update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
|
||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
||||
|
||||
bool isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
||||
|
||||
inline const float* getCenter() const { return m_center; }
|
||||
inline int getSegmentCount() const { return m_nsegs; }
|
||||
inline const float* getSegment(int i) const { return m_segs[i].s; }
|
||||
|
@ -51,6 +51,11 @@ public:
|
||||
bool moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
|
||||
float* startPos, float* endPos,
|
||||
dtNavMeshQuery* navquery);
|
||||
|
||||
bool trimInvalidPath(dtPolyRef safeRef, const float* safePos,
|
||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
||||
|
||||
bool isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
||||
|
||||
void movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
||||
void moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
||||
@ -61,6 +66,7 @@ public:
|
||||
inline const float* getTarget() const { return m_target; }
|
||||
|
||||
inline dtPolyRef getFirstPoly() const { return m_npath ? m_path[0] : 0; }
|
||||
inline dtPolyRef getLastPoly() const { return m_npath ? m_path[m_npath-1] : 0; }
|
||||
|
||||
inline const dtPolyRef* getPath() const { return m_path; }
|
||||
inline int getPathCount() const { return m_npath; }
|
||||
|
@ -31,12 +31,31 @@
|
||||
#include "DetourAlloc.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
|
||||
dtCrowd* dtAllocCrowd()
|
||||
{
|
||||
void* mem = dtAlloc(sizeof(dtCrowd), DT_ALLOC_PERM);
|
||||
if (!mem) return 0;
|
||||
return new(mem) dtCrowd;
|
||||
}
|
||||
|
||||
void dtFreeCrowd(dtCrowd* ptr)
|
||||
{
|
||||
if (!ptr) return;
|
||||
ptr->~dtCrowd();
|
||||
dtFree(ptr);
|
||||
}
|
||||
|
||||
|
||||
static const int MAX_ITERS_PER_UPDATE = 10;
|
||||
|
||||
static const int MAX_PATHQUEUE_NODES = 4096;
|
||||
static const int MAX_COMMON_NODES = 512;
|
||||
|
||||
inline float between(const float t, const float t0, const float t1)
|
||||
inline float tween(const float t, const float t0, const float t1)
|
||||
{
|
||||
return dtClamp((t-t0) / (t1-t0), 0.0f, 1.0f);
|
||||
}
|
||||
@ -396,11 +415,6 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
|
||||
float nearest[3];
|
||||
dtPolyRef ref;
|
||||
m_navquery->findNearestPoly(pos, m_ext, &m_filter, &ref, nearest);
|
||||
if (!ref)
|
||||
{
|
||||
// Could not find a location on navmesh.
|
||||
return -1;
|
||||
}
|
||||
|
||||
ag->corridor.reset(ref, nearest);
|
||||
ag->boundary.reset();
|
||||
@ -419,7 +433,11 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
|
||||
ag->t = 0;
|
||||
ag->var = (rand() % 10) / 9.0f;
|
||||
|
||||
ag->state = DT_CROWDAGENT_STATE_WALKING;
|
||||
if (ref)
|
||||
ag->state = DT_CROWDAGENT_STATE_WALKING;
|
||||
else
|
||||
ag->state = DT_CROWDAGENT_STATE_INVALID;
|
||||
|
||||
ag->active = 1;
|
||||
|
||||
return idx;
|
||||
@ -433,6 +451,65 @@ void dtCrowd::removeAgent(const int idx)
|
||||
}
|
||||
}
|
||||
|
||||
const dtCrowd::MoveRequest* dtCrowd::getActiveMoveTarget(const int idx) const
|
||||
{
|
||||
if (idx < 0 || idx > m_maxAgents)
|
||||
return 0;
|
||||
|
||||
MoveRequest* req = 0;
|
||||
// Check if there is existing request and update that instead.
|
||||
for (int i = 0; i < m_moveRequestCount; ++i)
|
||||
{
|
||||
if (m_moveRequests[i].idx == idx)
|
||||
{
|
||||
req = &m_moveRequests[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
bool dtCrowd::requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos)
|
||||
{
|
||||
if (idx < 0 || idx > m_maxAgents)
|
||||
return false;
|
||||
if (!ref)
|
||||
return false;
|
||||
|
||||
MoveRequest* req = 0;
|
||||
// Check if there is existing request and update that instead.
|
||||
for (int i = 0; i < m_moveRequestCount; ++i)
|
||||
{
|
||||
if (m_moveRequests[i].idx == idx)
|
||||
{
|
||||
req = &m_moveRequests[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!req)
|
||||
{
|
||||
if (m_moveRequestCount >= m_maxAgents)
|
||||
return false;
|
||||
req = &m_moveRequests[m_moveRequestCount++];
|
||||
memset(req, 0, sizeof(MoveRequest));
|
||||
}
|
||||
|
||||
// Initialize request.
|
||||
req->idx = idx;
|
||||
req->ref = ref;
|
||||
dtVcopy(req->pos, pos);
|
||||
req->pathqRef = DT_PATHQ_INVALID;
|
||||
req->state = MR_TARGET_REQUESTING;
|
||||
req->replan = true;
|
||||
|
||||
req->temp[0] = ref;
|
||||
req->ntemp = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool dtCrowd::requestMoveTarget(const int idx, dtPolyRef ref, const float* pos)
|
||||
{
|
||||
if (idx < 0 || idx > m_maxAgents)
|
||||
@ -464,6 +541,7 @@ bool dtCrowd::requestMoveTarget(const int idx, dtPolyRef ref, const float* pos)
|
||||
dtVcopy(req->pos, pos);
|
||||
req->pathqRef = DT_PATHQ_INVALID;
|
||||
req->state = MR_TARGET_REQUESTING;
|
||||
req->replan = false;
|
||||
|
||||
req->temp[0] = ref;
|
||||
req->ntemp = 1;
|
||||
@ -471,6 +549,7 @@ bool dtCrowd::requestMoveTarget(const int idx, dtPolyRef ref, const float* pos)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool dtCrowd::adjustMoveTarget(const int idx, dtPolyRef ref, const float* pos)
|
||||
{
|
||||
if (idx < 0 || idx > m_maxAgents)
|
||||
@ -562,26 +641,68 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
|
||||
|
||||
if (req->state == MR_TARGET_REQUESTING)
|
||||
{
|
||||
// Calculate request position.
|
||||
// If there is a lot of latency between requests, it is possible to
|
||||
// project the current position ahead and use raycast to find the actual
|
||||
// location and path.
|
||||
const dtPolyRef* path = ag->corridor.getPath();
|
||||
const int npath = ag->corridor.getPathCount();
|
||||
dtAssert(npath);
|
||||
|
||||
// Here we take the simple approach and set the path to be just the current location.
|
||||
float reqPos[3];
|
||||
dtVcopy(reqPos, ag->corridor.getPos()); // The location of the request
|
||||
dtPolyRef reqPath[8]; // The path to the request location
|
||||
reqPath[0] = path[0];
|
||||
int reqPathCount = 1;
|
||||
|
||||
req->pathqRef = m_pathq.request(reqPath[reqPathCount-1], req->ref, reqPos, req->pos, &m_filter);
|
||||
if (req->pathqRef != DT_PATHQ_INVALID)
|
||||
// If agent location is invalid, try to recover.
|
||||
if (ag->state == DT_CROWDAGENT_STATE_INVALID)
|
||||
{
|
||||
ag->corridor.setCorridor(reqPos, reqPath, reqPathCount);
|
||||
req->state = MR_TARGET_WAITING_FOR_PATH;
|
||||
float agentPos[3];
|
||||
dtVcopy(agentPos, ag->npos);
|
||||
dtPolyRef agentRef = ag->corridor.getFirstPoly();
|
||||
if (!m_navquery->isValidPolyRef(agentRef, &m_filter))
|
||||
{
|
||||
// Current location is not valid, try to reposition.
|
||||
// TODO: this can snap agents, how to handle that?
|
||||
float nearest[3];
|
||||
agentRef = 0;
|
||||
m_navquery->findNearestPoly(ag->npos, m_ext, &m_filter, &agentRef, nearest);
|
||||
dtVcopy(agentPos, nearest);
|
||||
}
|
||||
if (!agentRef)
|
||||
{
|
||||
// Current location still outside navmesh, fail the request.
|
||||
req->state = MR_TARGET_FAILED;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Could relocation agent on navmesh again.
|
||||
ag->state = DT_CROWDAGENT_STATE_WALKING;
|
||||
ag->corridor.reset(agentRef, agentPos);
|
||||
}
|
||||
|
||||
// Calculate request position.
|
||||
|
||||
if (req->replan)
|
||||
{
|
||||
// Replan from the end of the current path.
|
||||
dtPolyRef reqRef = ag->corridor.getLastPoly();
|
||||
float reqPos[3];
|
||||
dtVcopy(reqPos, ag->corridor.getTarget());
|
||||
req->pathqRef = m_pathq.request(reqRef, req->ref, reqPos, req->pos, &m_filter);
|
||||
if (req->pathqRef != DT_PATHQ_INVALID)
|
||||
{
|
||||
req->state = MR_TARGET_WAITING_FOR_PATH;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is a lot of latency between requests, it is possible to
|
||||
// project the current position ahead and use raycast to find the actual
|
||||
// location and path.
|
||||
const dtPolyRef* path = ag->corridor.getPath();
|
||||
const int npath = ag->corridor.getPathCount();
|
||||
dtAssert(npath);
|
||||
// Here we take the simple approach and set the path to be just the current location.
|
||||
float reqPos[3];
|
||||
dtVcopy(reqPos, ag->corridor.getPos()); // The location of the request
|
||||
dtPolyRef reqPath[8]; // The path to the request location
|
||||
reqPath[0] = path[0];
|
||||
int reqPathCount = 1;
|
||||
|
||||
req->pathqRef = m_pathq.request(reqPath[reqPathCount-1], req->ref, reqPos, req->pos, &m_filter);
|
||||
if (req->pathqRef != DT_PATHQ_INVALID)
|
||||
{
|
||||
ag->corridor.setCorridor(reqPos, reqPath, reqPathCount);
|
||||
req->state = MR_TARGET_WAITING_FOR_PATH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -647,10 +768,26 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
|
||||
// Make space for the old path.
|
||||
if ((npath-1)+nres > m_maxPathResult)
|
||||
nres = m_maxPathResult - (npath-1);
|
||||
|
||||
memmove(res+npath-1, res, sizeof(dtPolyRef)*nres);
|
||||
// Copy old path in the beginning.
|
||||
memcpy(res, path, sizeof(dtPolyRef)*(npath-1));
|
||||
nres += npath-1;
|
||||
|
||||
// Remove trackbacks
|
||||
for (int j = 0; j < nres; ++j)
|
||||
{
|
||||
if (j-1 >= 0 && j+1 < nres)
|
||||
{
|
||||
if (res[j-1] == res[j+1])
|
||||
{
|
||||
memmove(res+(j-1), res+(j+1), sizeof(dtPolyRef)*(nres-(j+1)));
|
||||
nres -= 2;
|
||||
j -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check for partial path.
|
||||
@ -667,7 +804,10 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
|
||||
|
||||
if (valid)
|
||||
{
|
||||
// Set current corridor.
|
||||
ag->corridor.setCorridor(targetPos, res, nres);
|
||||
// Force to update boundary.
|
||||
ag->boundary.reset();
|
||||
req->state = MR_TARGET_VALID;
|
||||
}
|
||||
else
|
||||
@ -741,7 +881,7 @@ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagent
|
||||
for (int i = 0; i < nagents; ++i)
|
||||
{
|
||||
dtCrowdAgent* ag = agents[i];
|
||||
if (ag->state == DT_CROWDAGENT_STATE_OFFMESH)
|
||||
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
|
||||
continue;
|
||||
if ((ag->params.updateFlags & DT_CROWD_OPTIMIZE_TOPO) == 0)
|
||||
continue;
|
||||
@ -759,6 +899,101 @@ void dtCrowd::updateTopologyOptimization(dtCrowdAgent** agents, const int nagent
|
||||
|
||||
}
|
||||
|
||||
void dtCrowd::checkPathValidty(dtCrowdAgent** agents, const int nagents, const float dt)
|
||||
{
|
||||
static const int CHECK_LOOKAHEAD = 10;
|
||||
|
||||
for (int i = 0; i < nagents; ++i)
|
||||
{
|
||||
dtCrowdAgent* ag = agents[i];
|
||||
|
||||
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
|
||||
continue;
|
||||
|
||||
// Skip if the corridor is valid
|
||||
if (ag->corridor.isValid(CHECK_LOOKAHEAD, m_navquery, &m_filter))
|
||||
continue;
|
||||
|
||||
// The current path is bad, try to recover.
|
||||
|
||||
// Store current state.
|
||||
float agentPos[3];
|
||||
dtPolyRef agentRef = 0;
|
||||
dtVcopy(agentPos, ag->npos);
|
||||
agentRef = ag->corridor.getFirstPoly();
|
||||
|
||||
float targetPos[3];
|
||||
dtPolyRef targetRef = 0;
|
||||
dtVcopy(targetPos, ag->corridor.getTarget());
|
||||
targetRef = ag->corridor.getLastPoly();
|
||||
// If has active move target, get target location from that instead.
|
||||
const int idx = getAgentIndex(ag);
|
||||
const MoveRequest* req = getActiveMoveTarget(idx);
|
||||
if (req)
|
||||
{
|
||||
if (req->state == MR_TARGET_REQUESTING ||
|
||||
req->state == MR_TARGET_WAITING_FOR_PATH ||
|
||||
req->state == MR_TARGET_VALID)
|
||||
{
|
||||
targetRef = req->ref;
|
||||
dtVcopy(targetPos, req->pos);
|
||||
}
|
||||
else if (req->state == MR_TARGET_ADJUST)
|
||||
{
|
||||
targetRef = req->aref;
|
||||
dtVcopy(targetPos, req->apos);
|
||||
}
|
||||
}
|
||||
|
||||
// First check that the current location is valid.
|
||||
if (!m_navquery->isValidPolyRef(agentRef, &m_filter))
|
||||
{
|
||||
// Current location is not valid, try to reposition.
|
||||
// TODO: this can snap agents, how to handle that?
|
||||
float nearest[3];
|
||||
agentRef = 0;
|
||||
m_navquery->findNearestPoly(ag->npos, m_ext, &m_filter, &agentRef, nearest);
|
||||
dtVcopy(agentPos, nearest);
|
||||
}
|
||||
|
||||
if (!agentRef)
|
||||
{
|
||||
// Count not find location in navmesh, set state to invalid.
|
||||
ag->corridor.reset(0, agentPos);
|
||||
ag->boundary.reset();
|
||||
ag->state = DT_CROWDAGENT_STATE_INVALID;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: make temp path by raycasting towards current velocity.
|
||||
|
||||
ag->corridor.trimInvalidPath(agentRef, agentPos, m_navquery, &m_filter);
|
||||
ag->boundary.reset();
|
||||
dtVcopy(ag->npos, agentPos);
|
||||
|
||||
|
||||
// Check that target is still reachable.
|
||||
if (!m_navquery->isValidPolyRef(targetRef, &m_filter))
|
||||
{
|
||||
// Current target is not valid, try to reposition.
|
||||
float nearest[3];
|
||||
targetRef = 0;
|
||||
m_navquery->findNearestPoly(targetPos, m_ext, &m_filter, &targetRef, nearest);
|
||||
dtVcopy(targetPos, nearest);
|
||||
}
|
||||
|
||||
if (!targetRef)
|
||||
{
|
||||
// Target not reachable anymore, cannot replan.
|
||||
// TODO: indicate failure!
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to replan path to goal.
|
||||
requestMoveTargetReplan(idx, targetRef, targetPos);
|
||||
}
|
||||
}
|
||||
|
||||
void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
|
||||
{
|
||||
m_velocitySampleCount = 0;
|
||||
@ -768,6 +1003,9 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
|
||||
dtCrowdAgent** agents = m_activeAgents;
|
||||
int nagents = getActiveAgents(agents, m_maxAgents);
|
||||
|
||||
// Check that all agents still have valid paths.
|
||||
checkPathValidty(agents, nagents, dt);
|
||||
|
||||
// Update async move request and path finder.
|
||||
updateMoveRequest(dt);
|
||||
|
||||
@ -788,12 +1026,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
|
||||
for (int i = 0; i < nagents; ++i)
|
||||
{
|
||||
dtCrowdAgent* ag = agents[i];
|
||||
if (ag->state == DT_CROWDAGENT_STATE_OFFMESH)
|
||||
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
|
||||
continue;
|
||||
|
||||
// Only update the collision boundary after certain distance has been passed.
|
||||
// Update the collision boundary after certain distance has been passed or
|
||||
// if it has become invalid.
|
||||
const float updateThr = ag->params.collisionQueryRange*0.25f;
|
||||
if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr))
|
||||
if (dtVdist2DSqr(ag->npos, ag->boundary.getCenter()) > dtSqr(updateThr) ||
|
||||
!ag->boundary.isValid(m_navquery, &m_filter))
|
||||
{
|
||||
ag->boundary.update(ag->corridor.getFirstPoly(), ag->npos, ag->params.collisionQueryRange,
|
||||
m_navquery, &m_filter);
|
||||
@ -809,7 +1049,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
|
||||
{
|
||||
dtCrowdAgent* ag = agents[i];
|
||||
|
||||
if (ag->state == DT_CROWDAGENT_STATE_OFFMESH)
|
||||
if (ag->state != DT_CROWDAGENT_STATE_WALKING)
|
||||
continue;
|
||||
|
||||
// Find corners for steering
|
||||
@ -829,7 +1069,6 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
|
||||
dtVcopy(debug->optStart, ag->corridor.getPos());
|
||||
dtVcopy(debug->optEnd, target);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1119,12 +1358,12 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
|
||||
const float tb = anim->tmax;
|
||||
if (anim->t < ta)
|
||||
{
|
||||
const float u = between(anim->t, 0.0, ta);
|
||||
const float u = tween(anim->t, 0.0, ta);
|
||||
dtVlerp(ag->npos, anim->initPos, anim->startPos, u);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float u = between(anim->t, ta, tb);
|
||||
const float u = tween(anim->t, ta, tb);
|
||||
dtVlerp(ag->npos, anim->startPos, anim->endPos, u);
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,8 @@
|
||||
|
||||
|
||||
dtLocalBoundary::dtLocalBoundary() :
|
||||
m_nsegs(0)
|
||||
m_nsegs(0),
|
||||
m_npolys(0)
|
||||
{
|
||||
dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
|
||||
}
|
||||
@ -37,6 +38,7 @@ dtLocalBoundary::~dtLocalBoundary()
|
||||
void dtLocalBoundary::reset()
|
||||
{
|
||||
dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
|
||||
m_npolys = 0;
|
||||
m_nsegs = 0;
|
||||
}
|
||||
|
||||
@ -52,7 +54,7 @@ void dtLocalBoundary::addSegment(const float dist, const float* s)
|
||||
else if (dist >= m_segs[m_nsegs-1].d)
|
||||
{
|
||||
// Further than the last segment, skip.
|
||||
if (m_nsegs >= MAX_SEGS)
|
||||
if (m_nsegs >= MAX_LOCAL_SEGS)
|
||||
return;
|
||||
// Last, trivial accept.
|
||||
seg = &m_segs[m_nsegs];
|
||||
@ -65,8 +67,8 @@ void dtLocalBoundary::addSegment(const float dist, const float* s)
|
||||
if (dist <= m_segs[i].d)
|
||||
break;
|
||||
const int tgt = i+1;
|
||||
const int n = dtMin(m_nsegs-i, MAX_SEGS-tgt);
|
||||
dtAssert(tgt+n <= MAX_SEGS);
|
||||
const int n = dtMin(m_nsegs-i, MAX_LOCAL_SEGS-tgt);
|
||||
dtAssert(tgt+n <= MAX_LOCAL_SEGS);
|
||||
if (n > 0)
|
||||
memmove(&m_segs[tgt], &m_segs[i], sizeof(Segment)*n);
|
||||
seg = &m_segs[i];
|
||||
@ -75,38 +77,36 @@ void dtLocalBoundary::addSegment(const float dist, const float* s)
|
||||
seg->d = dist;
|
||||
memcpy(seg->s, s, sizeof(float)*6);
|
||||
|
||||
if (m_nsegs < MAX_SEGS)
|
||||
if (m_nsegs < MAX_LOCAL_SEGS)
|
||||
m_nsegs++;
|
||||
}
|
||||
|
||||
void dtLocalBoundary::update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
|
||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
||||
{
|
||||
static const int MAX_LOCAL_POLYS = 16;
|
||||
static const int MAX_SEGS_PER_POLY = DT_VERTS_PER_POLYGON*3;
|
||||
|
||||
if (!ref)
|
||||
{
|
||||
dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
|
||||
m_nsegs = 0;
|
||||
m_npolys = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dtVcopy(m_center, pos);
|
||||
|
||||
// First query non-overlapping polygons.
|
||||
dtPolyRef locals[MAX_LOCAL_POLYS];
|
||||
int nlocals = 0;
|
||||
navquery->findLocalNeighbourhood(ref, pos, collisionQueryRange,
|
||||
filter, locals, 0, &nlocals, MAX_LOCAL_POLYS);
|
||||
filter, m_polys, 0, &m_npolys, MAX_LOCAL_POLYS);
|
||||
|
||||
// Secondly, store all polygon edges.
|
||||
m_nsegs = 0;
|
||||
float segs[MAX_SEGS_PER_POLY*6];
|
||||
int nsegs = 0;
|
||||
for (int j = 0; j < nlocals; ++j)
|
||||
for (int j = 0; j < m_npolys; ++j)
|
||||
{
|
||||
navquery->getPolyWallSegments(locals[j], filter, segs, 0, &nsegs, MAX_SEGS_PER_POLY);
|
||||
navquery->getPolyWallSegments(m_polys[j], filter, segs, 0, &nsegs, MAX_SEGS_PER_POLY);
|
||||
for (int k = 0; k < nsegs; ++k)
|
||||
{
|
||||
const float* s = &segs[k*6];
|
||||
@ -119,3 +119,19 @@ void dtLocalBoundary::update(dtPolyRef ref, const float* pos, const float collis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool dtLocalBoundary::isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
||||
{
|
||||
if (!m_npolys)
|
||||
return false;
|
||||
|
||||
// Check that all polygons still pass query filter.
|
||||
for (int i = 0; i < m_npolys; ++i)
|
||||
{
|
||||
if (!navquery->isValidPolyRef(m_polys[i], filter))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,7 @@ int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int max
|
||||
return req+size;
|
||||
}
|
||||
|
||||
|
||||
dtPathCorridor::dtPathCorridor() :
|
||||
m_path(0),
|
||||
m_npath(0),
|
||||
@ -263,6 +264,8 @@ void dtPathCorridor::optimizePathVisibility(const float* next, const float pathO
|
||||
|
||||
bool dtPathCorridor::optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
||||
{
|
||||
dtAssert(navquery);
|
||||
dtAssert(filter);
|
||||
dtAssert(m_path);
|
||||
|
||||
if (m_npath < 3)
|
||||
@ -384,3 +387,55 @@ void dtPathCorridor::setCorridor(const float* target, const dtPolyRef* path, con
|
||||
memcpy(m_path, path, sizeof(dtPolyRef)*npath);
|
||||
m_npath = npath;
|
||||
}
|
||||
|
||||
bool dtPathCorridor::trimInvalidPath(dtPolyRef safeRef, const float* safePos,
|
||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
||||
{
|
||||
dtAssert(navquery);
|
||||
dtAssert(filter);
|
||||
dtAssert(m_path);
|
||||
|
||||
// Keep valid path as far as possible.
|
||||
int n = 0;
|
||||
while (n < m_npath && navquery->isValidPolyRef(m_path[n], filter)) {
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n == m_npath)
|
||||
{
|
||||
// All valid, no need to fix.
|
||||
return true;
|
||||
}
|
||||
else if (n == 0)
|
||||
{
|
||||
// The first polyref is bad, use current safe values.
|
||||
dtVcopy(m_pos, safePos);
|
||||
m_path[0] = safeRef;
|
||||
m_npath = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The path is partially usable.
|
||||
m_npath = n;
|
||||
}
|
||||
|
||||
// Clamp target pos to last poly
|
||||
float tgt[3];
|
||||
dtVcopy(tgt, m_target);
|
||||
navquery->closestPointOnPolyBoundary(m_path[m_npath-1], tgt, m_target);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtPathCorridor::isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
||||
{
|
||||
// Check that all polygons still pass query filter.
|
||||
const int n = dtMin(m_npath, maxLookAhead);
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
if (!navquery->isValidPolyRef(m_path[i], filter))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
Binary file not shown.
@ -35,7 +35,6 @@ class CrowdTool : public SampleTool
|
||||
float m_targetPos[3];
|
||||
dtPolyRef m_targetRef;
|
||||
|
||||
|
||||
bool m_expandSelectedDebugDraw;
|
||||
bool m_showCorners;
|
||||
bool m_showCollisionSegments;
|
||||
@ -73,16 +72,16 @@ class CrowdTool : public SampleTool
|
||||
};
|
||||
AgentTrail m_trails[MAX_AGENTS];
|
||||
|
||||
dtCrowd m_crowd;
|
||||
|
||||
ValueHistory m_crowdTotalTime;
|
||||
ValueHistory m_crowdSampleCount;
|
||||
|
||||
|
||||
enum ToolMode
|
||||
{
|
||||
TOOLMODE_CREATE,
|
||||
TOOLMODE_MOVE_TARGET,
|
||||
TOOLMODE_SELECT,
|
||||
TOOLMODE_TOGGLE_POLYS,
|
||||
};
|
||||
ToolMode m_mode;
|
||||
|
||||
|
@ -49,11 +49,12 @@ enum SamplePolyAreas
|
||||
};
|
||||
enum SamplePolyFlags
|
||||
{
|
||||
SAMPLE_POLYFLAGS_WALK = 0x01, ///< Ability to walk (ground, grass, road)
|
||||
SAMPLE_POLYFLAGS_SWIM = 0x02, ///< Ability to swim (water).
|
||||
SAMPLE_POLYFLAGS_DOOR = 0x04, ///< Ability to move through doors.
|
||||
SAMPLE_POLYFLAGS_JUMP = 0x08, ///< Ability to jump.
|
||||
SAMPLE_POLYFLAGS_ALL = 0xffff ///< All abilities.
|
||||
SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
|
||||
SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
|
||||
SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
|
||||
SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
|
||||
SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
|
||||
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
|
||||
};
|
||||
|
||||
struct SampleTool
|
||||
@ -78,6 +79,8 @@ protected:
|
||||
class InputGeom* m_geom;
|
||||
class dtNavMesh* m_navMesh;
|
||||
class dtNavMeshQuery* m_navQuery;
|
||||
class dtCrowd* m_crowd;
|
||||
|
||||
unsigned char m_navMeshDrawFlags;
|
||||
|
||||
float m_cellSize;
|
||||
@ -122,6 +125,7 @@ public:
|
||||
virtual class InputGeom* getInputGeom() { return m_geom; }
|
||||
virtual class dtNavMesh* getNavMesh() { return m_navMesh; }
|
||||
virtual class dtNavMeshQuery* getNavMeshQuery() { return m_navQuery; }
|
||||
virtual class dtCrowd* getCrowd() { return m_crowd; }
|
||||
virtual float getAgentRadius() { return m_agentRadius; }
|
||||
virtual float getAgentHeight() { return m_agentHeight; }
|
||||
virtual float getAgentClimb() { return m_agentMaxClimb; }
|
||||
|
@ -147,42 +147,47 @@ void CrowdTool::init(Sample* sample)
|
||||
}
|
||||
|
||||
dtNavMesh* nav = m_sample->getNavMesh();
|
||||
if (nav)
|
||||
dtCrowd* crowd = m_sample->getCrowd();
|
||||
if (nav && crowd && crowd->getAgentCount() == 0)
|
||||
{
|
||||
m_crowd.init(MAX_AGENTS, m_sample->getAgentRadius(), nav);
|
||||
crowd->init(MAX_AGENTS, m_sample->getAgentRadius(), nav);
|
||||
|
||||
// Make polygons with 'disabled' flag invalid.
|
||||
crowd->getEditableFilter()->setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
|
||||
|
||||
// Setup local avoidance params to different qualities.
|
||||
dtObstacleAvoidanceParams params;
|
||||
// Use mostly default settings, copy from dtCrowd.
|
||||
memcpy(¶ms, m_crowd.getObstacleAvoidanceParams(0), sizeof(dtObstacleAvoidanceParams));
|
||||
memcpy(¶ms, crowd->getObstacleAvoidanceParams(0), sizeof(dtObstacleAvoidanceParams));
|
||||
|
||||
// Low (11)
|
||||
params.velBias = 0.5f;
|
||||
params.adaptiveDivs = 5;
|
||||
params.adaptiveRings = 2;
|
||||
params.adaptiveDepth = 1;
|
||||
m_crowd.setObstacleAvoidanceParams(0, ¶ms);
|
||||
crowd->setObstacleAvoidanceParams(0, ¶ms);
|
||||
|
||||
// Medium (22)
|
||||
params.velBias = 0.5f;
|
||||
params.adaptiveDivs = 5;
|
||||
params.adaptiveRings = 2;
|
||||
params.adaptiveDepth = 2;
|
||||
m_crowd.setObstacleAvoidanceParams(1, ¶ms);
|
||||
crowd->setObstacleAvoidanceParams(1, ¶ms);
|
||||
|
||||
// Good (45)
|
||||
params.velBias = 0.5f;
|
||||
params.adaptiveDivs = 7;
|
||||
params.adaptiveRings = 2;
|
||||
params.adaptiveDepth = 3;
|
||||
m_crowd.setObstacleAvoidanceParams(2, ¶ms);
|
||||
crowd->setObstacleAvoidanceParams(2, ¶ms);
|
||||
|
||||
// High (66)
|
||||
params.velBias = 0.5f;
|
||||
params.adaptiveDivs = 7;
|
||||
params.adaptiveRings = 3;
|
||||
params.adaptiveDepth = 3;
|
||||
m_crowd.setObstacleAvoidanceParams(3, ¶ms);
|
||||
|
||||
crowd->setObstacleAvoidanceParams(3, ¶ms);
|
||||
}
|
||||
|
||||
}
|
||||
@ -201,6 +206,8 @@ void CrowdTool::handleMenu()
|
||||
m_mode = TOOLMODE_MOVE_TARGET;
|
||||
if (imguiCheck("Select Agent", m_mode == TOOLMODE_SELECT))
|
||||
m_mode = TOOLMODE_SELECT;
|
||||
if (imguiCheck("Toggle Polys", m_mode == TOOLMODE_TOGGLE_POLYS))
|
||||
m_mode = TOOLMODE_TOGGLE_POLYS;
|
||||
|
||||
imguiSeparatorLine();
|
||||
|
||||
@ -291,6 +298,8 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
if (!m_sample) return;
|
||||
InputGeom* geom = m_sample->getInputGeom();
|
||||
if (!geom) return;
|
||||
dtCrowd* crowd = m_sample->getCrowd();
|
||||
if (!crowd) return;
|
||||
|
||||
if (m_mode == TOOLMODE_CREATE)
|
||||
{
|
||||
@ -300,9 +309,9 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
int isel = -1;
|
||||
float tsel = FLT_MAX;
|
||||
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
float bmin[3], bmax[3];
|
||||
getAgentBounds(ag, bmin, bmax);
|
||||
@ -318,7 +327,7 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
}
|
||||
if (isel != -1)
|
||||
{
|
||||
m_crowd.removeAgent(isel);
|
||||
crowd->removeAgent(isel);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -330,7 +339,7 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
ap.height = m_sample->getAgentHeight();
|
||||
ap.maxAcceleration = 8.0f;
|
||||
ap.maxSpeed = 3.5f;
|
||||
ap.collisionQueryRange = ap.radius * 8.0f;
|
||||
ap.collisionQueryRange = ap.radius * 12.0f;
|
||||
ap.pathOptimizationRange = ap.radius * 30.0f;
|
||||
ap.updateFlags = 0;
|
||||
if (m_anticipateTurns)
|
||||
@ -346,11 +355,11 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
ap.obstacleAvoidanceType = (unsigned char)m_obstacleAvoidanceType;
|
||||
ap.separationWeight = m_separationWeight;
|
||||
|
||||
int idx = m_crowd.addAgent(p, &ap);
|
||||
int idx = crowd->addAgent(p, &ap);
|
||||
if (idx != -1)
|
||||
{
|
||||
if (m_targetRef)
|
||||
m_crowd.requestMoveTarget(idx, m_targetRef, m_targetPos);
|
||||
crowd->requestMoveTarget(idx, m_targetRef, m_targetPos);
|
||||
|
||||
// Init trail
|
||||
AgentTrail* trail = &m_trails[idx];
|
||||
@ -364,29 +373,47 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
{
|
||||
// Find nearest point on navmesh and set move request to that location.
|
||||
dtNavMeshQuery* navquery = m_sample->getNavMeshQuery();
|
||||
const dtQueryFilter* filter = m_crowd.getFilter();
|
||||
const float* ext = m_crowd.getQueryExtents();
|
||||
const dtQueryFilter* filter = crowd->getFilter();
|
||||
const float* ext = crowd->getQueryExtents();
|
||||
|
||||
navquery->findNearestPoly(p, ext, filter, &m_targetRef, m_targetPos);
|
||||
|
||||
if (shift)
|
||||
{
|
||||
// Adjust target using tiny local search.
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
if (m_agentDebug.idx != -1)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
m_crowd.adjustMoveTarget(i, m_targetRef, m_targetPos);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(m_agentDebug.idx);
|
||||
if (ag && ag->active)
|
||||
crowd->adjustMoveTarget(m_agentDebug.idx, m_targetRef, m_targetPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
crowd->adjustMoveTarget(i, m_targetRef, m_targetPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move target using paht finder
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
if (m_agentDebug.idx != -1)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
m_crowd.requestMoveTarget(i, m_targetRef, m_targetPos);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(m_agentDebug.idx);
|
||||
if (ag && ag->active)
|
||||
crowd->requestMoveTarget(m_agentDebug.idx, m_targetRef, m_targetPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
crowd->requestMoveTarget(i, m_targetRef, m_targetPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,9 +423,9 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
m_agentDebug.idx = -1;
|
||||
|
||||
float tsel = FLT_MAX;
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
float bmin[3], bmax[3];
|
||||
getAgentBounds(ag, bmin, bmax);
|
||||
@ -413,10 +440,36 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_mode == TOOLMODE_TOGGLE_POLYS)
|
||||
{
|
||||
dtNavMesh* nav = m_sample->getNavMesh();
|
||||
dtNavMeshQuery* navquery = m_sample->getNavMeshQuery();
|
||||
if (nav && navquery)
|
||||
{
|
||||
dtQueryFilter filter;
|
||||
const float* ext = crowd->getQueryExtents();
|
||||
float tgt[3];
|
||||
dtPolyRef ref;
|
||||
navquery->findNearestPoly(p, ext, &filter, &ref, tgt);
|
||||
if (ref)
|
||||
{
|
||||
unsigned short flags = 0;
|
||||
if (dtStatusSucceed(nav->getPolyFlags(ref, &flags)))
|
||||
{
|
||||
flags ^= SAMPLE_POLYFLAGS_DISABLED;
|
||||
nav->setPolyFlags(ref, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CrowdTool::updateAgentParams()
|
||||
{
|
||||
dtCrowd* crowd = m_sample->getCrowd();
|
||||
if (!crowd) return;
|
||||
|
||||
unsigned char updateFlags = 0;
|
||||
unsigned char obstacleAvoidanceType = 0;
|
||||
|
||||
@ -437,34 +490,34 @@ void CrowdTool::updateAgentParams()
|
||||
|
||||
dtCrowdAgentParams params;
|
||||
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
memcpy(¶ms, &ag->params, sizeof(dtCrowdAgentParams));
|
||||
params.updateFlags = updateFlags;
|
||||
params.obstacleAvoidanceType = obstacleAvoidanceType;
|
||||
params.separationWeight = m_separationWeight;
|
||||
m_crowd.updateAgentParameters(i, ¶ms);
|
||||
crowd->updateAgentParameters(i, ¶ms);
|
||||
}
|
||||
}
|
||||
|
||||
void CrowdTool::updateTick(const float dt)
|
||||
{
|
||||
dtNavMesh* nav = m_sample->getNavMesh();
|
||||
if (!nav)
|
||||
return;
|
||||
dtCrowd* crowd = m_sample->getCrowd();
|
||||
if (!nav || !crowd) return;
|
||||
|
||||
TimeVal startTime = getPerfTime();
|
||||
|
||||
m_crowd.update(dt, &m_agentDebug);
|
||||
crowd->update(dt, &m_agentDebug);
|
||||
|
||||
TimeVal endTime = getPerfTime();
|
||||
|
||||
// Update agent trails
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
AgentTrail* trail = &m_trails[i];
|
||||
if (!ag->active)
|
||||
continue;
|
||||
@ -475,7 +528,7 @@ void CrowdTool::updateTick(const float dt)
|
||||
|
||||
m_agentDebug.vod->normalizeSamples();
|
||||
|
||||
m_crowdSampleCount.addSample((float)m_crowd.getVelocitySampleCount());
|
||||
m_crowdSampleCount.addSample((float)crowd->getVelocitySampleCount());
|
||||
m_crowdTotalTime.addSample(getPerfDeltaTimeUsec(startTime, endTime) / 1000.0f);
|
||||
}
|
||||
|
||||
@ -503,13 +556,16 @@ void CrowdTool::handleRender()
|
||||
DebugDrawGL dd;
|
||||
const float s = m_sample->getAgentRadius();
|
||||
|
||||
dtNavMesh* nmesh = m_sample->getNavMesh();
|
||||
if (!nmesh)
|
||||
dtNavMesh* nav = m_sample->getNavMesh();
|
||||
dtCrowd* crowd = m_sample->getCrowd();
|
||||
if (!nav || !crowd)
|
||||
return;
|
||||
|
||||
if (m_showNodes && m_crowd.getPathQueue())
|
||||
duDebugDrawNavMeshPolysWithFlags(&dd, *nav, SAMPLE_POLYFLAGS_DISABLED, duRGBA(220,0,0,128));
|
||||
|
||||
if (m_showNodes && crowd->getPathQueue())
|
||||
{
|
||||
const dtNavMeshQuery* navquery = m_crowd.getPathQueue()->getNavQuery();
|
||||
const dtNavMeshQuery* navquery = crowd->getPathQueue()->getNavQuery();
|
||||
if (navquery)
|
||||
duDebugDrawNavMeshNodes(&dd, *navquery);
|
||||
}
|
||||
@ -521,14 +577,14 @@ void CrowdTool::handleRender()
|
||||
{
|
||||
if (m_agentDebug.idx != -1)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(m_agentDebug.idx);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(m_agentDebug.idx);
|
||||
if (ag->active)
|
||||
{
|
||||
|
||||
const dtPolyRef* path = ag->corridor.getPath();
|
||||
const int npath = ag->corridor.getPathCount();
|
||||
for (int i = 0; i < npath; ++i)
|
||||
duDebugDrawNavMeshPoly(&dd, *nmesh, path[i], duRGBA(0,0,0,16));
|
||||
duDebugDrawNavMeshPoly(&dd, *nav, path[i], duRGBA(0,0,0,16));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -540,9 +596,9 @@ void CrowdTool::handleRender()
|
||||
if (m_showGrid)
|
||||
{
|
||||
float gridy = -FLT_MAX;
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
const float* pos = ag->corridor.getPos();
|
||||
gridy = dtMax(gridy, pos[1]);
|
||||
@ -550,7 +606,7 @@ void CrowdTool::handleRender()
|
||||
gridy += 1.0f;
|
||||
|
||||
dd.begin(DU_DRAW_QUADS);
|
||||
const dtProximityGrid* grid = m_crowd.getGrid();
|
||||
const dtProximityGrid* grid = crowd->getGrid();
|
||||
const int* bounds = grid->getBounds();
|
||||
const float cs = grid->getCellSize();
|
||||
for (int y = bounds[1]; y <= bounds[3]; ++y)
|
||||
@ -570,9 +626,9 @@ void CrowdTool::handleRender()
|
||||
}
|
||||
|
||||
// Trail
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
|
||||
const AgentTrail* trail = &m_trails[i];
|
||||
@ -598,7 +654,7 @@ void CrowdTool::handleRender()
|
||||
// Corners & co
|
||||
if (m_agentDebug.idx != -1)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(m_agentDebug.idx);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(m_agentDebug.idx);
|
||||
if (ag->active)
|
||||
{
|
||||
|
||||
@ -684,13 +740,13 @@ void CrowdTool::handleRender()
|
||||
// TODO: fix this properly.
|
||||
int n = ag->neis[j].idx;
|
||||
const dtCrowdAgent* nei = 0;
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
if (n == 0)
|
||||
{
|
||||
nei = m_crowd.getAgent(ag->neis[j].idx);
|
||||
nei = crowd->getAgent(ag->neis[j].idx);
|
||||
break;
|
||||
}
|
||||
n--;
|
||||
@ -715,9 +771,9 @@ void CrowdTool::handleRender()
|
||||
}
|
||||
|
||||
// Agent cylinders.
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
|
||||
const float radius = ag->params.radius;
|
||||
@ -726,9 +782,9 @@ void CrowdTool::handleRender()
|
||||
duDebugDrawCircle(&dd, pos[0], pos[1], pos[2], radius, duRGBA(0,0,0,32), 2.0f);
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
|
||||
const float height = ag->params.height;
|
||||
@ -746,7 +802,7 @@ void CrowdTool::handleRender()
|
||||
|
||||
if (m_agentDebug.idx != -1)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(m_agentDebug.idx);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(m_agentDebug.idx);
|
||||
if (ag->active)
|
||||
{
|
||||
if (m_showVO)
|
||||
@ -780,9 +836,9 @@ void CrowdTool::handleRender()
|
||||
}
|
||||
|
||||
// Velocity stuff.
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
|
||||
const float radius = ag->params.radius;
|
||||
@ -824,40 +880,47 @@ void CrowdTool::handleRenderOverlay(double* proj, double* model, int* view)
|
||||
|
||||
if (m_showLabels)
|
||||
{
|
||||
for (int i = 0; i < m_crowd.getAgentCount(); ++i)
|
||||
dtCrowd* crowd = m_sample->getCrowd();
|
||||
if (crowd)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
const float* pos = ag->npos;
|
||||
const float h = ag->params.height;
|
||||
if (gluProject((GLdouble)pos[0], (GLdouble)pos[1]+h, (GLdouble)pos[2],
|
||||
model, proj, view, &x, &y, &z))
|
||||
for (int i = 0; i < crowd->getAgentCount(); ++i)
|
||||
{
|
||||
snprintf(label, 32, "%d", i);
|
||||
imguiDrawText((int)x, (int)y+15, IMGUI_ALIGN_CENTER, label, imguiRGBA(0,0,0,220));
|
||||
}
|
||||
|
||||
const dtCrowdAgent* ag = crowd->getAgent(i);
|
||||
if (!ag->active) continue;
|
||||
const float* pos = ag->npos;
|
||||
const float h = ag->params.height;
|
||||
if (gluProject((GLdouble)pos[0], (GLdouble)pos[1]+h, (GLdouble)pos[2],
|
||||
model, proj, view, &x, &y, &z))
|
||||
{
|
||||
snprintf(label, 32, "%d", i);
|
||||
imguiDrawText((int)x, (int)y+15, IMGUI_ALIGN_CENTER, label, imguiRGBA(0,0,0,220));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_agentDebug.idx != -1)
|
||||
{
|
||||
const dtCrowdAgent* ag = m_crowd.getAgent(m_agentDebug.idx);
|
||||
if (ag->active)
|
||||
dtCrowd* crowd = m_sample->getCrowd();
|
||||
if (crowd)
|
||||
{
|
||||
const float radius = ag->params.radius;
|
||||
|
||||
if (m_showNeis)
|
||||
const dtCrowdAgent* ag = crowd->getAgent(m_agentDebug.idx);
|
||||
if (ag->active)
|
||||
{
|
||||
for (int j = 0; j < ag->nneis; ++j)
|
||||
const float radius = ag->params.radius;
|
||||
|
||||
if (m_showNeis)
|
||||
{
|
||||
const dtCrowdAgent* nei = m_crowd.getAgent(ag->neis[j].idx);
|
||||
if (!nei->active) continue;
|
||||
|
||||
if (gluProject((GLdouble)nei->npos[0], (GLdouble)nei->npos[1]+radius, (GLdouble)nei->npos[2],
|
||||
model, proj, view, &x, &y, &z))
|
||||
for (int j = 0; j < ag->nneis; ++j)
|
||||
{
|
||||
snprintf(label, 32, "%.3f", ag->neis[j].dist);
|
||||
imguiDrawText((int)x, (int)y+15, IMGUI_ALIGN_CENTER, label, imguiRGBA(255,255,255,220));
|
||||
const dtCrowdAgent* nei = crowd->getAgent(ag->neis[j].idx);
|
||||
if (!nei->active) continue;
|
||||
|
||||
if (gluProject((GLdouble)nei->npos[0], (GLdouble)nei->npos[1]+radius, (GLdouble)nei->npos[2],
|
||||
model, proj, view, &x, &y, &z))
|
||||
{
|
||||
snprintf(label, 32, "%.3f", ag->neis[j].dist);
|
||||
imguiDrawText((int)x, (int)y+15, IMGUI_ALIGN_CENTER, label, imguiRGBA(255,255,255,220));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -899,5 +962,9 @@ void CrowdTool::handleRenderOverlay(double* proj, double* model, int* view)
|
||||
}
|
||||
ty -= 20;
|
||||
imguiDrawText(280, ty, IMGUI_ALIGN_LEFT, "SPACE: Run/Pause simulation. 1: Step simulation.", imguiRGBA(255,255,255,192));
|
||||
|
||||
ty -= 20;
|
||||
if (m_run)
|
||||
imguiDrawText(280, ty, IMGUI_ALIGN_LEFT, "- RUNNING -", imguiRGBA(255,32,16,255));
|
||||
else
|
||||
imguiDrawText(280, ty, IMGUI_ALIGN_LEFT, "- PAUSED -", imguiRGBA(255,255,255,128));
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "DetourDebugDraw.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "DetourCrowd.h"
|
||||
#include "imgui.h"
|
||||
#include "SDL.h"
|
||||
#include "SDL_opengl.h"
|
||||
@ -38,18 +39,21 @@ Sample::Sample() :
|
||||
m_geom(0),
|
||||
m_navMesh(0),
|
||||
m_navQuery(0),
|
||||
m_crowd(0),
|
||||
m_navMeshDrawFlags(DU_DRAWNAVMESH_OFFMESHCONS|DU_DRAWNAVMESH_CLOSEDLIST),
|
||||
m_tool(0),
|
||||
m_ctx(0)
|
||||
{
|
||||
resetCommonSettings();
|
||||
m_navQuery = dtAllocNavMeshQuery();
|
||||
m_crowd = dtAllocCrowd();
|
||||
}
|
||||
|
||||
Sample::~Sample()
|
||||
{
|
||||
dtFreeNavMeshQuery(m_navQuery);
|
||||
dtFreeNavMesh(m_navMesh);
|
||||
dtFreeCrowd(m_crowd);
|
||||
delete m_tool;
|
||||
}
|
||||
|
||||
|
@ -1166,6 +1166,18 @@ bool Sample_TempObstacles::handleBuild()
|
||||
m_cacheBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f;
|
||||
m_cacheBuildMemUsage = m_talloc->high;
|
||||
|
||||
|
||||
const dtNavMesh* nav = m_navMesh;
|
||||
int navmeshMemUsage = 0;
|
||||
for (int i = 0; i < nav->getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = nav->getTile(i);
|
||||
if (tile->header)
|
||||
navmeshMemUsage += tile->dataSize;
|
||||
}
|
||||
printf("navmeshMemUsage = %.1f kB", navmeshMemUsage/1024.0f);
|
||||
|
||||
|
||||
if (m_tool)
|
||||
m_tool->init(this);
|
||||
|
||||
|
@ -423,10 +423,10 @@ int main(int /*argc*/, char** /*argv*/)
|
||||
}
|
||||
|
||||
// Clamp the framerate so that we do not hog all the CPU.
|
||||
const float FRAME_RATE = 40;
|
||||
if (dt < FRAME_RATE)
|
||||
const float MIN_FRAME_TIME = 1.0f/40.0f;
|
||||
if (dt < MIN_FRAME_TIME)
|
||||
{
|
||||
int ms = (int)((FRAME_RATE - dt)*1000.0f);
|
||||
int ms = (int)((MIN_FRAME_TIME - dt)*1000.0f);
|
||||
if (ms > 10) ms = 10;
|
||||
if (ms >= 0)
|
||||
SDL_Delay(ms);
|
||||
|
Loading…
x
Reference in New Issue
Block a user