#include "precompiled.h" #include "TerrainOverlay.h" #include "ps/Game.h" #include "ps/World.h" #include "graphics/Terrain.h" #include "maths/MathUtil.h" #include "lib/ogl.h" #include // Handy things for STL: /// Functor for sorting pairs, using the <-ordering of their second values. struct compare2nd { template bool operator()(const std::pair& a, const std::pair& b) const { return a.second < b.second; } }; /// Functor for comparing the firsts of pairs to a specified value. template struct equal1st { const S& val; equal1st(const S& val) : val(val) {} template bool operator()(const std::pair& a) const { return a.first == val; } private: const equal1st& operator=(const equal1st& rhs); }; /// Functor for calling ->Render on pairs' firsts. struct render1st { template void operator()(const std::pair& a) const { a.first->Render(); } }; ////////////////////////////////////////////////////////////////////////// // Global overlay list management: static std::vector > g_TerrainOverlayList; TerrainOverlay::TerrainOverlay(int priority) { // Add to global list of overlays g_TerrainOverlayList.push_back(std::make_pair(this, priority)); // Sort by overlays by priority. Do stable sort so that adding/removing // overlays doesn't randomly disturb all the existing ones (which would // be noticeable if they have the same priority and overlap). std::stable_sort(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(), compare2nd()); } TerrainOverlay::~TerrainOverlay() { std::vector >::iterator newEnd = std::remove_if(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(), equal1st(this)); g_TerrainOverlayList.erase(newEnd, g_TerrainOverlayList.end()); } void TerrainOverlay::RenderOverlays() { if (g_TerrainOverlayList.size() == 0) return; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_FALSE); // To ensure that outlines are drawn on top of the terrain correctly (and // don't Z-fight and flicker nastily), draw them as QUADS with the LINE // PolygonMode, and use PolygonOffset to pull them towards the camera. // (See e.g. http://www.opengl.org/resources/faq/technical/polygonoffset.htm) glPolygonOffset(-1.f, -1.f); glEnable(GL_POLYGON_OFFSET_LINE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); std::for_each(g_TerrainOverlayList.begin(), g_TerrainOverlayList.end(), render1st()); // Clean up state changes glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDisable(GL_POLYGON_OFFSET_LINE); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glDepthMask(GL_TRUE); glDisable(GL_BLEND); } ////////////////////////////////////////////////////////////////////////// void TerrainOverlay::GetTileExtents( int& min_i_inclusive, int& min_j_inclusive, int& max_i_inclusive, int& max_j_inclusive) { // Default to whole map min_i_inclusive = min_j_inclusive = 0; max_i_inclusive = max_j_inclusive = m_Terrain->GetTilesPerSide()-1; } void TerrainOverlay::Render() { m_Terrain = g_Game->GetWorld()->GetTerrain(); int min_i, min_j, max_i, max_j; GetTileExtents(min_i, min_j, max_i, max_j); // Clamp the min to 0, but the max to -1 - so tile -1 can never be rendered, // but if unclamped_max<0 then no tiles at all will be rendered. And the same // for the upper limit. min_i = clamp(min_i, 0, (int)m_Terrain->GetTilesPerSide()); min_j = clamp(min_j, 0, (int)m_Terrain->GetTilesPerSide()); max_i = clamp(max_i, -1, (int)m_Terrain->GetTilesPerSide()-1); max_j = clamp(max_j, -1, (int)m_Terrain->GetTilesPerSide()-1); for (m_j = min_j; m_j <= max_j; ++m_j) for (m_i = min_i; m_i <= max_i; ++m_i) ProcessTile(m_i, m_j); } void TerrainOverlay::RenderTile(const CColor& colour, bool draw_hidden) { // TODO: if this is unpleasantly slow, make it much more efficient // (e.g. buffering data and making a single draw call? or at least // far fewer calls than it makes now) if (draw_hidden) { glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } else { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); CVector3D pos; glBegin(GL_QUADS); glColor4fv(colour.FloatArray()); m_Terrain->CalcPosition(m_i, m_j, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(m_i+1, m_j, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(m_i+1, m_j+1, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(m_i, m_j+1, pos); glVertex3fv(pos.GetFloatArray()); glEnd(); } void TerrainOverlay::RenderTileOutline(const CColor& colour, int line_width, bool draw_hidden) { if (draw_hidden) { glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } else { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glLineWidth((float)line_width); CVector3D pos; glBegin(GL_QUADS); glColor4fv(colour.FloatArray()); m_Terrain->CalcPosition(m_i, m_j, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(m_i+1, m_j, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(m_i+1, m_j+1, pos); glVertex3fv(pos.GetFloatArray()); m_Terrain->CalcPosition(m_i, m_j+1, pos); glVertex3fv(pos.GetFloatArray()); glEnd(); }