/* Copyright (C) 2011 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it 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. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "TerritoryTexture.h" #include "graphics/Terrain.h" #include "lib/bits.h" #include "ps/Profile.h" #include "renderer/Renderer.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpTerrain.h" #include "simulation2/components/ICmpTerritoryManager.h" // TODO: There's a lot of duplication with CLOSTexture - might be nice to refactor a bit CTerritoryTexture::CTerritoryTexture(CSimulation2& simulation) : m_Simulation(simulation), m_DirtyID(0), m_Texture(0), m_MapSize(0), m_TextureSize(0) { } CTerritoryTexture::~CTerritoryTexture() { if (m_Texture) DeleteTexture(); } void CTerritoryTexture::DeleteTexture() { glDeleteTextures(1, &m_Texture); m_Texture = 0; } bool CTerritoryTexture::UpdateDirty() { CmpPtr cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY); if (cmpTerritoryManager.null()) return false; return cmpTerritoryManager->NeedUpdate(&m_DirtyID); } void CTerritoryTexture::BindTexture(int unit) { if (UpdateDirty()) RecomputeTexture(unit); g_Renderer.BindTexture(unit, m_Texture); } GLuint CTerritoryTexture::GetTexture() { if (UpdateDirty()) RecomputeTexture(0); return m_Texture; } const float* CTerritoryTexture::GetTextureMatrix() { ENSURE(!UpdateDirty()); return &m_TextureMatrix._11; } const float* CTerritoryTexture::GetMinimapTextureMatrix() { ENSURE(!UpdateDirty()); return &m_MinimapTextureMatrix._11; } void CTerritoryTexture::ConstructTexture(int unit) { CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY); if (cmpTerrain.null()) return; m_MapSize = cmpTerrain->GetVerticesPerSide() - 1; m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize); glGenTextures(1, &m_Texture); g_Renderer.BindTexture(unit, m_Texture); // Initialise texture with transparency, for the areas we don't // overwrite with glTexSubImage2D later u8* texData = new u8[m_TextureSize * m_TextureSize * 4]; memset(texData, 0x00, m_TextureSize * m_TextureSize * 4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_TextureSize, m_TextureSize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, texData); delete[] texData; 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); { // Texture matrix: We want to map // world pos (0, y, 0) (i.e. bottom-left of first tile) // onto texcoord (0, 0) (i.e. bottom-left of first texel); // world pos (mapsize*cellsize, y, mapsize*cellsize) (i.e. top-right of last tile) // onto texcoord (mapsize / texsize, mapsize / texsize) (i.e. top-right of last texel) float s = 1.f / (float)(m_TextureSize * CELL_SIZE); float t = 0.f; m_TextureMatrix.SetZero(); m_TextureMatrix._11 = s; m_TextureMatrix._23 = s; m_TextureMatrix._14 = t; m_TextureMatrix._24 = t; m_TextureMatrix._44 = 1; } { // Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize) float s = m_MapSize / (float)m_TextureSize; m_MinimapTextureMatrix.SetZero(); m_MinimapTextureMatrix._11 = s; m_MinimapTextureMatrix._22 = s; m_MinimapTextureMatrix._44 = 1; } } void CTerritoryTexture::RecomputeTexture(int unit) { // If the map was resized, delete and regenerate the texture if (m_Texture) { CmpPtr cmpTerrain(m_Simulation, SYSTEM_ENTITY); if (!cmpTerrain.null() && m_MapSize != (ssize_t)cmpTerrain->GetVerticesPerSide()) DeleteTexture(); } if (!m_Texture) ConstructTexture(unit); PROFILE("recompute territory texture"); std::vector bitmap; bitmap.resize(m_MapSize * m_MapSize * 4); CmpPtr cmpTerritoryManager(m_Simulation, SYSTEM_ENTITY); if (cmpTerritoryManager.null()) return; const Grid territories = cmpTerritoryManager->GetTerritoryGrid(); GenerateBitmap(territories, &bitmap[0], m_MapSize, m_MapSize); g_Renderer.BindTexture(unit, m_Texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize, m_MapSize, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &bitmap[0]); } void CTerritoryTexture::GenerateBitmap(const Grid& territories, u8* bitmap, ssize_t w, ssize_t h) { int alphaMax = 0xC0; int alphaFalloff = 0x20; u8* p = bitmap; for (ssize_t j = 0; j < h; ++j) { for (ssize_t i = 0; i < w; ++i) { u8 val = territories.get(i, j); switch (val) { // TODO: use player colours or something case 1: *p++ = 0x00; *p++ = 0x00; *p++ = 0xFF; break; case 2: *p++ = 0x00; *p++ = 0xFF; *p++ = 0x00; break; case 3: *p++ = 0xFF; *p++ = 0x00; *p++ = 0x00; break; case 4: *p++ = 0x00; *p++ = 0xFF; *p++ = 0xFF; break; case 5: *p++ = 0xFF; *p++ = 0xFF; *p++ = 0x00; break; case 6: *p++ = 0xFF; *p++ = 0x00; *p++ = 0xFF; break; default: *p++ = 0xFF; *p++ = 0xFF; *p++ = 0xFF; break; } if ((i > 0 && territories.get(i-1, j) != val) || (i < w-1 && territories.get(i+1, j) != val) || (j > 0 && territories.get(i, j-1) != val) || (j < h-1 && territories.get(i, j+1) != val) // || (i > 0 && j > 0 && territories.get(i-1, j-1) != val) // || (i < w-1 && j > 0 && territories.get(i+1, j-1) != val) // || (i > 0 && j > h-1 && territories.get(i-1, j+1) != val) // || (i < w-1 && j < h-1 && territories.get(i+1, j+1) != val) ) { *p++ = alphaMax; } else { *p++ = 0x00; } } } // Do a low-quality cheap blur effect for (ssize_t j = 0; j < h; ++j) { int a; a = 0; for (ssize_t i = 0; i < w; ++i) { a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]); bitmap[(j*w+i)*4 + 3] = a; } a = 0; for (ssize_t i = w-1; i >= 0; --i) { a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]); bitmap[(j*w+i)*4 + 3] = a; } } for (ssize_t i = 0; i < w; ++i) { int a; a = 0; for (ssize_t j = 0; j < w; ++j) { a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]); bitmap[(j*w+i)*4 + 3] = a; } a = 0; for (ssize_t j = w-1; j >= 0; --j) { a = std::max(a - alphaFalloff, (int)bitmap[(j*w+i)*4 + 3]); bitmap[(j*w+i)*4 + 3] = a; } } // Add a gap between the boundaries, by deleting the max-alpha tiles for (ssize_t j = 0; j < h; ++j) { for (ssize_t i = 0; i < w; ++i) { if (bitmap[(j*w+i)*4 + 3] == alphaMax) bitmap[(j*w+i)*4 + 3] = 0; } } }