Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/gui/ObjectTypes/CMiniMap.cpp
Show All 16 Lines | |||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "CMiniMap.h" | #include "CMiniMap.h" | ||||
#include "graphics/Canvas2D.h" | #include "graphics/Canvas2D.h" | ||||
#include "graphics/GameView.h" | #include "graphics/GameView.h" | ||||
#include "graphics/LOSTexture.h" | #include "graphics/LOSTexture.h" | ||||
#include "graphics/MiniMapTexture.h" | |||||
#include "graphics/MiniPatch.h" | #include "graphics/MiniPatch.h" | ||||
#include "graphics/ShaderManager.h" | #include "graphics/ShaderManager.h" | ||||
#include "graphics/Terrain.h" | #include "graphics/Terrain.h" | ||||
#include "graphics/TerrainTextureEntry.h" | #include "graphics/TerrainTextureEntry.h" | ||||
#include "graphics/TerrainTextureManager.h" | #include "graphics/TerrainTextureManager.h" | ||||
#include "graphics/TerritoryTexture.h" | #include "graphics/TerritoryTexture.h" | ||||
#include "gui/CGUI.h" | #include "gui/CGUI.h" | ||||
#include "gui/GUIManager.h" | #include "gui/GUIManager.h" | ||||
#include "gui/GUIMatrix.h" | #include "gui/GUIMatrix.h" | ||||
#include "lib/bits.h" | #include "lib/bits.h" | ||||
#include "lib/external_libraries/libsdl.h" | #include "lib/external_libraries/libsdl.h" | ||||
#include "lib/ogl.h" | #include "lib/ogl.h" | ||||
#include "lib/timer.h" | #include "lib/timer.h" | ||||
#include "ps/ConfigDB.h" | #include "ps/ConfigDB.h" | ||||
#include "ps/CStrInternStatic.h" | #include "ps/CStrInternStatic.h" | ||||
#include "ps/Filesystem.h" | #include "ps/Filesystem.h" | ||||
#include "ps/Game.h" | #include "ps/Game.h" | ||||
#include "ps/GameSetup/Config.h" | #include "ps/GameSetup/Config.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "ps/World.h" | #include "ps/World.h" | ||||
#include "ps/XML/Xeromyces.h" | |||||
#include "renderer/Renderer.h" | #include "renderer/Renderer.h" | ||||
#include "renderer/RenderingOptions.h" | #include "renderer/RenderingOptions.h" | ||||
#include "renderer/WaterManager.h" | #include "renderer/WaterManager.h" | ||||
#include "scriptinterface/Object.h" | #include "scriptinterface/Object.h" | ||||
#include "simulation2/Simulation2.h" | #include "simulation2/Simulation2.h" | ||||
#include "simulation2/components/ICmpMinimap.h" | #include "simulation2/components/ICmpMinimap.h" | ||||
#include "simulation2/components/ICmpRangeManager.h" | #include "simulation2/components/ICmpRangeManager.h" | ||||
#include "simulation2/helpers/Los.h" | #include "simulation2/helpers/Los.h" | ||||
#include "simulation2/system/ParamNode.h" | #include "simulation2/system/ParamNode.h" | ||||
#include <array> | #include <array> | ||||
#include <cmath> | #include <cmath> | ||||
#include <vector> | #include <vector> | ||||
extern bool g_GameRestarted; | extern bool g_GameRestarted; | ||||
namespace | namespace | ||||
{ | { | ||||
// Set max drawn entities to UINT16_MAX for now, which is more than enough | // Set max drawn entities to UINT16_MAX for now, which is more than enough | ||||
// TODO: we should be cleverer about drawing them to reduce clutter | // TODO: we should be cleverer about drawing them to reduce clutter | ||||
const u16 MAX_ENTITIES_DRAWN = 65535; | const u16 MAX_ENTITIES_DRAWN = 65535; | ||||
unsigned int ScaleColor(unsigned int color, float x) | |||||
{ | |||||
unsigned int r = unsigned(float(color & 0xff) * x); | |||||
unsigned int g = unsigned(float((color>>8) & 0xff) * x); | |||||
unsigned int b = unsigned(float((color>>16) & 0xff) * x); | |||||
return (0xff000000 | b | g<<8 | r<<16); | |||||
} | |||||
// Adds segments pieces lying inside the circle to lines. | // Adds segments pieces lying inside the circle to lines. | ||||
void CropPointsByCircle(const std::array<CVector3D, 4>& points, const CVector3D& center, const float radius, std::vector<CVector3D>* lines) | void CropPointsByCircle(const std::array<CVector3D, 4>& points, const CVector3D& center, const float radius, std::vector<CVector3D>* lines) | ||||
{ | { | ||||
constexpr float EPS = 1e-3f; | constexpr float EPS = 1e-3f; | ||||
lines->reserve(points.size() * 2); | lines->reserve(points.size() * 2); | ||||
for (size_t idx = 0; idx < points.size(); ++idx) | for (size_t idx = 0; idx < points.size(); ++idx) | ||||
{ | { | ||||
const CVector3D& currentPoint = points[idx]; | const CVector3D& currentPoint = points[idx]; | ||||
Show All 21 Lines | |||||
} | } | ||||
} // anonymous namespace | } // anonymous namespace | ||||
const CStr CMiniMap::EventNameWorldClick = "WorldClick"; | const CStr CMiniMap::EventNameWorldClick = "WorldClick"; | ||||
CMiniMap::CMiniMap(CGUI& pGUI) : | CMiniMap::CMiniMap(CGUI& pGUI) : | ||||
IGUIObject(pGUI), | IGUIObject(pGUI), | ||||
m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), m_TerrainDirty(true), m_MapScale(1.f), | m_MapSize(0), m_MapScale(1.f), | ||||
m_EntitiesDrawn(0), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW), m_Mask(this, "mask", false), | m_EntitiesDrawn(0), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW), m_Mask(this, "mask", false), | ||||
m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false), m_WaterHeight(0.0) | m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false) | ||||
{ | { | ||||
m_Clicking = false; | m_Clicking = false; | ||||
m_MouseHovering = false; | m_MouseHovering = false; | ||||
// Register Relax NG validator | |||||
CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng"); | |||||
m_ShallowPassageHeight = GetShallowPassageHeight(); | |||||
m_AttributePos.type = GL_FLOAT; | m_AttributePos.type = GL_FLOAT; | ||||
m_AttributePos.elems = 2; | m_AttributePos.elems = 2; | ||||
m_VertexArray.AddAttribute(&m_AttributePos); | m_VertexArray.AddAttribute(&m_AttributePos); | ||||
m_AttributeColor.type = GL_UNSIGNED_BYTE; | m_AttributeColor.type = GL_UNSIGNED_BYTE; | ||||
m_AttributeColor.elems = 4; | m_AttributeColor.elems = 4; | ||||
m_VertexArray.AddAttribute(&m_AttributeColor); | m_VertexArray.AddAttribute(&m_AttributeColor); | ||||
m_VertexArray.SetNumVertices(MAX_ENTITIES_DRAWN); | m_VertexArray.SetNumVertices(MAX_ENTITIES_DRAWN); | ||||
m_VertexArray.Layout(); | m_VertexArray.Layout(); | ||||
m_IndexArray.SetNumVertices(MAX_ENTITIES_DRAWN); | m_IndexArray.SetNumVertices(MAX_ENTITIES_DRAWN); | ||||
m_IndexArray.Layout(); | m_IndexArray.Layout(); | ||||
VertexArrayIterator<u16> index = m_IndexArray.GetIterator(); | VertexArrayIterator<u16> index = m_IndexArray.GetIterator(); | ||||
for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i) | for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i) | ||||
*index++ = i; | *index++ = i; | ||||
m_IndexArray.Upload(); | m_IndexArray.Upload(); | ||||
m_IndexArray.FreeBackingStore(); | m_IndexArray.FreeBackingStore(); | ||||
VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>(); | VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>(); | ||||
VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>(); | VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>(); | ||||
for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i) | for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i) | ||||
{ | { | ||||
(*attrColor)[0] = 0; | (*attrColor)[0] = 0; | ||||
(*attrColor)[1] = 0; | (*attrColor)[1] = 0; | ||||
(*attrColor)[2] = 0; | (*attrColor)[2] = 0; | ||||
(*attrColor)[3] = 0; | (*attrColor)[3] = 0; | ||||
Show All 13 Lines | CMiniMap::CMiniMap(CGUI& pGUI) : | ||||
if (CConfigDB::IsInitialised()) | if (CConfigDB::IsInitialised()) | ||||
{ | { | ||||
CFG_GET_VAL("gui.session.minimap.pingduration", m_PingDuration); | CFG_GET_VAL("gui.session.minimap.pingduration", m_PingDuration); | ||||
CFG_GET_VAL("gui.session.minimap.blinkduration", blinkDuration); | CFG_GET_VAL("gui.session.minimap.blinkduration", blinkDuration); | ||||
} | } | ||||
m_HalfBlinkDuration = blinkDuration/2; | m_HalfBlinkDuration = blinkDuration/2; | ||||
} | } | ||||
CMiniMap::~CMiniMap() | CMiniMap::~CMiniMap() = default; | ||||
{ | |||||
Destroy(); | |||||
} | |||||
void CMiniMap::HandleMessage(SGUIMessage& Message) | void CMiniMap::HandleMessage(SGUIMessage& Message) | ||||
{ | { | ||||
IGUIObject::HandleMessage(Message); | IGUIObject::HandleMessage(Message); | ||||
switch (Message.type) | switch (Message.type) | ||||
{ | { | ||||
case GUIM_MOUSE_PRESS_LEFT: | case GUIM_MOUSE_PRESS_LEFT: | ||||
if (m_MouseHovering) | if (m_MouseHovering) | ||||
▲ Show 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | if (!g_Game || !g_Game->IsGameStarted()) | ||||
return; | return; | ||||
canvas.Flush(); | canvas.Flush(); | ||||
CSimulation2* sim = g_Game->GetSimulation2(); | CSimulation2* sim = g_Game->GetSimulation2(); | ||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY); | CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY); | ||||
ENSURE(cmpRangeManager); | ENSURE(cmpRangeManager); | ||||
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); | |||||
CMiniMapTexture& miniMapTexture = g_Game->GetView()->GetMiniMapTexture(); | |||||
// Set our globals in case they hadn't been set before | // Set our globals in case they hadn't been set before | ||||
m_Camera = g_Game->GetView()->GetCamera(); | m_Camera = g_Game->GetView()->GetCamera(); | ||||
m_Terrain = g_Game->GetWorld()->GetTerrain(); | const CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); | ||||
m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left); | m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left); | ||||
m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top); | m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top); | ||||
m_MapSize = m_Terrain->GetVerticesPerSide(); | m_MapSize = terrain->GetVerticesPerSide(); | ||||
m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize); | m_TextureSize = miniMapTexture.GetTerrainTextureSize(); | ||||
m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f); | m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f); | ||||
if (!m_TerrainTexture || g_GameRestarted) | |||||
CreateTextures(); | |||||
// only update 2x / second | // only update 2x / second | ||||
// (note: since units only move a few pixels per second on the minimap, | // (note: since units only move a few pixels per second on the minimap, | ||||
// we can get away with infrequent updates; this is slow) | // we can get away with infrequent updates; this is slow) | ||||
// TODO: Update all but camera at same speed as simulation | // TODO: Update all but camera at same speed as simulation | ||||
static double last_time; | static double last_time; | ||||
const double cur_time = timer_Time(); | const double cur_time = timer_Time(); | ||||
const bool doUpdate = cur_time - last_time > 0.5; | const bool doUpdate = cur_time - last_time > 0.5; | ||||
if (doUpdate) | if (doUpdate) | ||||
{ | |||||
last_time = cur_time; | last_time = cur_time; | ||||
if (m_TerrainDirty || m_WaterHeight != g_Renderer.GetWaterManager()->m_WaterHeight) | |||||
RebuildTerrainTexture(); | |||||
} | |||||
const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom; | const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom; | ||||
const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top; | const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top; | ||||
const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize; | const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize; | ||||
const float angle = GetAngle(); | const float angle = GetAngle(); | ||||
const float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f); | const float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f); | ||||
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); | |||||
CShaderProgramPtr shader; | CShaderProgramPtr shader; | ||||
CShaderTechniquePtr tech; | CShaderTechniquePtr tech; | ||||
CShaderDefines baseDefines; | CShaderDefines baseDefines; | ||||
baseDefines.Add(str_MINIMAP_BASE, str_1); | baseDefines.Add(str_MINIMAP_BASE, str_1); | ||||
if (m_Mask) | if (m_Mask) | ||||
baseDefines.Add(str_MINIMAP_MASK, str_1); | baseDefines.Add(str_MINIMAP_MASK, str_1); | ||||
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), baseDefines); | tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), baseDefines); | ||||
tech->BeginPass(); | tech->BeginPass(); | ||||
shader = tech->GetShader(); | shader = tech->GetShader(); | ||||
// Draw the main textured quad | // Draw the main textured quad | ||||
shader->BindTexture(str_baseTex, m_TerrainTexture); | if (miniMapTexture.GetTerrainTexture()) | ||||
shader->BindTexture(str_baseTex, miniMapTexture.GetTerrainTexture()); | |||||
if (m_Mask) | if (m_Mask) | ||||
{ | { | ||||
shader->BindTexture(str_maskTex, losTexture.GetTexture()); | shader->BindTexture(str_maskTex, losTexture.GetTexture()); | ||||
CMatrix3D maskTextureTransform = *losTexture.GetMinimapTextureMatrix(); | CMatrix3D maskTextureTransform = *losTexture.GetMinimapTextureMatrix(); | ||||
// We need to have texture coordinates in the same coordinate space. | // We need to have texture coordinates in the same coordinate space. | ||||
const float scale = 1.0f / texCoordMax; | const float scale = 1.0f / texCoordMax; | ||||
maskTextureTransform.Scale(scale, scale, 1.0f); | maskTextureTransform.Scale(scale, scale, 1.0f); | ||||
shader->Uniform(str_maskTextureTransform, maskTextureTransform); | shader->Uniform(str_maskTextureTransform, maskTextureTransform); | ||||
} | } | ||||
const CMatrix3D baseTransform = GetDefaultGuiMatrix(); | const CMatrix3D baseTransform = GetDefaultGuiMatrix(); | ||||
CMatrix3D baseTextureTransform; | CMatrix3D baseTextureTransform; | ||||
baseTextureTransform.SetIdentity(); | baseTextureTransform.SetIdentity(); | ||||
shader->Uniform(str_transform, baseTransform); | shader->Uniform(str_transform, baseTransform); | ||||
shader->Uniform(str_textureTransform, baseTextureTransform); | shader->Uniform(str_textureTransform, baseTextureTransform); | ||||
if (m_Mask) | if (m_Mask) | ||||
{ | { | ||||
glEnable(GL_BLEND); | glEnable(GL_BLEND); | ||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
} | } | ||||
if (miniMapTexture.GetTerrainTexture()) | |||||
DrawTexture(shader, texCoordMax, angle, x, y, x2, y2); | DrawTexture(shader, texCoordMax, angle, x, y, x2, y2); | ||||
if (!m_Mask) | if (!m_Mask) | ||||
{ | { | ||||
glEnable(GL_BLEND); | glEnable(GL_BLEND); | ||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
} | } | ||||
// Draw territory boundaries | // Draw territory boundaries | ||||
▲ Show 20 Lines • Show All 149 Lines • ▼ Show 20 Lines | #endif | ||||
} | } | ||||
tech->EndPass(); | tech->EndPass(); | ||||
DrawViewRect(unitMatrix); | DrawViewRect(unitMatrix); | ||||
PROFILE_END("minimap units"); | PROFILE_END("minimap units"); | ||||
} | } | ||||
void CMiniMap::CreateTextures() | |||||
{ | |||||
Destroy(); | |||||
// Create terrain texture | |||||
glGenTextures(1, &m_TerrainTexture); | |||||
g_Renderer.BindTexture(0, m_TerrainTexture); | |||||
// Initialise texture with solid black, for the areas we don't | |||||
// overwrite with glTexSubImage2D later | |||||
u32* texData = new u32[m_TextureSize * m_TextureSize]; | |||||
for (ssize_t i = 0; i < m_TextureSize * m_TextureSize; ++i) | |||||
texData[i] = 0xFF000000; | |||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); | |||||
delete[] texData; | |||||
m_TerrainData = new u32[(m_MapSize - 1) * (m_MapSize - 1)]; | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |||||
// Rebuild and upload both of them | |||||
RebuildTerrainTexture(); | |||||
} | |||||
void CMiniMap::RebuildTerrainTexture() | |||||
{ | |||||
u32 x = 0; | |||||
u32 y = 0; | |||||
u32 w = m_MapSize - 1; | |||||
u32 h = m_MapSize - 1; | |||||
m_WaterHeight = g_Renderer.GetWaterManager()->m_WaterHeight; | |||||
m_TerrainDirty = false; | |||||
for (u32 j = 0; j < h; ++j) | |||||
{ | |||||
u32* dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x; | |||||
for (u32 i = 0; i < w; ++i) | |||||
{ | |||||
float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j) | |||||
+ m_Terrain->GetVertexGroundLevel((int)i+1, (int)j) | |||||
+ m_Terrain->GetVertexGroundLevel((int)i, (int)j+1) | |||||
+ m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1) | |||||
) / 4.0f; | |||||
if (avgHeight < m_WaterHeight && avgHeight > m_WaterHeight - m_ShallowPassageHeight) | |||||
{ | |||||
// shallow water | |||||
*dataPtr++ = 0xffc09870; | |||||
} | |||||
else if (avgHeight < m_WaterHeight) | |||||
{ | |||||
// Set water as constant color for consistency on different maps | |||||
*dataPtr++ = 0xffa07850; | |||||
} | |||||
else | |||||
{ | |||||
int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8; | |||||
int val = (hmap / 3) + 170; | |||||
u32 color = 0xFFFFFFFF; | |||||
CMiniPatch* mp = m_Terrain->GetTile(x + i, y + j); | |||||
if (mp) | |||||
{ | |||||
CTerrainTextureEntry* tex = mp->GetTextureEntry(); | |||||
if (tex) | |||||
{ | |||||
// If the texture can't be loaded yet, set the dirty flags | |||||
// so we'll try regenerating the terrain texture again soon | |||||
if(!tex->GetTexture()->TryLoad()) | |||||
m_TerrainDirty = true; | |||||
color = tex->GetBaseColor(); | |||||
} | |||||
} | |||||
*dataPtr++ = ScaleColor(color, float(val) / 255.0f); | |||||
} | |||||
} | |||||
} | |||||
// Upload the texture | |||||
g_Renderer.BindTexture(0, m_TerrainTexture); | |||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_RGBA, GL_UNSIGNED_BYTE, m_TerrainData); | |||||
} | |||||
void CMiniMap::Destroy() | |||||
{ | |||||
if (m_TerrainTexture) | |||||
{ | |||||
glDeleteTextures(1, &m_TerrainTexture); | |||||
m_TerrainTexture = 0; | |||||
} | |||||
SAFE_ARRAY_DELETE(m_TerrainData); | |||||
} | |||||
// static | |||||
float CMiniMap::GetShallowPassageHeight() | |||||
{ | |||||
float shallowPassageHeight = 0.0f; | |||||
CParamNode externalParamNode; | |||||
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder"); | |||||
const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses"); | |||||
if (pathingSettings.GetChild("default").IsOk() && pathingSettings.GetChild("default").GetChild("MaxWaterDepth").IsOk()) | |||||
shallowPassageHeight = pathingSettings.GetChild("default").GetChild("MaxWaterDepth").ToFloat(); | |||||
return shallowPassageHeight; | |||||
} |
Wildfire Games · Phabricator