forked from 0ad/0ad
Refactor the grid update code. Should give a significant performance boost to the simulation update.
Also fixes some bad code that could lead to hidden bugs. Fixes #3296, thanks elexis for testing ;) This was SVN commit r16764.
This commit is contained in:
parent
0e7f0f861b
commit
473b282265
@ -511,7 +511,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void StartComputation(const shared_ptr<ScriptInterface::StructuredClone>& gameState,
|
void StartComputation(const shared_ptr<ScriptInterface::StructuredClone>& gameState,
|
||||||
const Grid<u16>& passabilityMap, bool passabilityMapDirty, const Grid<u8>* dirtinessGrid, bool passabilityMapEntirelyDirty,
|
const Grid<u16>& passabilityMap, const GridUpdateInformation& dirtinessInformations,
|
||||||
const Grid<u8>& territoryMap, bool territoryMapDirty,
|
const Grid<u8>& territoryMap, bool territoryMapDirty,
|
||||||
std::map<std::string, pass_class_t> passClassMasks)
|
std::map<std::string, pass_class_t> passClassMasks)
|
||||||
{
|
{
|
||||||
@ -520,13 +520,13 @@ public:
|
|||||||
m_GameState = gameState;
|
m_GameState = gameState;
|
||||||
JSContext* cx = m_ScriptInterface->GetContext();
|
JSContext* cx = m_ScriptInterface->GetContext();
|
||||||
|
|
||||||
if (passabilityMapDirty)
|
if (dirtinessInformations.dirty)
|
||||||
{
|
{
|
||||||
m_PassabilityMap = passabilityMap;
|
m_PassabilityMap = passabilityMap;
|
||||||
if (passabilityMapEntirelyDirty)
|
if (dirtinessInformations.globallyDirty)
|
||||||
m_LongPathfinder.Reload(passClassMasks, &m_PassabilityMap);
|
m_LongPathfinder.Reload(passClassMasks, &m_PassabilityMap);
|
||||||
else
|
else
|
||||||
m_LongPathfinder.Update(&m_PassabilityMap, dirtinessGrid);
|
m_LongPathfinder.Update(&m_PassabilityMap, dirtinessInformations.dirtinessGrid);
|
||||||
ScriptInterface::ToJSVal(cx, &m_PassabilityMapVal, m_PassabilityMap);
|
ScriptInterface::ToJSVal(cx, &m_PassabilityMapVal, m_PassabilityMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1035,10 +1035,7 @@ public:
|
|||||||
if (cmpPathfinder)
|
if (cmpPathfinder)
|
||||||
passabilityMap = &cmpPathfinder->GetPassabilityGrid();
|
passabilityMap = &cmpPathfinder->GetPassabilityGrid();
|
||||||
|
|
||||||
Grid<u8> dummyDirtinessGrid;
|
GridUpdateInformation dirtinessInformations = cmpPathfinder->GetDirtinessData();
|
||||||
const Grid<u8>* dirtinessGrid = &dummyDirtinessGrid;
|
|
||||||
bool passabilityMapEntirelyDirty = false;
|
|
||||||
bool passabilityMapDirty = cmpPathfinder->GetDirtinessData(dummyDirtinessGrid, passabilityMapEntirelyDirty);
|
|
||||||
|
|
||||||
// Get the territory data
|
// Get the territory data
|
||||||
// Since getting the territory grid can trigger a recalculation, we check NeedUpdate first
|
// Since getting the territory grid can trigger a recalculation, we check NeedUpdate first
|
||||||
@ -1058,7 +1055,7 @@ public:
|
|||||||
passClassMasks = cmpPathfinder->GetPassabilityClasses();
|
passClassMasks = cmpPathfinder->GetPassabilityClasses();
|
||||||
|
|
||||||
m_Worker.StartComputation(scriptInterface.WriteStructuredClone(state),
|
m_Worker.StartComputation(scriptInterface.WriteStructuredClone(state),
|
||||||
*passabilityMap, passabilityMapDirty, dirtinessGrid, passabilityMapEntirelyDirty,
|
*passabilityMap, dirtinessInformations,
|
||||||
*territoryMap, territoryMapDirty,
|
*territoryMap, territoryMapDirty,
|
||||||
passClassMasks);
|
passClassMasks);
|
||||||
|
|
||||||
|
@ -137,6 +137,8 @@ public:
|
|||||||
u32 m_UnitShapeNext; // next allocated id
|
u32 m_UnitShapeNext; // next allocated id
|
||||||
u32 m_StaticShapeNext;
|
u32 m_StaticShapeNext;
|
||||||
|
|
||||||
|
entity_pos_t m_MaxClearance;
|
||||||
|
|
||||||
bool m_PassabilityCircular;
|
bool m_PassabilityCircular;
|
||||||
|
|
||||||
entity_pos_t m_WorldX0;
|
entity_pos_t m_WorldX0;
|
||||||
@ -157,9 +159,9 @@ public:
|
|||||||
m_UnitShapeNext = 1;
|
m_UnitShapeNext = 1;
|
||||||
m_StaticShapeNext = 1;
|
m_StaticShapeNext = 1;
|
||||||
|
|
||||||
m_Dirty = true;
|
m_UpdateInformations.dirty = true;
|
||||||
m_NeedsGlobalUpdate = true;
|
m_UpdateInformations.globallyDirty = true;
|
||||||
m_DirtinessGrid = NULL;
|
m_UpdateInformations.globalRecompute = true;
|
||||||
|
|
||||||
m_PassabilityCircular = false;
|
m_PassabilityCircular = false;
|
||||||
|
|
||||||
@ -172,7 +174,6 @@ public:
|
|||||||
|
|
||||||
virtual void Deinit()
|
virtual void Deinit()
|
||||||
{
|
{
|
||||||
SAFE_DELETE(m_DirtinessGrid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename S>
|
template<typename S>
|
||||||
@ -234,14 +235,16 @@ public:
|
|||||||
ENSURE(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
|
ENSURE(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
|
||||||
ResetSubdivisions(x1, z1);
|
ResetSubdivisions(x1, z1);
|
||||||
|
|
||||||
SAFE_DELETE(m_DirtinessGrid);
|
|
||||||
|
|
||||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
|
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
|
||||||
if (!cmpTerrain)
|
if (!cmpTerrain)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
u16 tiles = cmpTerrain->GetTilesPerSide();
|
u16 tiles = cmpTerrain->GetTilesPerSide();
|
||||||
m_DirtinessGrid = new Grid<u8>(tiles*Pathfinding::NAVCELLS_PER_TILE, tiles*Pathfinding::NAVCELLS_PER_TILE);
|
m_UpdateInformations.dirtinessGrid = Grid<u8>(tiles*Pathfinding::NAVCELLS_PER_TILE, tiles*Pathfinding::NAVCELLS_PER_TILE);
|
||||||
|
|
||||||
|
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
|
||||||
|
if (cmpPathfinder)
|
||||||
|
m_MaxClearance = cmpPathfinder->GetMaximumClearance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
|
void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
|
||||||
@ -271,10 +274,11 @@ public:
|
|||||||
UnitShape shape = { ent, x, z, r, clearance, flags, group };
|
UnitShape shape = { ent, x, z, r, clearance, flags, group };
|
||||||
u32 id = m_UnitShapeNext++;
|
u32 id = m_UnitShapeNext++;
|
||||||
m_UnitShapes[id] = shape;
|
m_UnitShapes[id] = shape;
|
||||||
MakeDirtyUnit(flags, id);
|
|
||||||
|
|
||||||
m_UnitSubdivision.Add(id, CFixedVector2D(x - r, z - r), CFixedVector2D(x + r, z + r));
|
m_UnitSubdivision.Add(id, CFixedVector2D(x - r, z - r), CFixedVector2D(x + r, z + r));
|
||||||
|
|
||||||
|
MakeDirtyUnit(flags, id, shape);
|
||||||
|
|
||||||
return UNIT_INDEX_TO_TAG(id);
|
return UNIT_INDEX_TO_TAG(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,12 +292,13 @@ public:
|
|||||||
StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags, group, group2 };
|
StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags, group, group2 };
|
||||||
u32 id = m_StaticShapeNext++;
|
u32 id = m_StaticShapeNext++;
|
||||||
m_StaticShapes[id] = shape;
|
m_StaticShapes[id] = shape;
|
||||||
MakeDirtyStatic(flags, id);
|
|
||||||
|
|
||||||
CFixedVector2D center(x, z);
|
CFixedVector2D center(x, z);
|
||||||
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2));
|
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2));
|
||||||
m_StaticSubdivision.Add(id, center - bbHalfSize, center + bbHalfSize);
|
m_StaticSubdivision.Add(id, center - bbHalfSize, center + bbHalfSize);
|
||||||
|
|
||||||
|
MakeDirtyStatic(flags, id, shape);
|
||||||
|
|
||||||
return STATIC_INDEX_TO_TAG(id);
|
return STATIC_INDEX_TO_TAG(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,6 +329,8 @@ public:
|
|||||||
{
|
{
|
||||||
UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
|
UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
|
||||||
|
|
||||||
|
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region
|
||||||
|
|
||||||
m_UnitSubdivision.Move(TAG_TO_INDEX(tag),
|
m_UnitSubdivision.Move(TAG_TO_INDEX(tag),
|
||||||
CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
|
CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
|
||||||
CFixedVector2D(shape.x + shape.r, shape.z + shape.r),
|
CFixedVector2D(shape.x + shape.r, shape.z + shape.r),
|
||||||
@ -333,7 +340,7 @@ public:
|
|||||||
shape.x = x;
|
shape.x = x;
|
||||||
shape.z = z;
|
shape.z = z;
|
||||||
|
|
||||||
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag));
|
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -344,6 +351,8 @@ public:
|
|||||||
|
|
||||||
StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
|
StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
|
||||||
|
|
||||||
|
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region
|
||||||
|
|
||||||
CFixedVector2D fromBbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
|
CFixedVector2D fromBbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
|
||||||
CFixedVector2D toBbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(shape.hw, shape.hh));
|
CFixedVector2D toBbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(shape.hw, shape.hh));
|
||||||
m_StaticSubdivision.Move(TAG_TO_INDEX(tag),
|
m_StaticSubdivision.Move(TAG_TO_INDEX(tag),
|
||||||
@ -357,7 +366,7 @@ public:
|
|||||||
shape.u = u;
|
shape.u = u;
|
||||||
shape.v = v;
|
shape.v = v;
|
||||||
|
|
||||||
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag));
|
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +420,8 @@ public:
|
|||||||
CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
|
CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
|
||||||
CFixedVector2D(shape.x + shape.r, shape.z + shape.r));
|
CFixedVector2D(shape.x + shape.r, shape.z + shape.r));
|
||||||
|
|
||||||
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag));
|
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape);
|
||||||
|
|
||||||
m_UnitShapes.erase(TAG_TO_INDEX(tag));
|
m_UnitShapes.erase(TAG_TO_INDEX(tag));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -422,7 +432,8 @@ public:
|
|||||||
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
|
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
|
||||||
m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize);
|
m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize);
|
||||||
|
|
||||||
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag));
|
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape);
|
||||||
|
|
||||||
m_StaticShapes.erase(TAG_TO_INDEX(tag));
|
m_StaticShapes.erase(TAG_TO_INDEX(tag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,7 +462,7 @@ public:
|
|||||||
virtual bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector<entity_id_t>* out);
|
virtual bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector<entity_id_t>* out);
|
||||||
virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out);
|
virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out);
|
||||||
|
|
||||||
virtual void Rasterize(Grid<u16>& grid, const entity_pos_t& expand, ICmpObstructionManager::flags_t requireMask, u16 setMask);
|
virtual void Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, ICmpObstructionManager::flags_t requireMask, bool fullUpdate);
|
||||||
virtual void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares);
|
virtual void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares);
|
||||||
virtual void GetUnitsOnObstruction(const ObstructionSquare& square, std::vector<entity_id_t>& out, const IObstructionTestFilter& filter);
|
virtual void GetUnitsOnObstruction(const ObstructionSquare& square, std::vector<entity_id_t>& out, const IObstructionTestFilter& filter);
|
||||||
|
|
||||||
@ -479,29 +490,21 @@ public:
|
|||||||
|
|
||||||
void RenderSubmit(SceneCollector& collector);
|
void RenderSubmit(SceneCollector& collector);
|
||||||
|
|
||||||
virtual bool GetDirtinessData(Grid<u8>& dirtinessGrid, bool& globalUpdateNeeded)
|
virtual void UpdateInformations(GridUpdateInformation& informations)
|
||||||
{
|
{
|
||||||
if (!m_Dirty || !m_DirtinessGrid)
|
// If the pathfinder wants to perform a full update, don't change that.
|
||||||
{
|
if (m_UpdateInformations.dirty && !informations.globalRecompute)
|
||||||
globalUpdateNeeded = false;
|
informations = m_UpdateInformations;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirtinessGrid = *m_DirtinessGrid;
|
m_UpdateInformations.Clean();
|
||||||
globalUpdateNeeded = m_NeedsGlobalUpdate;
|
|
||||||
|
|
||||||
m_Dirty = false;
|
|
||||||
m_NeedsGlobalUpdate = false;
|
|
||||||
m_DirtinessGrid->reset();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Dynamic updates for the long-range pathfinder
|
// Dynamic updates for the long-range pathfinder
|
||||||
bool m_Dirty;
|
GridUpdateInformation m_UpdateInformations;
|
||||||
bool m_NeedsGlobalUpdate;
|
// These vectors might contain shapes that were deleted
|
||||||
Grid<u8>* m_DirtinessGrid;
|
std::vector<u32> m_DirtyStaticShapes;
|
||||||
|
std::vector<u32> m_DirtyUnitShapes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark all previous Rasterize()d grids as dirty, and the debug display.
|
* Mark all previous Rasterize()d grids as dirty, and the debug display.
|
||||||
@ -509,8 +512,11 @@ private:
|
|||||||
*/
|
*/
|
||||||
void MakeDirtyAll()
|
void MakeDirtyAll()
|
||||||
{
|
{
|
||||||
m_Dirty = true;
|
m_UpdateInformations.dirty = true;
|
||||||
m_NeedsGlobalUpdate = true;
|
m_UpdateInformations.globallyDirty = true;
|
||||||
|
m_UpdateInformations.globalRecompute = true;
|
||||||
|
m_UpdateInformations.dirtinessGrid.reset();
|
||||||
|
|
||||||
m_DebugOverlayDirty = true;
|
m_DebugOverlayDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,52 +531,92 @@ private:
|
|||||||
|
|
||||||
inline void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const entity_pos_t& r)
|
inline void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const entity_pos_t& r)
|
||||||
{
|
{
|
||||||
if (!m_DirtinessGrid)
|
MarkDirtinessGrid(x, z, CFixedVector2D(r, r));
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
inline void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const CFixedVector2D& hbox)
|
||||||
|
{
|
||||||
|
ENSURE(m_UpdateInformations.dirtinessGrid.m_W != 0 && m_UpdateInformations.dirtinessGrid.m_H != 0);
|
||||||
|
|
||||||
u16 j0, j1, i0, i1;
|
u16 j0, j1, i0, i1;
|
||||||
Pathfinding::NearestNavcell(x - r, z - r, i0, j0, m_DirtinessGrid->m_W, m_DirtinessGrid->m_H);
|
Pathfinding::NearestNavcell(x - hbox.X, z - hbox.Y, i0, j0, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H);
|
||||||
Pathfinding::NearestNavcell(x + r, z + r, i1, j1, m_DirtinessGrid->m_W, m_DirtinessGrid->m_H);
|
Pathfinding::NearestNavcell(x + hbox.X, z + hbox.Y, i1, j1, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H);
|
||||||
|
|
||||||
for (int j = j0; j < j1; ++j)
|
for (int j = j0; j < j1; ++j)
|
||||||
for (int i = i0; i < i1; ++i)
|
for (int i = i0; i < i1; ++i)
|
||||||
m_DirtinessGrid->set(i, j, 1);
|
m_UpdateInformations.dirtinessGrid.set(i, j, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
|
* Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
|
||||||
* Call this when a static shape has changed.
|
* Call this when a static shape has changed.
|
||||||
*/
|
*/
|
||||||
void MakeDirtyStatic(flags_t flags, u32 index)
|
void MakeDirtyStatic(flags_t flags, u32 index, const StaticShape& shape)
|
||||||
{
|
{
|
||||||
|
m_DebugOverlayDirty = true;
|
||||||
|
|
||||||
if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
|
if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
|
||||||
{
|
{
|
||||||
m_Dirty = true;
|
m_UpdateInformations.dirty = true;
|
||||||
|
|
||||||
auto it = m_StaticShapes.find(index);
|
if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), index) == m_DirtyStaticShapes.end())
|
||||||
if (it != m_StaticShapes.end())
|
m_DirtyStaticShapes.push_back(index);
|
||||||
MarkDirtinessGrid(it->second.x, it->second.z, std::max(it->second.hw, it->second.hh));
|
|
||||||
|
// All shapes overlapping the updated part of the grid should be dirtied too
|
||||||
|
CFixedVector2D center(shape.x, shape.z);
|
||||||
|
CFixedVector2D expandedBox =
|
||||||
|
Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh)) +
|
||||||
|
CFixedVector2D(m_MaxClearance, m_MaxClearance);
|
||||||
|
|
||||||
|
std::vector<u32> staticsNear;
|
||||||
|
m_StaticSubdivision.GetInRange(staticsNear, center - expandedBox, center + expandedBox);
|
||||||
|
for (u32& staticId : staticsNear)
|
||||||
|
if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end())
|
||||||
|
m_DirtyStaticShapes.push_back(staticId);
|
||||||
|
|
||||||
|
std::vector<u32> unitsNear;
|
||||||
|
m_UnitSubdivision.GetInRange(unitsNear, center - expandedBox, center + expandedBox);
|
||||||
|
for (u32& unitId : unitsNear)
|
||||||
|
if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end())
|
||||||
|
m_DirtyUnitShapes.push_back(unitId);
|
||||||
|
|
||||||
|
MarkDirtinessGrid(shape.x, shape.z, expandedBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_DebugOverlayDirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
|
* Mark all previous Rasterize()d grids as dirty, if they depend on this shape.
|
||||||
* Call this when a unit shape has changed.
|
* Call this when a unit shape has changed.
|
||||||
*/
|
*/
|
||||||
void MakeDirtyUnit(flags_t flags, u32 index)
|
void MakeDirtyUnit(flags_t flags, u32 index, const UnitShape& shape)
|
||||||
{
|
{
|
||||||
|
m_DebugOverlayDirty = true;
|
||||||
|
|
||||||
if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
|
if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION))
|
||||||
{
|
{
|
||||||
m_Dirty = true;
|
m_UpdateInformations.dirty = true;
|
||||||
|
|
||||||
auto it = m_UnitShapes.find(index);
|
if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), index) == m_DirtyUnitShapes.end())
|
||||||
if (it != m_UnitShapes.end())
|
m_DirtyUnitShapes.push_back(index);
|
||||||
MarkDirtinessGrid(it->second.x, it->second.z, it->second.r);
|
|
||||||
|
// All shapes overlapping the updated part of the grid should be dirtied too
|
||||||
|
CFixedVector2D center(shape.x, shape.z);
|
||||||
|
entity_pos_t extendedRadius = shape.r + m_MaxClearance;
|
||||||
|
|
||||||
|
std::vector<u32> staticsNear;
|
||||||
|
m_StaticSubdivision.GetNear(staticsNear, center, extendedRadius);
|
||||||
|
for (u32& staticId : staticsNear)
|
||||||
|
if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end())
|
||||||
|
m_DirtyStaticShapes.push_back(staticId);
|
||||||
|
|
||||||
|
std::vector<u32> unitsNear;
|
||||||
|
m_UnitSubdivision.GetNear(staticsNear, center, extendedRadius);
|
||||||
|
for (u32& unitId : unitsNear)
|
||||||
|
if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end())
|
||||||
|
m_DirtyUnitShapes.push_back(unitId);
|
||||||
|
|
||||||
|
MarkDirtinessGrid(shape.x, shape.z, extendedRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_DebugOverlayDirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -763,61 +809,81 @@ bool CCmpObstructionManager::TestUnitShape(const IObstructionTestFilter& filter,
|
|||||||
return false; // didn't collide, if we got this far
|
return false; // didn't collide, if we got this far
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCmpObstructionManager::Rasterize(Grid<u16>& grid, const entity_pos_t& expand, ICmpObstructionManager::flags_t requireMask, u16 setMask)
|
void CCmpObstructionManager::Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, ICmpObstructionManager::flags_t requireMask, bool fullUpdate)
|
||||||
{
|
{
|
||||||
PROFILE3("Rasterize");
|
PROFILE3("Rasterize");
|
||||||
|
|
||||||
// Since m_DirtyID is only updated for pathfinding/foundation blocking shapes,
|
// The update informations are only updated when pathfinding/foundation blocking shapes are modified.
|
||||||
// NeedUpdate+Rasterize will only be accurate for that subset of shapes.
|
|
||||||
// (If we ever want to support rasterizing more shapes, we need to improve
|
|
||||||
// the dirty-detection system too.)
|
|
||||||
ENSURE(!(requireMask & ~(FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION)));
|
ENSURE(!(requireMask & ~(FLAG_BLOCK_PATHFINDING|FLAG_BLOCK_FOUNDATION)));
|
||||||
|
|
||||||
// Cells are only marked as blocked if the whole cell is strictly inside the shape.
|
// Cells are only marked as blocked if the whole cell is strictly inside the shape.
|
||||||
// (That ensures the shape's geometric border is always reachable.)
|
// (That ensures the shape's geometric border is always reachable.)
|
||||||
|
|
||||||
// TODO: it might be nice to rasterize with rounded corners for large 'expand' values.
|
// Add obstructions onto the grid, for any class with (possibly zero) clearance
|
||||||
|
std::map<entity_pos_t, u16> combinedMasks;
|
||||||
// (This could be implemented much more efficiently.)
|
for (const PathfinderPassability& passability : passClasses)
|
||||||
|
|
||||||
for (auto& pair : m_StaticShapes)
|
|
||||||
{
|
{
|
||||||
const StaticShape& shape = pair.second;
|
if (!passability.m_HasClearance)
|
||||||
if (!(shape.flags & requireMask))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ObstructionSquare square = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh };
|
auto it = combinedMasks.find(passability.m_Clearance);
|
||||||
SimRasterize::Spans spans;
|
if (it == combinedMasks.end())
|
||||||
SimRasterize::RasterizeRectWithClearance(spans, square, expand, Pathfinding::NAVCELL_SIZE);
|
combinedMasks[passability.m_Clearance] = passability.m_Mask;
|
||||||
for (SimRasterize::Span& span : spans)
|
else
|
||||||
|
it->second |= passability.m_Mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& maskPair : combinedMasks)
|
||||||
|
{
|
||||||
|
for (auto& pair : m_StaticShapes)
|
||||||
{
|
{
|
||||||
i16 j = span.j;
|
if (!fullUpdate && std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), pair.first) == m_DirtyStaticShapes.end())
|
||||||
if (j >= 0 && j <= grid.m_H)
|
continue;
|
||||||
|
|
||||||
|
const StaticShape& shape = pair.second;
|
||||||
|
if (!(shape.flags & requireMask))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO: it might be nice to rasterize with rounded corners for large 'expand' values.
|
||||||
|
ObstructionSquare square = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh };
|
||||||
|
SimRasterize::Spans spans;
|
||||||
|
SimRasterize::RasterizeRectWithClearance(spans, square, maskPair.first, Pathfinding::NAVCELL_SIZE);
|
||||||
|
for (SimRasterize::Span& span : spans)
|
||||||
{
|
{
|
||||||
i16 i0 = std::max(span.i0, (i16)0);
|
i16 j = span.j;
|
||||||
i16 i1 = std::min(span.i1, (i16)grid.m_W);
|
if (j >= 0 && j <= grid.m_H)
|
||||||
for (i16 i = i0; i < i1; ++i)
|
{
|
||||||
grid.set(i, j, grid.get(i, j) | setMask);
|
i16 i0 = std::max(span.i0, (i16)0);
|
||||||
|
i16 i1 = std::min(span.i1, (i16)grid.m_W);
|
||||||
|
for (i16 i = i0; i < i1; ++i)
|
||||||
|
grid.set(i, j, grid.get(i, j) | maskPair.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& pair : m_UnitShapes)
|
||||||
|
{
|
||||||
|
if (!fullUpdate && std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), pair.first) == m_DirtyUnitShapes.end())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CFixedVector2D center(pair.second.x, pair.second.z);
|
||||||
|
|
||||||
|
if (!(pair.second.flags & requireMask))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
entity_pos_t r = pair.second.r + maskPair.first;
|
||||||
|
|
||||||
|
u16 i0, j0, i1, j1;
|
||||||
|
Pathfinding::NearestNavcell(center.X - r, center.Y - r, i0, j0, grid.m_W, grid.m_H);
|
||||||
|
Pathfinding::NearestNavcell(center.X + r, center.Y + r, i1, j1, grid.m_W, grid.m_H);
|
||||||
|
for (u16 j = j0+1; j < j1; ++j)
|
||||||
|
for (u16 i = i0+1; i < i1; ++i)
|
||||||
|
grid.set(i, j, grid.get(i, j) | maskPair.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& pair : m_UnitShapes)
|
m_DirtyStaticShapes.clear();
|
||||||
{
|
m_DirtyUnitShapes.clear();
|
||||||
CFixedVector2D center(pair.second.x, pair.second.z);
|
|
||||||
|
|
||||||
if (!(pair.second.flags & requireMask))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
entity_pos_t r = pair.second.r + expand;
|
|
||||||
|
|
||||||
u16 i0, j0, i1, j1;
|
|
||||||
Pathfinding::NearestNavcell(center.X - r, center.Y - r, i0, j0, grid.m_W, grid.m_H);
|
|
||||||
Pathfinding::NearestNavcell(center.X + r, center.Y + r, i1, j1, grid.m_W, grid.m_H);
|
|
||||||
for (u16 j = j0+1; j < j1; ++j)
|
|
||||||
for (u16 i = i0+1; i < i1; ++i)
|
|
||||||
grid.set(i, j, grid.get(i, j) | setMask);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCmpObstructionManager::GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares)
|
void CCmpObstructionManager::GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares)
|
||||||
@ -877,17 +943,11 @@ void CCmpObstructionManager::GetUnitsOnObstruction(const ObstructionSquare& squa
|
|||||||
// units s.t. the RasterizeRectWithClearance of the building's shape with the
|
// units s.t. the RasterizeRectWithClearance of the building's shape with the
|
||||||
// unit's clearance covers the navcell the unit is on.
|
// unit's clearance covers the navcell the unit is on.
|
||||||
|
|
||||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
|
|
||||||
if (!cmpPathfinder)
|
|
||||||
return;
|
|
||||||
|
|
||||||
entity_pos_t maxClearance = cmpPathfinder->GetMaximumClearance();
|
|
||||||
|
|
||||||
std::vector<entity_id_t> unitShapes;
|
std::vector<entity_id_t> unitShapes;
|
||||||
CFixedVector2D center(square.x, square.z);
|
CFixedVector2D center(square.x, square.z);
|
||||||
CFixedVector2D expandedBox =
|
CFixedVector2D expandedBox =
|
||||||
Geometry::GetHalfBoundingBox(square.u, square.v, CFixedVector2D(square.hw, square.hh)) +
|
Geometry::GetHalfBoundingBox(square.u, square.v, CFixedVector2D(square.hw, square.hh)) +
|
||||||
CFixedVector2D(maxClearance, maxClearance);
|
CFixedVector2D(m_MaxClearance, m_MaxClearance);
|
||||||
m_UnitSubdivision.GetInRange(unitShapes, center - expandedBox, center + expandedBox);
|
m_UnitSubdivision.GetInRange(unitShapes, center - expandedBox, center + expandedBox);
|
||||||
|
|
||||||
std::map<entity_pos_t, SimRasterize::Spans> rasterizedRects;
|
std::map<entity_pos_t, SimRasterize::Spans> rasterizedRects;
|
||||||
|
@ -49,10 +49,7 @@ void CCmpPathfinder::Init(const CParamNode& UNUSED(paramNode))
|
|||||||
{
|
{
|
||||||
m_MapSize = 0;
|
m_MapSize = 0;
|
||||||
m_Grid = NULL;
|
m_Grid = NULL;
|
||||||
m_BaseGrid = NULL;
|
m_TerrainOnlyGrid = NULL;
|
||||||
|
|
||||||
m_ObstructionsDirty = true;
|
|
||||||
m_TerrainDirty = true;
|
|
||||||
|
|
||||||
m_NextAsyncTicket = 1;
|
m_NextAsyncTicket = 1;
|
||||||
|
|
||||||
@ -104,7 +101,7 @@ void CCmpPathfinder::Deinit()
|
|||||||
SetDebugOverlay(false); // cleans up memory
|
SetDebugOverlay(false); // cleans up memory
|
||||||
|
|
||||||
SAFE_DELETE(m_Grid);
|
SAFE_DELETE(m_Grid);
|
||||||
SAFE_DELETE(m_BaseGrid);
|
SAFE_DELETE(m_TerrainOnlyGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SerializeLongRequest
|
struct SerializeLongRequest
|
||||||
@ -170,17 +167,12 @@ void CCmpPathfinder::HandleMessage(const CMessage& msg, bool UNUSED(global))
|
|||||||
case MT_TerrainChanged:
|
case MT_TerrainChanged:
|
||||||
case MT_WaterChanged:
|
case MT_WaterChanged:
|
||||||
case MT_ObstructionMapShapeChanged:
|
case MT_ObstructionMapShapeChanged:
|
||||||
{
|
|
||||||
// TODO PATHFINDER: we ought to only bother updating the dirtied region
|
|
||||||
m_TerrainDirty = true;
|
m_TerrainDirty = true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case MT_TurnStart:
|
case MT_TurnStart:
|
||||||
{
|
|
||||||
m_SameTurnMovesCount = 0;
|
m_SameTurnMovesCount = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCmpPathfinder::RenderSubmit(SceneCollector& collector)
|
void CCmpPathfinder::RenderSubmit(SceneCollector& collector)
|
||||||
@ -221,7 +213,9 @@ const PathfinderPassability* CCmpPathfinder::GetPassabilityFromMask(pass_class_t
|
|||||||
|
|
||||||
const Grid<u16>& CCmpPathfinder::GetPassabilityGrid()
|
const Grid<u16>& CCmpPathfinder::GetPassabilityGrid()
|
||||||
{
|
{
|
||||||
UpdateGrid();
|
if (!m_Grid)
|
||||||
|
UpdateGrid();
|
||||||
|
|
||||||
return *m_Grid;
|
return *m_Grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,14 +467,13 @@ void CCmpPathfinder::UpdateGrid()
|
|||||||
if (!cmpTerrain)
|
if (!cmpTerrain)
|
||||||
return; // error
|
return; // error
|
||||||
|
|
||||||
bool forceGlobalUpdate = false;
|
m_ObstructionsDirty.Clean();
|
||||||
|
|
||||||
// If the terrain was resized then delete the old grid data
|
// If the terrain was resized then delete the old grid data
|
||||||
if (m_Grid && m_MapSize != cmpTerrain->GetTilesPerSide())
|
if (m_Grid && m_MapSize != cmpTerrain->GetTilesPerSide())
|
||||||
{
|
{
|
||||||
SAFE_DELETE(m_Grid);
|
SAFE_DELETE(m_Grid);
|
||||||
SAFE_DELETE(m_BaseGrid);
|
SAFE_DELETE(m_TerrainOnlyGrid);
|
||||||
m_TerrainDirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the terrain data when first needed
|
// Initialise the terrain data when first needed
|
||||||
@ -488,25 +481,23 @@ void CCmpPathfinder::UpdateGrid()
|
|||||||
{
|
{
|
||||||
m_MapSize = cmpTerrain->GetTilesPerSide();
|
m_MapSize = cmpTerrain->GetTilesPerSide();
|
||||||
m_Grid = new Grid<NavcellData>(m_MapSize * Pathfinding::NAVCELLS_PER_TILE, m_MapSize * Pathfinding::NAVCELLS_PER_TILE);
|
m_Grid = new Grid<NavcellData>(m_MapSize * Pathfinding::NAVCELLS_PER_TILE, m_MapSize * Pathfinding::NAVCELLS_PER_TILE);
|
||||||
m_BaseGrid = new Grid<NavcellData>(m_MapSize * Pathfinding::NAVCELLS_PER_TILE, m_MapSize * Pathfinding::NAVCELLS_PER_TILE);
|
m_TerrainOnlyGrid = new Grid<NavcellData>(m_MapSize * Pathfinding::NAVCELLS_PER_TILE, m_MapSize * Pathfinding::NAVCELLS_PER_TILE);
|
||||||
forceGlobalUpdate = true;
|
|
||||||
|
m_ObstructionsDirty.dirty = true;
|
||||||
|
m_ObstructionsDirty.globallyDirty = true;
|
||||||
|
m_ObstructionsDirty.globalRecompute = true;
|
||||||
|
|
||||||
m_TerrainDirty = true;
|
m_TerrainDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
|
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
|
||||||
if (forceGlobalUpdate)
|
cmpObstructionManager->UpdateInformations(m_ObstructionsDirty);
|
||||||
{
|
|
||||||
m_ObstructionsDirty = true;
|
|
||||||
m_ObstructionsGlobalUpdate = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_ObstructionsDirty = cmpObstructionManager->GetDirtinessData(m_DirtinessGrid, m_ObstructionsGlobalUpdate);
|
|
||||||
|
|
||||||
if (!m_ObstructionsDirty && !m_TerrainDirty)
|
if (!m_ObstructionsDirty.dirty && !m_TerrainDirty)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the terrain has changed, recompute entirely m_Grid
|
// If the terrain has changed, recompute m_Grid
|
||||||
// Else, use data from m_BaseGrid and add obstructions
|
// Else, use data from m_TerrainOnlyGrid and add obstructions
|
||||||
if (m_TerrainDirty)
|
if (m_TerrainDirty)
|
||||||
{
|
{
|
||||||
Grid<u16> shoreGrid = ComputeShoreGrid();
|
Grid<u16> shoreGrid = ComputeShoreGrid();
|
||||||
@ -576,39 +567,43 @@ void CCmpPathfinder::UpdateGrid()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store the updated terrain-only grid
|
// Store the updated terrain-only grid
|
||||||
*m_BaseGrid = *m_Grid;
|
*m_TerrainOnlyGrid = *m_Grid;
|
||||||
|
|
||||||
m_TerrainDirty = false;
|
m_TerrainDirty = false;
|
||||||
|
m_ObstructionsDirty.globalRecompute = true;
|
||||||
|
m_ObstructionsDirty.globallyDirty = true;
|
||||||
|
}
|
||||||
|
else if (m_ObstructionsDirty.globalRecompute)
|
||||||
|
{
|
||||||
|
ENSURE(m_Grid->m_W == m_TerrainOnlyGrid->m_W && m_Grid->m_H == m_TerrainOnlyGrid->m_H);
|
||||||
|
memcpy(m_Grid->m_Data, m_TerrainOnlyGrid->m_Data, (m_Grid->m_W)*(m_Grid->m_H)*sizeof(NavcellData));
|
||||||
|
|
||||||
|
m_ObstructionsDirty.globallyDirty = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ENSURE(m_Grid->m_W == m_BaseGrid->m_W && m_Grid->m_H == m_BaseGrid->m_H);
|
ENSURE(m_Grid->m_W == m_ObstructionsDirty.dirtinessGrid.m_W && m_Grid->m_H == m_ObstructionsDirty.dirtinessGrid.m_H);
|
||||||
memcpy(m_Grid->m_Data, m_BaseGrid->m_Data, (m_Grid->m_W)*(m_Grid->m_H)*sizeof(NavcellData));
|
ENSURE(m_Grid->m_W == m_TerrainOnlyGrid->m_W && m_Grid->m_H == m_TerrainOnlyGrid->m_H);
|
||||||
|
|
||||||
|
for (u16 i = 0; i < m_ObstructionsDirty.dirtinessGrid.m_W; ++i)
|
||||||
|
for (u16 j = 0; j < m_ObstructionsDirty.dirtinessGrid.m_H; ++j)
|
||||||
|
if (m_ObstructionsDirty.dirtinessGrid.get(i, j) == 1)
|
||||||
|
m_Grid->set(i, j, m_TerrainOnlyGrid->get(i, j));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: tweak rasterizing conditions
|
// Add obstructions onto the grid
|
||||||
// Add obstructions onto the grid, for any class with (possibly zero) clearance
|
cmpObstructionManager->Rasterize(*m_Grid, m_PassClasses, ICmpObstructionManager::FLAG_BLOCK_PATHFINDING, m_ObstructionsDirty.globalRecompute);
|
||||||
for (PathfinderPassability& passability : m_PassClasses)
|
|
||||||
{
|
|
||||||
// TODO: if multiple classes have the same clearance, we should
|
|
||||||
// only bother running Rasterize once for them all
|
|
||||||
if (passability.m_HasClearance)
|
|
||||||
cmpObstructionManager->Rasterize(*m_Grid, passability.m_Clearance, ICmpObstructionManager::FLAG_BLOCK_PATHFINDING, passability.m_Mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_ObstructionsGlobalUpdate)
|
// Update the long-range pathfinder
|
||||||
|
if (m_ObstructionsDirty.globallyDirty)
|
||||||
m_LongPathfinder.Reload(m_PassClassMasks, m_Grid);
|
m_LongPathfinder.Reload(m_PassClassMasks, m_Grid);
|
||||||
else
|
else
|
||||||
m_LongPathfinder.Update(m_Grid, &m_DirtinessGrid);
|
m_LongPathfinder.Update(m_Grid, m_ObstructionsDirty.dirtinessGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCmpPathfinder::GetDirtinessData(Grid<u8>& dirtinessGrid, bool& globalUpdateNeeded)
|
const GridUpdateInformation& CCmpPathfinder::GetDirtinessData() const
|
||||||
{
|
{
|
||||||
if (!m_ObstructionsDirty)
|
return m_ObstructionsDirty;
|
||||||
return false;
|
|
||||||
|
|
||||||
dirtinessGrid = m_DirtinessGrid;
|
|
||||||
globalUpdateNeeded = m_ObstructionsGlobalUpdate;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
@ -778,8 +773,6 @@ ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckBuildingPlacement(const I
|
|||||||
|
|
||||||
// Test against terrain:
|
// Test against terrain:
|
||||||
|
|
||||||
UpdateGrid();
|
|
||||||
|
|
||||||
ICmpObstructionManager::ObstructionSquare square;
|
ICmpObstructionManager::ObstructionSquare square;
|
||||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id);
|
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id);
|
||||||
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square))
|
if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square))
|
||||||
|
@ -106,13 +106,11 @@ public:
|
|||||||
|
|
||||||
u16 m_MapSize; // tiles per side
|
u16 m_MapSize; // tiles per side
|
||||||
Grid<NavcellData>* m_Grid; // terrain/passability information
|
Grid<NavcellData>* m_Grid; // terrain/passability information
|
||||||
Grid<NavcellData>* m_BaseGrid; // same as m_Grid, but only with terrain, to avoid some recomputations
|
Grid<NavcellData>* m_TerrainOnlyGrid; // same as m_Grid, but only with terrain, to avoid some recomputations
|
||||||
bool m_TerrainDirty; // indicates if m_Grid has been updated since terrain changed
|
|
||||||
|
|
||||||
// Update data, stored for the AI manager
|
// Update data, used for clever updates and then stored for the AI manager
|
||||||
bool m_ObstructionsDirty;
|
GridUpdateInformation m_ObstructionsDirty;
|
||||||
bool m_ObstructionsGlobalUpdate;
|
bool m_TerrainDirty;
|
||||||
Grid<u8> m_DirtinessGrid;
|
|
||||||
|
|
||||||
// Interface to the long-range pathfinder.
|
// Interface to the long-range pathfinder.
|
||||||
LongPathfinder m_LongPathfinder;
|
LongPathfinder m_LongPathfinder;
|
||||||
@ -170,7 +168,7 @@ public:
|
|||||||
|
|
||||||
virtual const Grid<u16>& GetPassabilityGrid();
|
virtual const Grid<u16>& GetPassabilityGrid();
|
||||||
|
|
||||||
virtual bool GetDirtinessData(Grid<u8>& dirtinessGrid, bool& globalUpdateNeeded);
|
virtual const GridUpdateInformation& GetDirtinessData() const;
|
||||||
|
|
||||||
virtual Grid<u16> ComputeShoreGrid(bool expandOnWater = false);
|
virtual Grid<u16> ComputeShoreGrid(bool expandOnWater = false);
|
||||||
|
|
||||||
|
@ -20,8 +20,7 @@
|
|||||||
|
|
||||||
#include "simulation2/system/Interface.h"
|
#include "simulation2/system/Interface.h"
|
||||||
|
|
||||||
#include "simulation2/helpers/Grid.h"
|
#include "simulation2/helpers/Pathfinding.h"
|
||||||
#include "simulation2/helpers/Position.h"
|
|
||||||
|
|
||||||
#include "maths/FixedVector2D.h"
|
#include "maths/FixedVector2D.h"
|
||||||
|
|
||||||
@ -217,19 +216,18 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the current set of shapes onto a navcell grid.
|
* Convert the current set of shapes onto a navcell grid.
|
||||||
* Shapes are expanded by the clearance radius @p expand.
|
* If @p fullUpdate is false, the function will only go through dirty shapes.
|
||||||
|
* Shapes are expanded by the @p passClasses clearances, by ORing their masks onto the @p grid.
|
||||||
* Only shapes with at least one of the flags from @p requireMask will be considered.
|
* Only shapes with at least one of the flags from @p requireMask will be considered.
|
||||||
* @p setMask will be ORed onto the @p grid value for all navcells
|
|
||||||
* that are wholly enclosed by an expanded shape.
|
|
||||||
*/
|
*/
|
||||||
virtual void Rasterize(Grid<u16>& grid, const entity_pos_t& expand, ICmpObstructionManager::flags_t requireMask, u16 setMask) = 0;
|
virtual void Rasterize(Grid<u16>& grid, const std::vector<PathfinderPassability>& passClasses, ICmpObstructionManager::flags_t requireMask, bool fullUpdate) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets dirtiness information and resets it afterwards. Then it's the role of CCmpPathfinder
|
* Gets dirtiness information and resets it afterwards. Then it's the role of CCmpPathfinder
|
||||||
* to pass the information to other components if needed. (AIs, etc.)
|
* to pass the information to other components if needed. (AIs, etc.)
|
||||||
* The return value is false if an update is unnecessary.
|
* The return value is false if an update is unnecessary.
|
||||||
*/
|
*/
|
||||||
virtual bool GetDirtinessData(Grid<u8>& dirtinessGrid, bool& globalUpdateNeeded) = 0;
|
virtual void UpdateInformations(GridUpdateInformation& informations) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard representation for all types of shapes, for use with geometry processing code.
|
* Standard representation for all types of shapes, for use with geometry processing code.
|
||||||
|
@ -74,7 +74,7 @@ public:
|
|||||||
* Passes the lazily-stored dirtiness data collected from
|
* Passes the lazily-stored dirtiness data collected from
|
||||||
* the obstruction manager during the previous grid update.
|
* the obstruction manager during the previous grid update.
|
||||||
*/
|
*/
|
||||||
virtual bool GetDirtinessData(Grid<u8>& dirtinessGrid, bool& globalUpdateNeeded) = 0;
|
virtual const GridUpdateInformation& GetDirtinessData() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a grid representing the distance to the shore of the terrain tile.
|
* Get a grid representing the distance to the shore of the terrain tile.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2013 Wildfire Games.
|
/* Copyright (C) 2015 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@ -185,4 +185,29 @@ public:
|
|||||||
size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated
|
size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure holding grid dirtiness informations, for clever updates
|
||||||
|
*
|
||||||
|
* Note: globallyDirty should be used to know which parts of the grid have been changed after an update,
|
||||||
|
* whereas globalRecompute should be used during the update itself, to avoid unnecessary recomputations.
|
||||||
|
*/
|
||||||
|
struct GridUpdateInformation
|
||||||
|
{
|
||||||
|
bool dirty;
|
||||||
|
bool globallyDirty;
|
||||||
|
bool globalRecompute;
|
||||||
|
Grid<u8> dirtinessGrid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark everything as clean
|
||||||
|
*/
|
||||||
|
void Clean()
|
||||||
|
{
|
||||||
|
dirty = false;
|
||||||
|
globallyDirty = false;
|
||||||
|
globalRecompute = false;
|
||||||
|
dirtinessGrid.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif // INCLUDED_GRID
|
#endif // INCLUDED_GRID
|
||||||
|
@ -300,16 +300,16 @@ void HierarchicalPathfinder::Recompute(const std::map<std::string, pass_class_t>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HierarchicalPathfinder::Update(Grid<NavcellData>* grid, const Grid<u8>* dirtinessGrid)
|
void HierarchicalPathfinder::Update(Grid<NavcellData>* grid, const Grid<u8>& dirtinessGrid)
|
||||||
{
|
{
|
||||||
PROFILE3("Hierarchical Update");
|
PROFILE3("Hierarchical Update");
|
||||||
|
|
||||||
std::vector<std::pair<int, int> > processedChunks;
|
std::vector<std::pair<int, int> > processedChunks;
|
||||||
for (int j = 0; j < dirtinessGrid->m_H; ++j)
|
for (int j = 0; j < dirtinessGrid.m_H; ++j)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < dirtinessGrid->m_W; ++i)
|
for (int i = 0; i < dirtinessGrid.m_W; ++i)
|
||||||
{
|
{
|
||||||
if (!dirtinessGrid->get(i, j))
|
if (!dirtinessGrid.get(i, j))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::pair<int, int> chunkID(i / CHUNK_SIZE, j / CHUNK_SIZE);
|
std::pair<int, int> chunkID(i / CHUNK_SIZE, j / CHUNK_SIZE);
|
||||||
|
@ -80,7 +80,7 @@ public:
|
|||||||
void SetDebugOverlay(bool enabled, const CSimContext* simContext);
|
void SetDebugOverlay(bool enabled, const CSimContext* simContext);
|
||||||
|
|
||||||
void Recompute(const std::map<std::string, pass_class_t>& passClassMasks, Grid<NavcellData>* passabilityGrid);
|
void Recompute(const std::map<std::string, pass_class_t>& passClassMasks, Grid<NavcellData>* passabilityGrid);
|
||||||
void Update(Grid<NavcellData>* grid, const Grid<u8>* dirtinessGrid);
|
void Update(Grid<NavcellData>* grid, const Grid<u8>& dirtinessGrid);
|
||||||
|
|
||||||
RegionID Get(u16 i, u16 j, pass_class_t passClass);
|
RegionID Get(u16 i, u16 j, pass_class_t passClass);
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ public:
|
|||||||
m_PathfinderHier.Recompute(passClassMasks, passabilityGrid);
|
m_PathfinderHier.Recompute(passClassMasks, passabilityGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update(Grid<NavcellData>* passabilityGrid, const Grid<u8>* dirtinessGrid)
|
void Update(Grid<NavcellData>* passabilityGrid, const Grid<u8>& dirtinessGrid)
|
||||||
{
|
{
|
||||||
m_Grid = passabilityGrid;
|
m_Grid = passabilityGrid;
|
||||||
ASSERT(passabilityGrid->m_H == passabilityGrid->m_W);
|
ASSERT(passabilityGrid->m_H == passabilityGrid->m_W);
|
||||||
|
Loading…
Reference in New Issue
Block a user