Random polygon selection for Detour.

This commit is contained in:
Mikko Mononen
2012-02-27 07:17:51 +00:00
parent 478d5c8605
commit cd3a68dd9b
7 changed files with 444 additions and 1 deletions

View File

@@ -216,6 +216,281 @@ dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes)
return DT_SUCCESS;
}
dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*frand)(),
dtPolyRef* randomRef, float* randomPt) const
{
dtAssert(m_nav);
// Randomly pick one tile. Assume that all tiles cover roughly the same area.
const dtMeshTile* tile = 0;
float tsum = 0.0f;
for (int i = 0; i < m_nav->getMaxTiles(); i++)
{
const dtMeshTile* t = m_nav->getTile(i);
if (!t || !t->header) continue;
// Choose random tile using reservoi sampling.
const float area = 1.0f; // Could be tile area too.
tsum += area;
const float u = frand();
if (u*tsum <= area)
tile = t;
}
if (!tile)
return DT_FAILURE;
// Randomly pick one polygon weighted by polygon area.
const dtPoly* poly = 0;
dtPolyRef polyRef = 0;
const dtPolyRef base = m_nav->getPolyRefBase(tile);
float areaSum = 0.0f;
for (int i = 0; i < tile->header->polyCount; ++i)
{
const dtPoly* p = &tile->polys[i];
// Do not return off-mesh connection polygons.
if (p->getType() != DT_POLYTYPE_GROUND)
continue;
// Must pass filter
const dtPolyRef ref = base | (dtPolyRef)i;
if (!filter->passFilter(ref, tile, p))
continue;
// Calc area of the polygon.
float polyArea = 0.0f;
for (int j = 2; j < p->vertCount; ++j)
{
const float* va = &tile->verts[p->verts[0]*3];
const float* vb = &tile->verts[p->verts[j-1]*3];
const float* vc = &tile->verts[p->verts[j]*3];
polyArea += dtTriArea2D(va,vb,vc);
}
// Choose random polygon weighted by area, using reservoi sampling.
areaSum += polyArea;
const float u = frand();
if (u*areaSum <= polyArea)
{
poly = p;
polyRef = ref;
}
}
if (!poly)
return DT_FAILURE;
// Randomly pick point on polygon.
const float* v = &tile->verts[poly->verts[0]*3];
float verts[3*DT_VERTS_PER_POLYGON];
float areas[DT_VERTS_PER_POLYGON];
dtVcopy(&verts[0*3],v);
for (int j = 1; j < poly->vertCount; ++j)
{
v = &tile->verts[poly->verts[j]*3];
dtVcopy(&verts[j*3],v);
}
const float s = frand();
const float t = frand();
float pt[3];
dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt);
float h = 0.0f;
dtStatus status = getPolyHeight(polyRef, pt, &h);
if (dtStatusFailed(status))
return status;
pt[1] = h;
dtVcopy(randomPt, pt);
*randomRef = polyRef;
return DT_SUCCESS;
}
dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
const dtQueryFilter* filter, float (*frand)(),
dtPolyRef* randomRef, float* randomPt) const
{
dtAssert(m_nav);
dtAssert(m_nodePool);
dtAssert(m_openList);
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
return DT_FAILURE | DT_INVALID_PARAM;
const dtMeshTile* startTile = 0;
const dtPoly* startPoly = 0;
m_nav->getTileAndPolyByRefUnsafe(startRef, &startTile, &startPoly);
if (!filter->passFilter(startRef, startTile, startPoly))
return DT_FAILURE | DT_INVALID_PARAM;
m_nodePool->clear();
m_openList->clear();
dtNode* startNode = m_nodePool->getNode(startRef);
dtVcopy(startNode->pos, centerPos);
startNode->pidx = 0;
startNode->cost = 0;
startNode->total = 0;
startNode->id = startRef;
startNode->flags = DT_NODE_OPEN;
m_openList->push(startNode);
dtStatus status = DT_SUCCESS;
const float radiusSqr = dtSqr(radius);
float areaSum = 0.0f;
const dtMeshTile* randomTile = 0;
const dtPoly* randomPoly = 0;
dtPolyRef randomPolyRef = 0;
while (!m_openList->empty())
{
dtNode* bestNode = m_openList->pop();
bestNode->flags &= ~DT_NODE_OPEN;
bestNode->flags |= DT_NODE_CLOSED;
// Get poly and tile.
// The API input has been cheked already, skip checking internal data.
const dtPolyRef bestRef = bestNode->id;
const dtMeshTile* bestTile = 0;
const dtPoly* bestPoly = 0;
m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
// Place random locations on on ground.
if (bestPoly->getType() == DT_POLYTYPE_GROUND)
{
// Calc area of the polygon.
float polyArea = 0.0f;
for (int j = 2; j < bestPoly->vertCount; ++j)
{
const float* va = &bestTile->verts[bestPoly->verts[0]*3];
const float* vb = &bestTile->verts[bestPoly->verts[j-1]*3];
const float* vc = &bestTile->verts[bestPoly->verts[j]*3];
polyArea += dtTriArea2D(va,vb,vc);
}
// Choose random polygon weighted by area, using reservoi sampling.
areaSum += polyArea;
const float u = frand();
if (u*areaSum <= polyArea)
{
randomTile = bestTile;
randomPoly = bestPoly;
randomPolyRef = bestRef;
}
}
// Get parent poly and tile.
dtPolyRef parentRef = 0;
const dtMeshTile* parentTile = 0;
const dtPoly* parentPoly = 0;
if (bestNode->pidx)
parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
if (parentRef)
m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly);
for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
{
const dtLink* link = &bestTile->links[i];
dtPolyRef neighbourRef = link->ref;
// Skip invalid neighbours and do not follow back to parent.
if (!neighbourRef || neighbourRef == parentRef)
continue;
// Expand to neighbour
const dtMeshTile* neighbourTile = 0;
const dtPoly* neighbourPoly = 0;
m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly);
// Do not advance if the polygon is excluded by the filter.
if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
continue;
// Find edge and calc distance to the edge.
float va[3], vb[3];
if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb))
continue;
// If the circle is not touching the next polygon, skip it.
float tseg;
float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg);
if (distSqr > radiusSqr)
continue;
dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
if (!neighbourNode)
{
status |= DT_OUT_OF_NODES;
continue;
}
if (neighbourNode->flags & DT_NODE_CLOSED)
continue;
// Cost
if (neighbourNode->flags == 0)
dtVlerp(neighbourNode->pos, va, vb, 0.5f);
const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos);
// The node is already in open list and the new result is worse, skip.
if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total)
continue;
neighbourNode->id = neighbourRef;
neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
neighbourNode->total = total;
if (neighbourNode->flags & DT_NODE_OPEN)
{
m_openList->modify(neighbourNode);
}
else
{
neighbourNode->flags = DT_NODE_OPEN;
m_openList->push(neighbourNode);
}
}
}
if (!randomPoly)
return DT_FAILURE;
// Randomly pick point on polygon.
const float* v = &randomTile->verts[randomPoly->verts[0]*3];
float verts[3*DT_VERTS_PER_POLYGON];
float areas[DT_VERTS_PER_POLYGON];
dtVcopy(&verts[0*3],v);
for (int j = 1; j < randomPoly->vertCount; ++j)
{
v = &randomTile->verts[randomPoly->verts[j]*3];
dtVcopy(&verts[j*3],v);
}
const float s = frand();
const float t = frand();
float pt[3];
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
float h = 0.0f;
dtStatus stat = getPolyHeight(randomPolyRef, pt, &h);
if (dtStatusFailed(status))
return stat;
pt[1] = h;
dtVcopy(randomPt, pt);
*randomRef = randomPolyRef;
return DT_SUCCESS;
}
//////////////////////////////////////////////////////////////////////////////////////////
/// @par