1
0
forked from 0ad/0ad

Fix more pathfinding issues.

Change the way the long-range pathfinder rasterisation works slightly so
that we have a better compatibility with the short-range pathfinder.
Should fix the "stuck units" issues, though I am not sure so I am not
marking them as fixed so far. Refs #3471, #3505, and possibly #3292.

Caveat: I am now using clearance of 0.8 for "default" class, which might
have side-effects: please report anything weird.

Also fix leftover style issues.

This was SVN commit r17161.
This commit is contained in:
wraitii 2015-11-01 07:28:43 +00:00
parent 4428c1e690
commit 14038d4cd8
7 changed files with 44 additions and 17 deletions

View File

@ -11,7 +11,7 @@
<Obstructions>pathfinding</Obstructions>
<MaxWaterDepth>2</MaxWaterDepth>
<MaxTerrainSlope>1.0</MaxTerrainSlope>
<Clearance>1.0</Clearance>
<Clearance>0.8</Clearance>
</default>
<large>
<Obstructions>pathfinding</Obstructions>

View File

@ -995,8 +995,12 @@ void CCmpObstructionManager::GetUnitsOnObstruction(const ObstructionSquare& squa
if (rasterizedRects.find(shape.clearance) == rasterizedRects.end())
{
// Wraitii 31/10/15: check out helpers/Rasterize.cpp for more info, but
// a change in the long-range pathfinder rasterization to fix stuck units
// requires the foundation code to be more restrictive, thus adding the substraction
// of Pathfinding::NAVCELL_SIZE. Remove it if the problem in Rasterize.cpp is ever fixed.
SimRasterize::Spans& newSpans = rasterizedRects[shape.clearance];
SimRasterize::RasterizeRectWithClearance(newSpans, square, shape.clearance, Pathfinding::NAVCELL_SIZE);
SimRasterize::RasterizeRectWithClearance(newSpans, square, shape.clearance-Pathfinding::NAVCELL_SIZE, Pathfinding::NAVCELL_SIZE);
}
SimRasterize::Spans& spans = rasterizedRects[shape.clearance];

View File

@ -127,7 +127,7 @@ struct EdgeAA
// When computing vertexes to insert into the search graph,
// add a small delta so that the vertexes of an edge don't get interpreted
// as crossing the edge (given minor numerical inaccuracies)
static const entity_pos_t EDGE_EXPAND_DELTA = entity_pos_t::FromInt(1)/4;
static const entity_pos_t EDGE_EXPAND_DELTA = entity_pos_t::FromInt(1)/16;
/**
* Check whether a ray from 'a' to 'b' crosses any of the edges.

View File

@ -1042,7 +1042,9 @@ void CCmpUnitMotion::PlanNextStep(const CFixedVector2D& pos)
return;
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
if (!cmpPathfinder)
return;
const Waypoint& nextPoint = m_LongPath.m_Waypoints.back();
// The next step was obstructed the last time we checked; also check that

View File

@ -352,8 +352,7 @@ void HierarchicalPathfinder::Update(Grid<NavcellData>* grid, const Grid<u8>& dir
if (std::find(processedChunks.begin(), processedChunks.end(), chunkID) == processedChunks.end())
{
// iterate over a std::map<std::string, pass_class_t>
for (auto& passClassMask : m_PassClassMasks)
for (const std::pair<std::string, pass_class_t>& passClassMask : m_PassClassMasks)
{
pass_class_t passClass = passClassMask.second;
Chunk& a = m_Chunks[passClass].at(chunkID.second*m_ChunksW + chunkID.first);
@ -366,8 +365,7 @@ void HierarchicalPathfinder::Update(Grid<NavcellData>* grid, const Grid<u8>& dir
// TODO: Also be clever with edges
m_Edges.clear();
// iterate over a std::map<std::string, pass_class_t>
for (auto& passClassMask : m_PassClassMasks)
for (const std::pair<std::string, pass_class_t>& passClassMask : m_PassClassMasks)
{
pass_class_t passClass = passClassMask.second;
EdgesMap& edges = m_Edges[passClass];

View File

@ -310,6 +310,12 @@ public:
{
m_Clearance = node.GetChild("Clearance").ToFixed();
/* According to Philip who designed the original doc (in docs/pathfinder.pdf),
* clearance should usually be integer to ensure consistent behavior when rasterizing
* the passability map.
* This seems doubtful to me and my pathfinder fix makes having a clearance of 0.8 quite convenient
* so I comment out this check, but leave it here for the purpose of documentation should a bug arise.
if (!(m_Clearance % Pathfinding::NAVCELL_SIZE).IsZero())
{
// If clearance isn't an integer number of navcells then we'll
@ -317,7 +323,7 @@ public:
// by clearance, vs expanding static obstructions by clearance
LOGWARNING("Pathfinder passability class has clearance %f, should be multiple of %f",
m_Clearance.ToFloat(), Pathfinding::NAVCELL_SIZE.ToFloat());
}
}*/
}
else
m_Clearance = fixed::Zero();

View File

@ -25,9 +25,26 @@ void SimRasterize::RasterizeRectWithClearance(Spans& spans,
const ICmpObstructionManager::ObstructionSquare& shape,
entity_pos_t clearance, entity_pos_t cellSize)
{
// A long-standing issue with the pathfinding has been that the long-range one
// uses a AA navcell grid, while the short-range uses an accurate vector representation.
// This means we could get paths accepted by one but not both pathfinders.
// Since the new pathfinder, the short-range pathfinder's representation was usually
// encompassing the rasterisation of the long-range one for a building.
// This means that we could never get quite as close as the long-range pathfinder wanted.
// This could mean units tried going through impassable paths.
// To fix this, we need to make sure that the short-range pathfinder is always mostly
// included in the rasterisation. The easiest way is to rasterise more, thus this variable
// Since this is a very complicated subject, check out logs on 31/10/2015 for more detailled info.
// or ask wraitii about it.
// If the short-range pathfinder is sufficiently changed, this could become unnecessary and thus removed.
// A side effect is that the basic clearance has been set to 0.8, so removing this line should be done
// in parallel with setting clearance back to 1 for the default passability class (though this isn't strictly necessary).
// Also: the code detecting foundation obstruction in CcmpObstructionManager had to be changed similarly.
entity_pos_t rasterClearance = clearance + entity_pos_t::FromInt(1);
// Get the bounds of cells that might possibly be within the shape
// (We'll then test each of those cells more precisely)
CFixedVector2D halfSize(shape.hw + clearance, shape.hh + clearance);
CFixedVector2D halfSize(shape.hw + rasterClearance, shape.hh + rasterClearance);
CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(shape.u, shape.v, halfSize);
i16 i0 = ((shape.x - halfBound.X) / cellSize).ToInt_RoundToNegInfinity();
i16 j0 = ((shape.z - halfBound.Y) / cellSize).ToInt_RoundToNegInfinity();
@ -38,11 +55,11 @@ void SimRasterize::RasterizeRectWithClearance(Spans& spans,
return; // empty bounds - this shouldn't happen
spans.reserve(j1 - j0);
for (i16 j = j0; j < j1; ++j)
{
// Find the min/max range of cells that are strictly inside the square+clearance.
// (Since the square+clearance is a convex shape, we can just test each
// Find the min/max range of cells that are strictly inside the square+realClearance.
// (Since the square+realClearance is a convex shape, we can just test each
// corner of each cell is inside the shape.)
// (TODO: This potentially does a lot of redundant work.)
i16 spanI0 = std::numeric_limits<i16>::max();
@ -51,28 +68,28 @@ void SimRasterize::RasterizeRectWithClearance(Spans& spans,
{
if (Geometry::DistanceToSquare(
CFixedVector2D(cellSize*i, cellSize*j) - CFixedVector2D(shape.x, shape.z),
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > clearance)
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > rasterClearance)
{
continue;
}
if (Geometry::DistanceToSquare(
CFixedVector2D(cellSize*(i+1), cellSize*j) - CFixedVector2D(shape.x, shape.z),
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > clearance)
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > rasterClearance)
{
continue;
}
if (Geometry::DistanceToSquare(
CFixedVector2D(cellSize*i, cellSize*(j+1)) - CFixedVector2D(shape.x, shape.z),
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > clearance)
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > rasterClearance)
{
continue;
}
if (Geometry::DistanceToSquare(
CFixedVector2D(cellSize*(i+1), cellSize*(j+1)) - CFixedVector2D(shape.x, shape.z),
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > clearance)
shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh), true) > rasterClearance)
{
continue;
}