mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
Remove classes inheriting TerrainTextureOverlay
Those classes only exposed one function. It's better to use `std::function` for that, because it's not intrusive.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -342,8 +342,11 @@ void TerrainOverlay::RenderTileOutline(
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TerrainTextureOverlay::TerrainTextureOverlay(float texelsPerTile) :
|
||||
ITerrainOverlay(100), m_TexelsPerTile(texelsPerTile)
|
||||
TerrainTextureOverlay::TerrainTextureOverlay(float texelsPerTile,
|
||||
std::function<void(std::uint8_t*, std::size_t, std::size_t)> buildTextureRGBA) :
|
||||
ITerrainOverlay{100},
|
||||
m_BuildTextureRGBA{std::move(buildTextureRGBA)},
|
||||
m_TexelsPerTile{texelsPerTile}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -373,7 +376,7 @@ void TerrainTextureOverlay::RenderAfterWater(
|
||||
}
|
||||
|
||||
u8* data = (u8*)calloc(w * h, 4);
|
||||
BuildTextureRGBA(data, w, h);
|
||||
m_BuildTextureRGBA(data, w, h);
|
||||
|
||||
deviceCommandContext->UploadTextureRegion(
|
||||
m_Texture.get(), Renderer::Backend::Format::R8G8B8A8_UNORM, data, w * h * 4, 0, 0, w, h);
|
||||
@@ -387,7 +390,7 @@ void TerrainTextureOverlay::RenderAfterWater(
|
||||
deviceCommandContext, cullGroup, textureTransform, m_Texture.get());
|
||||
}
|
||||
|
||||
SColor4ub TerrainTextureOverlay::GetColor(size_t idx, u8 alpha) const
|
||||
SColor4ub TerrainTextureOverlay::GetColor(std::size_t idx, std::uint8_t alpha)
|
||||
{
|
||||
static u8 colors[][3] =
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "lib/types.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class CSimContext;
|
||||
@@ -197,31 +198,25 @@ private:
|
||||
* texels per terrain tile, intended for debugging purposes.
|
||||
* Subclasses must implement BuildTextureRGBA which will be called each frame.
|
||||
*/
|
||||
class TerrainTextureOverlay : public ITerrainOverlay
|
||||
class TerrainTextureOverlay final : public ITerrainOverlay
|
||||
{
|
||||
public:
|
||||
TerrainTextureOverlay(float texelsPerTile);
|
||||
TerrainTextureOverlay(float texelsPerTile,
|
||||
std::function<void(std::uint8_t*, std::size_t, std::size_t)> buildTextureRGBA);
|
||||
|
||||
~TerrainTextureOverlay() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called each frame to generate the texture to render on the terrain.
|
||||
* @p data is w*h*4 bytes, where w and h are the terrain size multiplied
|
||||
* by texelsPerTile. @p data defaults to fully transparent, and should
|
||||
* be filled with data in RGBA order.
|
||||
*/
|
||||
virtual void BuildTextureRGBA(u8* data, size_t w, size_t h) = 0;
|
||||
~TerrainTextureOverlay() final;
|
||||
|
||||
/**
|
||||
* Returns an arbitrary color, for subclasses that want to distinguish
|
||||
* different integers visually.
|
||||
*/
|
||||
SColor4ub GetColor(size_t idx, u8 alpha) const;
|
||||
static SColor4ub GetColor(std::size_t idx, std::uint8_t alpha);
|
||||
|
||||
private:
|
||||
void RenderAfterWater(
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext, int cullGroup) override;
|
||||
Renderer::Backend::IDeviceCommandContext* deviceCommandContext, int cullGroup) final;
|
||||
|
||||
std::function<void(std::uint8_t*, std::size_t, std::size_t)> m_BuildTextureRGBA;
|
||||
|
||||
float m_TexelsPerTile;
|
||||
std::unique_ptr<Renderer::Backend::ITexture> m_Texture;
|
||||
|
||||
@@ -76,6 +76,33 @@ class SceneCollector;
|
||||
|
||||
REGISTER_COMPONENT_TYPE(Pathfinder)
|
||||
|
||||
namespace
|
||||
{
|
||||
void BuildTextureRGBA(const CCmpPathfinder& pathfinder, std::uint8_t* data,
|
||||
std::size_t w, std::size_t h)
|
||||
{
|
||||
// Render navcell passability, based on the terrain-only grid
|
||||
u8* p = data;
|
||||
for (size_t j = 0; j < h; ++j)
|
||||
{
|
||||
for (size_t i = 0; i < w; ++i)
|
||||
{
|
||||
SColor4ub color(0, 0, 0, 0);
|
||||
if (!IS_PASSABLE(pathfinder.m_TerrainOnlyGrid->get(static_cast<int>(i),
|
||||
static_cast<int>(j)), pathfinder.m_OverlayPassClass))
|
||||
{
|
||||
color = SColor4ub(255, 0, 0, 127);
|
||||
}
|
||||
|
||||
*p++ = color.R;
|
||||
*p++ = color.G;
|
||||
*p++ = color.B;
|
||||
*p++ = color.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpPathfinder::Init(const CParamNode&)
|
||||
{
|
||||
m_GridSize = 0;
|
||||
@@ -262,9 +289,12 @@ void CCmpPathfinder::SetAtlasOverlay(bool enable, pass_class_t passClass)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
m_OverlayPassClass = passClass;
|
||||
if (!m_AtlasOverlay)
|
||||
m_AtlasOverlay = new AtlasOverlay(this, passClass);
|
||||
m_AtlasOverlay->m_PassClass = passClass;
|
||||
{
|
||||
m_AtlasOverlay = new TerrainTextureOverlay{Pathfinding::NAVCELLS_PER_TERRAIN_TILE,
|
||||
std::bind_front(BuildTextureRGBA, std::ref(*this))};
|
||||
}
|
||||
}
|
||||
else
|
||||
SAFE_DELETE(m_AtlasOverlay);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -51,7 +51,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class AtlasOverlay;
|
||||
class SceneCollector;
|
||||
|
||||
#ifdef NDEBUG
|
||||
@@ -147,7 +146,8 @@ public:
|
||||
|
||||
u32 m_NextAsyncTicket; // Unique IDs for asynchronous path requests.
|
||||
|
||||
AtlasOverlay* m_AtlasOverlay;
|
||||
pass_class_t m_OverlayPassClass;
|
||||
TerrainTextureOverlay* m_AtlasOverlay;
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
@@ -270,36 +270,4 @@ public:
|
||||
void RenderSubmit(SceneCollector& collector);
|
||||
};
|
||||
|
||||
class AtlasOverlay : public TerrainTextureOverlay
|
||||
{
|
||||
public:
|
||||
const CCmpPathfinder* m_Pathfinder;
|
||||
pass_class_t m_PassClass;
|
||||
|
||||
AtlasOverlay(const CCmpPathfinder* pathfinder, pass_class_t passClass) :
|
||||
TerrainTextureOverlay(Pathfinding::NAVCELLS_PER_TERRAIN_TILE), m_Pathfinder(pathfinder), m_PassClass(passClass)
|
||||
{
|
||||
}
|
||||
|
||||
void BuildTextureRGBA(u8* data, size_t w, size_t h) override
|
||||
{
|
||||
// Render navcell passability, based on the terrain-only grid
|
||||
u8* p = data;
|
||||
for (size_t j = 0; j < h; ++j)
|
||||
{
|
||||
for (size_t i = 0; i < w; ++i)
|
||||
{
|
||||
SColor4ub color(0, 0, 0, 0);
|
||||
if (!IS_PASSABLE(m_Pathfinder->m_TerrainOnlyGrid->get((int)i, (int)j), m_PassClass))
|
||||
color = SColor4ub(255, 0, 0, 127);
|
||||
|
||||
*p++ = color.R;
|
||||
*p++ = color.G;
|
||||
*p++ = color.B;
|
||||
*p++ = color.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // INCLUDED_CCMPPATHFINDER_COMMON
|
||||
|
||||
@@ -73,15 +73,27 @@
|
||||
class CCmpTerritoryManager;
|
||||
class CFrustum;
|
||||
|
||||
class TerritoryOverlay final : public TerrainTextureOverlay
|
||||
namespace
|
||||
{
|
||||
NONCOPYABLE(TerritoryOverlay);
|
||||
public:
|
||||
CCmpTerritoryManager& m_TerritoryManager;
|
||||
constexpr bool DISABLE_TERRITORY_OVERLAY{true};
|
||||
|
||||
TerritoryOverlay(CCmpTerritoryManager& manager);
|
||||
void BuildTextureRGBA(u8* data, size_t w, size_t h) override;
|
||||
};
|
||||
void BuildTextureRGBA(Grid<u8>*& territories, u8* data, size_t w, size_t h)
|
||||
{
|
||||
for (size_t j = 0; j < h; ++j)
|
||||
{
|
||||
for (size_t i = 0; i < w; ++i)
|
||||
{
|
||||
SColor4ub color;
|
||||
u8 id = (territories->get((int)i, (int)j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK);
|
||||
color = TerrainTextureOverlay::GetColor(id, 64);
|
||||
*data++ = color.R;
|
||||
*data++ = color.G;
|
||||
*data++ = color.B;
|
||||
*data++ = color.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CCmpTerritoryManager : public ICmpTerritoryManager
|
||||
{
|
||||
@@ -140,7 +152,7 @@ public:
|
||||
|
||||
double m_AnimTime; // time since start of rendering, in seconds
|
||||
|
||||
TerritoryOverlay* m_DebugOverlay;
|
||||
TerrainTextureOverlay* m_DebugOverlay;
|
||||
|
||||
bool m_EnableLineDebugOverlays; ///< Enable node debugging overlays for boundary lines?
|
||||
std::vector<SOverlayLine> m_DebugBoundaryLineNodes;
|
||||
@@ -149,8 +161,10 @@ public:
|
||||
{
|
||||
m_Territories = NULL;
|
||||
m_CostGrid = NULL;
|
||||
m_DebugOverlay = NULL;
|
||||
// m_DebugOverlay = new TerritoryOverlay(*this);
|
||||
m_DebugOverlay = DISABLE_TERRITORY_OVERLAY? nullptr :
|
||||
new TerrainTextureOverlay{static_cast<float>(Pathfinding::NAVCELLS_PER_TERRAIN_TILE) /
|
||||
ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE,
|
||||
std::bind_front(BuildTextureRGBA, std::ref(this->m_Territories))};
|
||||
m_BoundaryLinesDirty = true;
|
||||
m_TriggerEvent = true;
|
||||
m_EnableLineDebugOverlays = false;
|
||||
@@ -899,25 +913,3 @@ void CCmpTerritoryManager::UpdateColors()
|
||||
boundaryLine.overlay.m_Color = boundaryLine.color;
|
||||
}
|
||||
}
|
||||
|
||||
TerritoryOverlay::TerritoryOverlay(CCmpTerritoryManager& manager) :
|
||||
TerrainTextureOverlay((float)Pathfinding::NAVCELLS_PER_TERRAIN_TILE / ICmpTerritoryManager::NAVCELLS_PER_TERRITORY_TILE),
|
||||
m_TerritoryManager(manager)
|
||||
{ }
|
||||
|
||||
void TerritoryOverlay::BuildTextureRGBA(u8* data, size_t w, size_t h)
|
||||
{
|
||||
for (size_t j = 0; j < h; ++j)
|
||||
{
|
||||
for (size_t i = 0; i < w; ++i)
|
||||
{
|
||||
SColor4ub color;
|
||||
u8 id = (m_TerritoryManager.m_Territories->get((int)i, (int)j) & ICmpTerritoryManager::TERRITORY_PLAYER_MASK);
|
||||
color = GetColor(id, 64);
|
||||
*data++ = color.R;
|
||||
*data++ = color.G;
|
||||
*data++ = color.B;
|
||||
*data++ = color.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -38,8 +38,10 @@
|
||||
|
||||
class CSimContext;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Find the root ID of a region, used by InitRegions
|
||||
inline u16 RootID(u16 x, const std::vector<u16>& v)
|
||||
u16 RootID(u16 x, const std::vector<u16>& v)
|
||||
{
|
||||
while (v[x] < x)
|
||||
x = v[x];
|
||||
@@ -47,6 +49,37 @@ inline u16 RootID(u16 x, const std::vector<u16>& v)
|
||||
return x;
|
||||
}
|
||||
|
||||
void BuildTextureRGBA(HierarchicalPathfinder& pathfinderHier, std::uint8_t* data, std::size_t w,
|
||||
std::size_t h)
|
||||
{
|
||||
ENSURE(h <= std::numeric_limits<u16>::max() && w <= std::numeric_limits<u16>::max());
|
||||
u16 height = static_cast<u16>(h);
|
||||
u16 width = static_cast<u16>(w);
|
||||
pass_class_t passClass = pathfinderHier.GetPassabilityClass("default");
|
||||
|
||||
for (u16 j = 0; j < height; ++j)
|
||||
{
|
||||
for (u16 i = 0; i < width; ++i)
|
||||
{
|
||||
SColor4ub color;
|
||||
|
||||
HierarchicalPathfinder::RegionID rid = pathfinderHier.Get(i, j, passClass);
|
||||
if (rid.r == 0)
|
||||
color = SColor4ub(0, 0, 0, 0);
|
||||
else if (rid.r == 0xFFFF)
|
||||
color = SColor4ub(255, 0, 255, 255);
|
||||
else
|
||||
color = TerrainTextureOverlay::GetColor(rid.r + rid.ci*5 + rid.cj*7, 127);
|
||||
|
||||
*data++ = color.R;
|
||||
*data++ = color.G;
|
||||
*data++ = color.B;
|
||||
*data++ = color.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HierarchicalPathfinder::Chunk::InitRegions(int ci, int cj, Grid<NavcellData>* grid, pass_class_t passClass)
|
||||
{
|
||||
ENSURE(ci < 256 && cj < 256); // avoid overflows
|
||||
@@ -350,7 +383,8 @@ void HierarchicalPathfinder::SetDebugOverlay(bool enabled, const CSimContext* si
|
||||
{
|
||||
if (enabled && !m_DebugOverlay)
|
||||
{
|
||||
m_DebugOverlay = new HierarchicalOverlay(*this);
|
||||
m_DebugOverlay = new TerrainTextureOverlay{Pathfinding::NAVCELLS_PER_TERRAIN_TILE,
|
||||
std::bind_front(BuildTextureRGBA, std::ref(*this))};
|
||||
m_DebugOverlayLines.clear();
|
||||
m_SimContext = simContext;
|
||||
AddDebugEdges(GetPassabilityClass("default"));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -66,7 +66,6 @@ class TestCmpPathfinder;
|
||||
class TestHierarchicalPathfinder;
|
||||
#endif
|
||||
|
||||
class HierarchicalOverlay;
|
||||
class SceneCollector;
|
||||
|
||||
class HierarchicalPathfinder
|
||||
@@ -302,52 +301,10 @@ private:
|
||||
std::map<std::string, pass_class_t> m_PassClassMasks;
|
||||
|
||||
void AddDebugEdges(pass_class_t passClass);
|
||||
HierarchicalOverlay* m_DebugOverlay;
|
||||
TerrainTextureOverlay* m_DebugOverlay;
|
||||
const CSimContext* m_SimContext; // Used for drawing the debug lines
|
||||
|
||||
public:
|
||||
std::vector<SOverlayLine> m_DebugOverlayLines;
|
||||
};
|
||||
|
||||
class HierarchicalOverlay : public TerrainTextureOverlay
|
||||
{
|
||||
public:
|
||||
HierarchicalPathfinder& m_PathfinderHier;
|
||||
|
||||
HierarchicalOverlay(HierarchicalPathfinder& pathfinderHier) :
|
||||
TerrainTextureOverlay(Pathfinding::NAVCELLS_PER_TERRAIN_TILE), m_PathfinderHier(pathfinderHier)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void BuildTextureRGBA(u8* data, size_t w, size_t h)
|
||||
{
|
||||
ENSURE(h <= std::numeric_limits<u16>::max() && w <= std::numeric_limits<u16>::max());
|
||||
u16 height = static_cast<u16>(h);
|
||||
u16 width = static_cast<u16>(w);
|
||||
pass_class_t passClass = m_PathfinderHier.GetPassabilityClass("default");
|
||||
|
||||
for (u16 j = 0; j < height; ++j)
|
||||
{
|
||||
for (u16 i = 0; i < width; ++i)
|
||||
{
|
||||
SColor4ub color;
|
||||
|
||||
HierarchicalPathfinder::RegionID rid = m_PathfinderHier.Get(i, j, passClass);
|
||||
if (rid.r == 0)
|
||||
color = SColor4ub(0, 0, 0, 0);
|
||||
else if (rid.r == 0xFFFF)
|
||||
color = SColor4ub(255, 0, 255, 255);
|
||||
else
|
||||
color = GetColor(rid.r + rid.ci*5 + rid.cj*7, 127);
|
||||
|
||||
*data++ = color.R;
|
||||
*data++ = color.G;
|
||||
*data++ = color.B;
|
||||
*data++ = color.A;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // INCLUDED_HIERPATHFINDER
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -38,6 +38,84 @@
|
||||
namespace
|
||||
{
|
||||
static std::mutex g_DebugMutex;
|
||||
|
||||
void BuildTextureRGBA(LongPathfinder& pathfinder, std::uint8_t* data, std::size_t w, std::size_t h)
|
||||
{
|
||||
// Grab the debug data for the most recently generated path
|
||||
u32 steps;
|
||||
double time;
|
||||
Grid<u8> debugGrid;
|
||||
pathfinder.GetDebugData(steps, time, debugGrid);
|
||||
|
||||
// Render navcell passability
|
||||
u8* p = data;
|
||||
for (size_t j = 0; j < h; ++j)
|
||||
{
|
||||
for (size_t i = 0; i < w; ++i)
|
||||
{
|
||||
SColor4ub color(0, 0, 0, 0);
|
||||
if (!IS_PASSABLE(pathfinder.m_Grid->get(static_cast<int>(i), static_cast<int>(j)),
|
||||
pathfinder.m_Debug.PassClass))
|
||||
{
|
||||
color = SColor4ub(255, 0, 0, 127);
|
||||
}
|
||||
|
||||
if (debugGrid.m_W && debugGrid.m_H)
|
||||
{
|
||||
u8 n = debugGrid.get((int)i, (int)j);
|
||||
|
||||
if (n == 1)
|
||||
color = SColor4ub(255, 255, 0, 127);
|
||||
else if (n == 2)
|
||||
color = SColor4ub(0, 255, 0, 127);
|
||||
|
||||
if (pathfinder.m_Debug.Goal.NavcellContainsGoal(i, j))
|
||||
color = SColor4ub(0, 0, 255, 127);
|
||||
}
|
||||
|
||||
*p++ = color.R;
|
||||
*p++ = color.G;
|
||||
*p++ = color.B;
|
||||
*p++ = color.A;
|
||||
}
|
||||
}
|
||||
|
||||
// Render the most recently generated path
|
||||
if (pathfinder.m_Debug.Path && !pathfinder.m_Debug.Path->m_Waypoints.empty())
|
||||
{
|
||||
std::vector<Waypoint>& waypoints = pathfinder.m_Debug.Path->m_Waypoints;
|
||||
u16 ip = 0, jp = 0;
|
||||
for (size_t k = 0; k < waypoints.size(); ++k)
|
||||
{
|
||||
u16 i, j;
|
||||
Pathfinding::NearestNavcell(waypoints[k].x, waypoints[k].z, i, j, pathfinder.m_GridSize,
|
||||
pathfinder.m_GridSize);
|
||||
if (k == 0)
|
||||
{
|
||||
ip = i;
|
||||
jp = j;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool firstCell = true;
|
||||
do
|
||||
{
|
||||
if (data[(jp*w + ip)*4+3] == 0)
|
||||
{
|
||||
data[(jp*w + ip)*4+0] = 0xFF;
|
||||
data[(jp*w + ip)*4+1] = 0xFF;
|
||||
data[(jp*w + ip)*4+2] = 0xFF;
|
||||
data[(jp*w + ip)*4+3] = firstCell ? 0xA0 : 0x60;
|
||||
}
|
||||
ip = ip < i ? ip+1 : ip > i ? ip-1 : ip;
|
||||
jp = jp < j ? jp+1 : jp > j ? jp-1 : jp;
|
||||
firstCell = false;
|
||||
}
|
||||
while (ip != i || jp != j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,6 +469,24 @@ LongPathfinder::LongPathfinder() :
|
||||
{
|
||||
}
|
||||
|
||||
LongPathfinder::~LongPathfinder()
|
||||
{
|
||||
SAFE_DELETE(m_Debug.Overlay);
|
||||
SAFE_DELETE(m_Debug.Grid);
|
||||
SAFE_DELETE(m_Debug.Path);
|
||||
}
|
||||
|
||||
void LongPathfinder::SetDebugOverlay(bool enabled)
|
||||
{
|
||||
if (enabled && !m_Debug.Overlay)
|
||||
{
|
||||
m_Debug.Overlay = new TerrainTextureOverlay{Pathfinding::NAVCELLS_PER_TERRAIN_TILE,
|
||||
std::bind_front(BuildTextureRGBA, std::ref(*this))};
|
||||
}
|
||||
else if (!enabled && m_Debug.Overlay)
|
||||
SAFE_DELETE(m_Debug.Overlay);
|
||||
}
|
||||
|
||||
#define PASSABLE(i, j) IS_PASSABLE(state.terrain->get(i, j), state.passClass)
|
||||
|
||||
// Calculate heuristic cost from tile i,j to goal
|
||||
@@ -1056,108 +1152,3 @@ void LongPathfinder::GenerateSpecialMap(pass_class_t passClass, std::vector<Circ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terrain overlay for pathfinder debugging.
|
||||
* Renders a representation of the most recent pathfinding operation.
|
||||
*/
|
||||
class LongOverlay : public TerrainTextureOverlay
|
||||
{
|
||||
public:
|
||||
LongPathfinder& m_Pathfinder;
|
||||
|
||||
LongOverlay(LongPathfinder& pathfinder) :
|
||||
TerrainTextureOverlay(Pathfinding::NAVCELLS_PER_TERRAIN_TILE), m_Pathfinder(pathfinder)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void BuildTextureRGBA(u8* data, size_t w, size_t h)
|
||||
{
|
||||
// Grab the debug data for the most recently generated path
|
||||
u32 steps;
|
||||
double time;
|
||||
Grid<u8> debugGrid;
|
||||
m_Pathfinder.GetDebugData(steps, time, debugGrid);
|
||||
|
||||
// Render navcell passability
|
||||
u8* p = data;
|
||||
for (size_t j = 0; j < h; ++j)
|
||||
{
|
||||
for (size_t i = 0; i < w; ++i)
|
||||
{
|
||||
SColor4ub color(0, 0, 0, 0);
|
||||
if (!IS_PASSABLE(m_Pathfinder.m_Grid->get((int)i, (int)j), m_Pathfinder.m_Debug.PassClass))
|
||||
color = SColor4ub(255, 0, 0, 127);
|
||||
|
||||
if (debugGrid.m_W && debugGrid.m_H)
|
||||
{
|
||||
u8 n = debugGrid.get((int)i, (int)j);
|
||||
|
||||
if (n == 1)
|
||||
color = SColor4ub(255, 255, 0, 127);
|
||||
else if (n == 2)
|
||||
color = SColor4ub(0, 255, 0, 127);
|
||||
|
||||
if (m_Pathfinder.m_Debug.Goal.NavcellContainsGoal(i, j))
|
||||
color = SColor4ub(0, 0, 255, 127);
|
||||
}
|
||||
|
||||
*p++ = color.R;
|
||||
*p++ = color.G;
|
||||
*p++ = color.B;
|
||||
*p++ = color.A;
|
||||
}
|
||||
}
|
||||
|
||||
// Render the most recently generated path
|
||||
if (m_Pathfinder.m_Debug.Path && !m_Pathfinder.m_Debug.Path->m_Waypoints.empty())
|
||||
{
|
||||
std::vector<Waypoint>& waypoints = m_Pathfinder.m_Debug.Path->m_Waypoints;
|
||||
u16 ip = 0, jp = 0;
|
||||
for (size_t k = 0; k < waypoints.size(); ++k)
|
||||
{
|
||||
u16 i, j;
|
||||
Pathfinding::NearestNavcell(waypoints[k].x, waypoints[k].z, i, j, m_Pathfinder.m_GridSize, m_Pathfinder.m_GridSize);
|
||||
if (k == 0)
|
||||
{
|
||||
ip = i;
|
||||
jp = j;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool firstCell = true;
|
||||
do
|
||||
{
|
||||
if (data[(jp*w + ip)*4+3] == 0)
|
||||
{
|
||||
data[(jp*w + ip)*4+0] = 0xFF;
|
||||
data[(jp*w + ip)*4+1] = 0xFF;
|
||||
data[(jp*w + ip)*4+2] = 0xFF;
|
||||
data[(jp*w + ip)*4+3] = firstCell ? 0xA0 : 0x60;
|
||||
}
|
||||
ip = ip < i ? ip+1 : ip > i ? ip-1 : ip;
|
||||
jp = jp < j ? jp+1 : jp > j ? jp-1 : jp;
|
||||
firstCell = false;
|
||||
}
|
||||
while (ip != i || jp != j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// These two functions must come below LongOverlay's definition.
|
||||
void LongPathfinder::SetDebugOverlay(bool enabled)
|
||||
{
|
||||
if (enabled && !m_Debug.Overlay)
|
||||
m_Debug.Overlay = new LongOverlay(*this);
|
||||
else if (!enabled && m_Debug.Overlay)
|
||||
SAFE_DELETE(m_Debug.Overlay);
|
||||
}
|
||||
|
||||
LongPathfinder::~LongPathfinder()
|
||||
{
|
||||
SAFE_DELETE(m_Debug.Overlay);
|
||||
SAFE_DELETE(m_Debug.Grid);
|
||||
SAFE_DELETE(m_Debug.Path);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@@ -162,7 +162,7 @@ struct PathfinderState
|
||||
const JumpPointCache* jpc;
|
||||
};
|
||||
|
||||
class LongOverlay;
|
||||
class TerrainTextureOverlay;
|
||||
|
||||
class HierarchicalPathfinder;
|
||||
|
||||
@@ -232,8 +232,13 @@ public:
|
||||
// Debugging - output from last pathfind operation.
|
||||
struct Debug
|
||||
{
|
||||
// Atomic - used to toggle debugging.
|
||||
std::atomic<LongOverlay*> Overlay = nullptr;
|
||||
/**
|
||||
* Atomic - used to toggle debugging.
|
||||
*
|
||||
* Terrain overlay for pathfinder debugging.
|
||||
* Renders a representation of the most recent pathfinding operation.
|
||||
*/
|
||||
std::atomic<TerrainTextureOverlay*> Overlay = nullptr;
|
||||
// Mutable - set by ComputeJPSPath (thus possibly from different threads).
|
||||
// Synchronized via mutex if necessary.
|
||||
mutable PathfindTileGrid* Grid = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user