// ============================================================== // This file is part of The Glest Advanced Engine // // Copyright (C) 2009 James McCulloch // // You can redistribute this code and/or modify it under // the terms of the GNU General Public License as published // by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version // ============================================================== #ifndef DEBUG_RENDERING_ENABLED # error debug_renderer.h included without DEBUG_RENDERING_ENABLED defined #endif #ifndef _GLEST_GAME_DEBUG_RENDERER_ #define _GLEST_GAME_DEBUG_RENDERER_ #include "vec.h" #include "math_util.h" #include "pixmap.h" #include "texture.h" #include "graphics_factory_gl.h" #include "route_planner.h" #include "influence_map.h" #include "cartographer.h" #include "cluster_map.h" #include "game.h" #define g_renderer (Renderer::getInstance()) #define g_world (*static_cast(Program::getInstance()->getState())->getWorld()) #define g_console (*static_cast(Program::getInstance()->getState())->getConsole()) using namespace Shared::Graphics; using namespace Shared::Graphics::Gl; using namespace Shared::Util; namespace Glest { namespace Game { class PathFinderTextureCallback { public: set pathSet, openSet, closedSet; Vec2i pathStart, pathDest; map localAnnotations; Field debugField; Texture2D *PFDebugTextures[26]; PathFinderTextureCallback(); void reset(); void loadTextures(); Texture2DGl* operator()(const Vec2i &cell); }; class GridTextureCallback { public: Texture2D *tex; void reset() { tex = 0; } void loadTextures(); GridTextureCallback() : tex(0) {} Texture2DGl* operator()(const Vec2i &cell) { return (Texture2DGl*)tex; } }; enum HighlightColour { hcBlue, hcGreen, hcCount }; class CellHighlightOverlay { public: typedef map CellColours; CellColours cells; Vec4f highlightColours[hcCount]; CellHighlightOverlay() { highlightColours[hcBlue] = Vec4f(0.f, 0.f, 1.f, 0.6f); highlightColours[hcGreen] = Vec4f(0.f, 1.f, 0.f, 0.6f); } void reset() { cells.clear(); } bool empty() const { return cells.empty(); } bool operator()(const Vec2i &cell, Vec4f &colour) { CellColours::iterator it = cells.find(cell); if (it != cells.end()) { colour = highlightColours[it->second]; return true; } return false; } }; class InfluenceMapOverlay { public: TypeMap *iMap; Vec3f baseColour; float max; bool operator()(const Vec2i &cell, Vec4f &colour) { const float influence = iMap->getInfluence(cell); if (influence != 0.f) { colour = Vec4f(baseColour, clamp(influence / max, 0.f, 1.f)); return true; } return false; } }; class VisibleAreaOverlay { public: set quadSet; Vec4f colour; void reset() { colour = Vec4f(0.f, 1.f, 0.f, 0.5f); quadSet.clear(); } bool operator()(const Vec2i &cell, Vec4f &colour) { if (quadSet.find(cell) == quadSet.end()) { return false; } colour = this->colour; return true; } }; class TeamSightOverlay { public: bool operator()(const Vec2i &cell, Vec4f &colour); }; class ClusterMapOverlay { public: set entranceCells; set pathCells; void reset() { entranceCells.clear(); pathCells.clear(); } bool operator()(const Vec2i &cell, Vec4f &colour) { const int &clusterSize = GameConstants::clusterSize; if ( cell.x % clusterSize == clusterSize - 1 || cell.y % clusterSize == clusterSize - 1 ) { if ( entranceCells.find(cell) != entranceCells.end() ) { colour = Vec4f(0.f, 1.f, 0.f, 0.7f); // entrance } else { colour = Vec4f(1.f, 0.f, 0.f, 0.7f); // border } } else if ( pathCells.find(cell) != pathCells.end() ) { // intra-cluster edge colour = Vec4f(0.f, 0.f, 1.f, 0.7f); } else { return false; // nothing interesting } return true; } }; class ResourceMapOverlay { public: const ResourceType *rt; ResourceMapOverlay() : rt(0) {} void reset() { rt = 0; } bool operator()(const Vec2i &cell, Vec4f &colour); }; class StoreMapOverlay { public: typedef vector KeyList; KeyList storeMaps; void reset() { storeMaps.clear(); } bool operator()(const Vec2i &cell, Vec4f &colour); }; class BuildSiteMapOverlay { public: set cells; void reset() { cells.clear(); } bool operator()(const Vec2i &cell, Vec4f &colour) { if (cells.find(cell) != cells.end()) { colour = Vec4f(0.f, 1.f, 0.3f, 0.7f); return true; } return false; } }; // ===================================================== // class DebugRender // /// Helper class compiled with _GAE_DEBUG_EDITION_ only // ===================================================== class DebugRenderer { private: set clusterEdgesWest; set clusterEdgesNorth; Vec3f frstmPoints[8]; PathFinderTextureCallback pfCallback; GridTextureCallback gtCallback; CellHighlightOverlay rhCallback; VisibleAreaOverlay vqCallback; ClusterMapOverlay cmOverlay; ResourceMapOverlay rmOverlay; StoreMapOverlay smOverlay; BuildSiteMapOverlay bsOverlay; InfluenceMapOverlay imOverlay; public: DebugRenderer(); void init(); void commandLine(string &line); bool gridTextures, // show cell grid AAStarTextures, // AA* search space and results of last low-level search HAAStarOverlay, // HAA* search space and results of last hierarchical search showVisibleQuad, // set to show visualisation of last captured scene cull captureVisibleQuad, // set to trigger a capture of the next scene cull captureFrustum, // set to trigger a capture of the view frustum showFrustum, // set to show visualisation of captured view frustum regionHilights, // show hilighted cells, are, and can further be, used for various things resourceMapOverlay, // show resource goal map overlay storeMapOverlay, // show store goal map overlay buildSiteMaps, // show building site goal maps influenceMap; // visualise an inluence map, [TypeMap only] void addCellHighlight(const Vec2i &pos, HighlightColour c = hcBlue) { rhCallback.cells[pos] = c; } void clearCellHilights() { rhCallback.cells.clear(); } void addBuildSiteCell(const Vec2i &pos) { bsOverlay.cells.insert(pos); } void setInfluenceMap(TypeMap *iMap, Vec3f colour, float max); PathFinderTextureCallback& getPFCallback() { return pfCallback; } ClusterMapOverlay& getCMOverlay() { return cmOverlay; } private: /***/ template void renderCellTextures(Quad2i &quad, TextureCallback callback) { const Map *map = g_world.getMap(); const Rect2i mapBounds(0, 0, map->getSurfaceW() - 1, map->getSurfaceH() - 1); float coordStep = g_world.getTileset()->getSurfaceAtlas()->getCoordStep(); assertGl(); glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT); glEnable(GL_BLEND); glEnable(GL_COLOR_MATERIAL); glDisable(GL_ALPHA_TEST); glActiveTexture( GL_TEXTURE0 ); PosQuadIterator pqi(map, quad); while (pqi.next()) { const Vec2i &pos = pqi.getPos(); int cx, cy; cx = pos.x * 2; cy = pos.y * 2; if (mapBounds.isInside(pos)) { SurfaceCell *tc00 = map->getSurfaceCell(pos.x, pos.y), *tc10 = map->getSurfaceCell(pos.x+1, pos.y), *tc01 = map->getSurfaceCell(pos.x, pos.y+1), *tc11 = map->getSurfaceCell(pos.x+1, pos.y+1); Vec3f tl = tc00->getVertex (), tr = tc10->getVertex (), bl = tc01->getVertex (), br = tc11->getVertex (); Vec3f tc = tl + (tr - tl) / 2, ml = tl + (bl - tl) / 2, mr = tr + (br - tr) / 2, mc = ml + (mr - ml) / 2, bc = bl + (br - bl) / 2; Vec2i cPos(cx, cy); const Texture2DGl *tex = callback(cPos); renderCellTextured(tex, tc00->getNormal(), tl, tc, mc, ml); cPos = Vec2i(cx + 1, cy); tex = callback(cPos); renderCellTextured(tex, tc00->getNormal(), tc, tr, mr, mc); cPos = Vec2i(cx, cy + 1 ); tex = callback(cPos); renderCellTextured(tex, tc00->getNormal(), ml, mc, bc, bl); cPos = Vec2i(cx + 1, cy + 1); tex = callback(cPos); renderCellTextured(tex, tc00->getNormal(), mc, mr, br, bc); } } //Restore glPopAttrib(); //assert glGetError(); //remove when first mtex problem solved assertGl(); } // renderCellTextures () /***/ template< typename ColourCallback > void renderCellOverlay(Quad2i &quad, ColourCallback callback) { const Map *map = g_world.getMap(); const Rect2i mapBounds(0, 0, map->getSurfaceW() - 1, map->getSurfaceH() - 1); float coordStep = g_world.getTileset()->getSurfaceAtlas()->getCoordStep(); Vec4f colour; assertGl(); glPushAttrib( GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_FOG_BIT | GL_TEXTURE_BIT ); glEnable( GL_BLEND ); glEnable( GL_COLOR_MATERIAL ); glDisable( GL_ALPHA_TEST ); glActiveTexture( GL_TEXTURE0 ); glDisable( GL_TEXTURE_2D ); PosQuadIterator pqi(map, quad); while(pqi.next()){ const Vec2i &pos= pqi.getPos(); int cx, cy; cx = pos.x * 2; cy = pos.y * 2; if ( mapBounds.isInside( pos ) ) { SurfaceCell *tc00= map->getSurfaceCell(pos.x, pos.y), *tc10= map->getSurfaceCell(pos.x+1, pos.y), *tc01= map->getSurfaceCell(pos.x, pos.y+1), *tc11= map->getSurfaceCell(pos.x+1, pos.y+1); Vec3f tl = tc00->getVertex(), tr = tc10->getVertex(), bl = tc01->getVertex(), br = tc11->getVertex(); tl.y += 0.1f; tr.y += 0.1f; bl.y += 0.1f; br.y += 0.1f; Vec3f tc = tl + (tr - tl) / 2, ml = tl + (bl - tl) / 2, mr = tr + (br - tr) / 2, mc = ml + (mr - ml) / 2, bc = bl + (br - bl) / 2; if (callback(Vec2i(cx,cy), colour)) { renderCellOverlay(colour, tc00->getNormal(), tl, tc, mc, ml); } if (callback(Vec2i(cx+1, cy), colour)) { renderCellOverlay(colour, tc00->getNormal(), tc, tr, mr, mc); } if (callback(Vec2i(cx, cy + 1), colour)) { renderCellOverlay(colour, tc00->getNormal(), ml, mc, bc, bl); } if (callback(Vec2i(cx + 1, cy + 1), colour)) { renderCellOverlay(colour, tc00->getNormal(), mc, mr, br, bc); } } } //Restore glPopAttrib(); assertGl(); } /***/ void renderCellTextured(const Texture2DGl *tex, const Vec3f &norm, const Vec3f &v0, const Vec3f &v1, const Vec3f &v2, const Vec3f &v3); /***/ void renderCellOverlay(const Vec4f colour, const Vec3f &norm, const Vec3f &v0, const Vec3f &v1, const Vec3f &v2, const Vec3f &v3); /***/ void renderArrow(const Vec3f &pos1, const Vec3f &pos2, const Vec3f &color, float width); /***/ void renderPathOverlay(); /***/ void renderIntraClusterEdges(const Vec2i &cluster, CardinalDir dir = CardinalDir::COUNT); /***/ void renderFrustum() const; list waypoints; public: void clearWaypoints() { waypoints.clear(); } void addWaypoint(Vec3f v) { waypoints.push_back(v); } bool willRenderSurface() const { return AAStarTextures || gridTextures; } void renderSurface(Quad2i &quad) { if (AAStarTextures) { if (gridTextures) gridTextures = false; renderCellTextures(quad, pfCallback); } else if (gridTextures) { renderCellTextures(quad, gtCallback); } } void renderEffects(Quad2i &quad); }; DebugRenderer& getDebugRenderer(); }} #endif