diff --git a/binaries/data/mods/public/shaders/water_high.fs b/binaries/data/mods/public/shaders/water_high.fs index 404b9a9cf5..865ff685cf 100644 --- a/binaries/data/mods/public/shaders/water_high.fs +++ b/binaries/data/mods/public/shaders/water_high.fs @@ -5,6 +5,7 @@ uniform vec3 cameraPos; uniform sampler2D normalMap; uniform sampler2D reflectionMap; uniform sampler2D refractionMap; +uniform sampler2D losMap; uniform float shininess; // Blinn-Phong specular strength uniform float specularStrength; // Scaling for specular reflection (specular color is (this,this,this)) uniform float waviness; // "Wildness" of the reflections and refractions; choose based on texture @@ -17,7 +18,6 @@ uniform float reflectionTintStrength; // Strength of reflection tint (how much o varying vec3 worldPos; varying float w; varying float waterDepth; -varying float losMod; void main() { @@ -28,7 +28,8 @@ void main() float t; // Temporary variable vec2 reflCoords, refrCoords; vec3 reflColor, refrColor, specular; - + float losMod; + n = normalize(texture2D(normalMap, gl_TexCoord[0].st).xzy - vec3(0.5, 0.5, 0.5)); l = -sunDir; v = normalize(cameraPos - worldPos); @@ -55,7 +56,9 @@ void main() myMurkiness); specular = pow(max(0.0, ndoth), shininess) * sunColor * specularStrength; - + + losMod = texture2D(losMap, gl_TexCoord[3].st).a; + gl_FragColor.rgb = mix(refrColor + 0.3*specular, reflColor + specular, fresnel) * losMod; // Make alpha vary based on both depth (so it blends with the shore) and view angle (make it diff --git a/binaries/data/mods/public/shaders/water_high.vs b/binaries/data/mods/public/shaders/water_high.vs index 8894abf39d..ec972f21c5 100644 --- a/binaries/data/mods/public/shaders/water_high.vs +++ b/binaries/data/mods/public/shaders/water_high.vs @@ -1,23 +1,22 @@ uniform mat4 reflectionMatrix; uniform mat4 refractionMatrix; +uniform mat4 losMatrix; uniform vec4 translation; attribute float vertexDepth; -attribute float losMultiplier; varying vec3 worldPos; varying float w; varying float waterDepth; -varying float losMod; void main() { worldPos = gl_Vertex.xyz; waterDepth = vertexDepth; - losMod = losMultiplier; gl_TexCoord[0] = gl_MultiTexCoord0 + translation; gl_TexCoord[1] = reflectionMatrix * gl_Vertex; // projective texturing gl_TexCoord[2] = reflectionMatrix * gl_Vertex; + gl_TexCoord[3] = losMatrix * gl_Vertex; w = gl_TexCoord[1].w; gl_Position = ftransform(); } diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index b59bc485fa..2ea0897fe5 100644 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -24,6 +24,7 @@ #include "graphics/ColladaManager.h" #include "graphics/HFTracer.h" #include "graphics/LightEnv.h" +#include "graphics/LOSTexture.h" #include "graphics/Model.h" #include "graphics/ObjectManager.h" #include "graphics/Patch.h" @@ -198,6 +199,7 @@ public: CMeshManager MeshManager; CSkeletonAnimManager SkeletonAnimManager; CObjectManager ObjectManager; + CLOSTexture LOSTexture; /** * this camera controls the eye position when rendering @@ -354,12 +356,10 @@ CCinemaManager* CGameView::GetCinema() return &m->TrackManager; }; -/* -void CGameView::AttachToUnit(CEntity* target) +CLOSTexture& CGameView::GetLOSTexture() { - m->UnitAttach = target; + return m->LOSTexture; } -*/ void CGameViewImpl::ScriptingInit() diff --git a/source/graphics/GameView.h b/source/graphics/GameView.h index 201b2cd6a3..a85a97b324 100644 --- a/source/graphics/GameView.h +++ b/source/graphics/GameView.h @@ -31,6 +31,7 @@ class CGame; class CObjectManager; class CCamera; class CCinemaManager; +class CLOSTexture; class CVector3D; struct SViewPort; @@ -90,6 +91,7 @@ public: CCamera *GetCamera(); CCinemaManager* GetCinema(); + CLOSTexture& GetLOSTexture(); JSObject* GetScript(); static void ScriptingInit(); diff --git a/source/graphics/LOSTexture.cpp b/source/graphics/LOSTexture.cpp new file mode 100644 index 0000000000..a6726ba652 --- /dev/null +++ b/source/graphics/LOSTexture.cpp @@ -0,0 +1,244 @@ +/* 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 "LOSTexture.h" + +#include "graphics/Terrain.h" +#include "lib/bits.h" +#include "ps/Game.h" +#include "ps/Profile.h" +#include "ps/World.h" +#include "renderer/Renderer.h" +#include "simulation2/Simulation2.h" +#include "simulation2/components/ICmpRangeManager.h" + +/* + +The LOS bitmap is computed with one value per map vertex, based on +CCmpRangeManager's visibility information. + +The bitmap is then blurred using an NxN box filter (i.e. convolution +with (1 1 1; 1 1 1; 1 1 1) etc). To implement the blur efficiently without +using extra memory for a second copy of the bitmap, we generate the bitmap +with (N-1)/2 pixels of padding on each side, then the blur shifts the +image back into the corner. + +The blurred bitmap is then uploaded into a GL texture for use by the renderer. + +*/ + + +// Blur with a NxN box filter, where N = g_BlurSize must be an odd number. +static const size_t g_BlurSize = 5; + +CLOSTexture::CLOSTexture() : + m_Dirty(true), m_Texture(0), m_MapSize(0), m_TextureSize(0) +{ +} + +CLOSTexture::~CLOSTexture() +{ + if (m_Texture) + { + glDeleteTextures(1, &m_Texture); + m_Texture = 0; + } +} + +void CLOSTexture::MakeDirty() +{ + m_Dirty = true; +} + +void CLOSTexture::BindTexture(int unit) +{ + if (m_Dirty) + { + RecomputeTexture(unit); + m_Dirty = false; + } + + g_Renderer.BindTexture(unit, m_Texture); +} + +CMatrix3D CLOSTexture::GetTextureMatrix() +{ + debug_assert(m_MapSize); + + // We want to map + // world pos (0, y, 0) (i.e. first vertex) + // onto texcoord (0.5/texsize, 0.5/texsize) (i.e. middle of first texel); + // world pos ((mapsize-1)*cellsize, y, (mapsize-1)*cellsize) (i.e. last vertex) + // onto texcoord ((mapsize-0.5) / texsize, (mapsize-0.5) / texsize) (i.e. middle of last texel) + // so construct an appropriate matrix: + + float s = (m_MapSize-1) / (float)(m_TextureSize * (m_MapSize-1) * CELL_SIZE); + float t = 0.5f / m_TextureSize; + CMatrix3D m; + m.SetZero(); + m._11 = s; + m._23 = s; + m._14 = t; + m._24 = t; + m._44 = 1; + return m; +} + +CMatrix3D CLOSTexture::GetMinimapTextureMatrix() +{ + debug_assert(m_MapSize); + + // We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize) + + float s = m_MapSize / (float)m_TextureSize; + CMatrix3D m; + m.SetZero(); + m._11 = s; + m._22 = s; + m._44 = 1; + return m; +} + +void CLOSTexture::ConstructTexture(int unit) +{ + CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); + m_MapSize = terrain->GetVerticesPerSide(); + m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize + g_BlurSize - 1); + + glGenTextures(1, &m_Texture); + g_Renderer.BindTexture(unit, m_Texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); + 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); +} + +void CLOSTexture::RecomputeTexture(int unit) +{ + if (!m_Texture) + ConstructTexture(unit); + + PROFILE("recompute LOS texture"); + + std::vector losData; + losData.resize(GetBitmapSize(m_MapSize, m_MapSize)); + + CmpPtr cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); + if (cmpRangeManager.null()) + return; + + ICmpRangeManager::CLosQuerier los (cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID())); + + GenerateBitmap(los, &losData[0], m_MapSize, m_MapSize); + + g_Renderer.BindTexture(unit, m_Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize + g_BlurSize - 1, m_MapSize + g_BlurSize - 1, GL_ALPHA, GL_UNSIGNED_BYTE, &losData[0]); +} + +size_t CLOSTexture::GetBitmapSize(size_t w, size_t h) +{ + return (w + g_BlurSize - 1) * (h + g_BlurSize - 1); +} + +void CLOSTexture::GenerateBitmap(ICmpRangeManager::CLosQuerier los, u8* losData, size_t w, size_t h) +{ + const size_t rowSize = w + g_BlurSize-1; // size of losData rows + + u8 *dataPtr = losData; + + // Initialise the top padding + for (size_t j = 0; j < g_BlurSize/2; ++j) + for (size_t i = 0; i < rowSize; ++i) + *dataPtr++ = 0; + + for (size_t j = 0; j < h; ++j) + { + // Initialise the left padding + for (size_t i = 0; i < g_BlurSize/2; ++i) + *dataPtr++ = 0; + + // Fill in the visibility data + for (size_t i = 0; i < w; ++i) + { + if (los.IsVisible(i, j)) + *dataPtr++ = 255; + else if (los.IsExplored(i, j)) + *dataPtr++ = 127; + else + *dataPtr++ = 0; + } + + // Initialise the right padding + for (size_t i = 0; i < g_BlurSize/2; ++i) + *dataPtr++ = 0; + } + + // Initialise the bottom padding + for (size_t j = 0; j < g_BlurSize/2; ++j) + for (size_t i = 0; i < rowSize; ++i) + *dataPtr++ = 0; + + // Horizontal blur: + + for (size_t j = g_BlurSize/2; j < h + g_BlurSize/2; ++j) + { + u32 accum = 0; // sum of values in the sliding Nx1 window + + // Initialise the filter window for the first output pixel + // (Start i at the first of the non-padding pixels, since the + // padding was set to 0) + for (size_t i = g_BlurSize/2; i < g_BlurSize-1; ++i) + accum += losData[i+j*rowSize]; + + // For each pixel, update the sliding window and store the average + // of the window's sum + for (size_t i = 0; i < w; ++i) + { + accum += losData[i+g_BlurSize-1+j*rowSize]; + + u8 old = losData[i+j*rowSize]; + + losData[i+j*rowSize] = accum / g_BlurSize; + + accum -= old; + } + } + + // Vertical blur: + + for (size_t i = 0; i < w; ++i) + { + u32 accum = 0; + + for (size_t j = g_BlurSize/2; j < g_BlurSize-1; ++j) + accum += losData[i+j*rowSize]; + + for (size_t j = 0; j < h; ++j) + { + accum += losData[i+(j+g_BlurSize-1)*rowSize]; + + u8 old = losData[i+j*rowSize]; + + losData[i+j*rowSize] = accum / g_BlurSize; + + accum -= old; + } + } +} diff --git a/source/graphics/LOSTexture.h b/source/graphics/LOSTexture.h new file mode 100644 index 0000000000..43ef3cc1f4 --- /dev/null +++ b/source/graphics/LOSTexture.h @@ -0,0 +1,78 @@ +/* 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 "lib/ogl.h" + +#include "simulation2/components/ICmpRangeManager.h" + +class CMatrix3D; + +/** + * Maintains the LOS (fog-of-war / shroud-of-darkness) texture, used for + * rendering and for the minimap. + */ +class CLOSTexture +{ + NONCOPYABLE(CLOSTexture); + friend class TestLOSTexture; + +public: + CLOSTexture(); + ~CLOSTexture(); + + /** + * Marks the LOS texture as needing recomputation. Call this after each + * simulation update, to ensure responsive updates. + */ + void MakeDirty(); + + /** + * Recomputes the LOS texture if necessary, and binds it to the requested + * texture unit. + * Also switches the current active texture unit, and enables texturing on it. + * The texture is in 8-bit ALPHA format. + */ + void BindTexture(int unit); + + /** + * Returns a matrix to map (x,y,z) world coordinates onto (u,v) LOS texture + * coordinates. + * This must only be called after BindTexture. + */ + CMatrix3D GetTextureMatrix(); + + /** + * Returns a matrix to map (0,0)-(1,1) texture coordinates onto LOS texture + * coordinates. + * This must only be called after BindTexture. + */ + CMatrix3D GetMinimapTextureMatrix(); + +private: + void ConstructTexture(int unit); + void RecomputeTexture(int unit); + + size_t GetBitmapSize(size_t w, size_t h); + void GenerateBitmap(ICmpRangeManager::CLosQuerier los, u8* losData, size_t w, size_t h); + + bool m_Dirty; + + GLuint m_Texture; + + ssize_t m_MapSize; // vertexes per side + GLsizei m_TextureSize; // texels per side +}; diff --git a/source/graphics/tests/test_LOSTexture.h b/source/graphics/tests/test_LOSTexture.h new file mode 100644 index 0000000000..b2f2f245d0 --- /dev/null +++ b/source/graphics/tests/test_LOSTexture.h @@ -0,0 +1,78 @@ +/* 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 "lib/self_test.h" + +#include "graphics/LOSTexture.h" +#include "lib/timer.h" + +class TestLOSTexture : public CxxTest::TestSuite +{ +public: + void test_basic() + { + CLOSTexture tex; + + const ssize_t size = 8; + u32 inputData[size*size] = { + 2, 2, 2, 0, 0, 0, 0, 0, + 2, 2, 2, 0, 0, 0, 0, 0, + 2, 2, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2 + }; + std::vector inputDataVec(inputData, inputData+size*size); + + ICmpRangeManager::CLosQuerier los(1, inputDataVec, size); + + std::vector losData; + losData.resize(tex.GetBitmapSize(size, size)); + + tex.GenerateBitmap(los, &losData[0], size, size); + +// for (size_t i = 0; i < losData.size(); ++i) +// printf("%s %3d", i % (size_t)sqrt(losData.size()) ? "" : "\n", losData[i]); + + TS_ASSERT_EQUALS(losData[0], 91); + } + + void test_perf_DISABLED() + { + CLOSTexture tex; + + const ssize_t size = 257; + std::vector inputDataVec; + inputDataVec.resize(size*size); + + ICmpRangeManager::CLosQuerier los(1, inputDataVec, size); + + size_t reps = 128; + double t = timer_Time(); + for (size_t i = 0; i < reps; ++i) + { + std::vector losData; + losData.resize(tex.GetBitmapSize(size, size)); + + tex.GenerateBitmap(los, &losData[0], size, size); + } + double dt = timer_Time() - t; + printf("\n# %f secs\n", dt/reps); + } +}; diff --git a/source/gui/MiniMap.cpp b/source/gui/MiniMap.cpp index 03bcd9b1c6..1984198b94 100644 --- a/source/gui/MiniMap.cpp +++ b/source/gui/MiniMap.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* 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 @@ -22,6 +22,7 @@ #include "MiniMap.h" #include "graphics/GameView.h" +#include "graphics/LOSTexture.h" #include "graphics/MiniPatch.h" #include "graphics/Terrain.h" #include "graphics/TerrainTextureEntry.h" @@ -39,7 +40,6 @@ #include "scriptinterface/ScriptInterface.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpMinimap.h" -#include "simulation2/components/ICmpRangeManager.h" bool g_GameRestarted = false; @@ -51,9 +51,8 @@ static unsigned int ScaleColor(unsigned int color, float x) return (0xff000000 | r | g<<8 | b<<16); } -CMiniMap::CMiniMap() - : m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), - m_LOSTexture(0), m_TerrainDirty(true) +CMiniMap::CMiniMap() : + m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), m_TerrainDirty(true) { AddSetting(GUIST_CColor, "fov_wedge_color"); AddSetting(GUIST_bool, "circular"); @@ -61,8 +60,6 @@ CMiniMap::CMiniMap() AddSetting(GUIST_CStr, "tooltip_style"); m_Clicking = false; m_Hovering = false; - - m_LOSScale = 2; } CMiniMap::~CMiniMap() @@ -279,9 +276,7 @@ void CMiniMap::Draw() m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left); m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top); m_MapSize = m_Terrain->GetVerticesPerSide(); - m_LOSMapSize = (m_MapSize + m_LOSScale - 1) / m_LOSScale; // divide and round upwards m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize); - m_LOSTextureSize = (GLsizei)round_up_to_pow2((size_t)m_LOSMapSize); if(!m_TerrainTexture || g_GameRestarted) CreateTextures(); @@ -298,16 +293,12 @@ void CMiniMap::Draw() if(m_TerrainDirty) RebuildTerrainTexture(); - - RebuildLOSTexture(); } const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom; const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top; const float z = GetBufferedZ(); const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize; - const float losTexCoordMax = (float)(m_LOSMapSize - 1) / (float)m_LOSTextureSize; - const float angle = GetAngle(); // Draw the main textured quad @@ -371,20 +362,29 @@ void CMiniMap::Draw() */ // Draw the LOS quad in black, using alpha values from the LOS texture - g_Renderer.BindTexture(0, m_LOSTexture); + CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); + losTexture.BindTexture(0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor3f(0.0f, 0.0f, 0.0f); - DrawTexture(losTexCoordMax, angle, x, y, x2, y2, z); + + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(&losTexture.GetMinimapTextureMatrix()._11); + glMatrixMode(GL_MODELVIEW); + + DrawTexture(1.0f, angle, x, y, x2, y2, z); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glDisable(GL_BLEND); // Set up the matrix for drawing points and lines @@ -463,22 +463,11 @@ void CMiniMap::CreateTextures() 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); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); - - // Create LOS texture - glGenTextures(1, &m_LOSTexture); - g_Renderer.BindTexture(0, m_LOSTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, m_LOSTextureSize, m_LOSTextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); - m_LOSData.resize(m_LOSMapSize * m_LOSMapSize); - 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); - glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); + 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(); - RebuildLOSTexture(); } @@ -539,44 +528,6 @@ void CMiniMap::RebuildTerrainTexture() glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_TerrainData); } - -void CMiniMap::RebuildLOSTexture() -{ - PROFILE("rebuild minimap: los"); - - ssize_t w = m_MapSize; - ssize_t h = m_MapSize; - - CmpPtr cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); - if (cmpRangeManager.null() || cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID())) - { - memset(&m_LOSData[0], 0, m_LOSData.size()); - } - else - { - ICmpRangeManager::CLosQuerier los (cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID())); - - u8 *dataPtr = &m_LOSData[0]; - - for (ssize_t j = 0; j < h; j += m_LOSScale) - { - for (ssize_t i = 0; i < w; i += m_LOSScale) - { - if (los.IsVisible(i, j)) - *dataPtr++ = 0; - else if (los.IsExplored(i, j)) - *dataPtr++ = 76; - else - *dataPtr++ = 255; - } - } - } - - // Upload the texture - g_Renderer.BindTexture(0, m_LOSTexture); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_LOSMapSize, m_LOSMapSize, GL_ALPHA, GL_UNSIGNED_BYTE, &m_LOSData[0]); -} - void CMiniMap::Destroy() { if(m_TerrainTexture) @@ -585,11 +536,6 @@ void CMiniMap::Destroy() m_TerrainTexture = 0; } - if(m_LOSTexture) - { - glDeleteTextures(1, &m_LOSTexture); - m_LOSTexture = 0; - } - - delete[] m_TerrainData; m_TerrainData = 0; + delete[] m_TerrainData; + m_TerrainData = 0; } diff --git a/source/gui/MiniMap.h b/source/gui/MiniMap.h index 7683d3e68a..a915011ba6 100644 --- a/source/gui/MiniMap.h +++ b/source/gui/MiniMap.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* 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 @@ -40,9 +40,6 @@ protected: // rebuild the terrain texture map void RebuildTerrainTexture(); - // rebuild the LOS map - void RebuildLOSTexture(); - // destroy and free any memory and textures void Destroy(); @@ -61,15 +58,9 @@ protected: // minimap texture handles GLuint m_TerrainTexture; - GLuint m_LOSTexture; - - // number of vertexes per LOS-texture texel - u8 m_LOSScale; - ssize_t m_LOSMapSize; // texture data u32* m_TerrainData; - std::vector m_LOSData; // whether we need to regenerate the terrain texture bool m_TerrainDirty; @@ -81,7 +72,6 @@ protected: // texture size GLsizei m_TextureSize; - GLsizei m_LOSTextureSize; void DrawTexture(float coordMax, float angle, float x, float y, float x2, float y2, float z); diff --git a/source/lib/glext_funcs.h b/source/lib/glext_funcs.h index 57999af058..a37b723cc1 100644 --- a/source/lib/glext_funcs.h +++ b/source/lib/glext_funcs.h @@ -65,6 +65,7 @@ FUNC2(void, glDrawRangeElementsEXT, glDrawRangeElements, "1.2", (GLenum, GLuint, // GL_ARB_multitexture / GL1.3: FUNC2(void, glMultiTexCoord2fARB, glMultiTexCoord2f, "1.3", (int, float, float)) +FUNC2(void, glMultiTexCoord3fARB, glMultiTexCoord3f, "1.3", (int, float, float, float)) FUNC2(void, glActiveTextureARB, glActiveTexture, "1.3", (int)) FUNC2(void, glClientActiveTextureARB, glClientActiveTexture, "1.3", (int)) diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index 86e9255300..6c0fe974b3 100644 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -20,6 +20,7 @@ #include "Game.h" #include "graphics/GameView.h" +#include "graphics/LOSTexture.h" #include "graphics/UnitManager.h" #include "lib/timer.h" #include "network/NetClient.h" @@ -234,7 +235,10 @@ bool CGame::Update(double deltaTime, bool doInterpolate) PROFILE("update"); if (m_TurnManager->Update(deltaTime, maxTurns)) + { g_GUI->SendEventToAll("SimulationUpdate"); + GetView()->GetLOSTexture().MakeDirty(); + } } if (doInterpolate) diff --git a/source/renderer/PatchRData.cpp b/source/renderer/PatchRData.cpp index a75714e32f..e01cd36242 100644 --- a/source/renderer/PatchRData.cpp +++ b/source/renderer/PatchRData.cpp @@ -137,7 +137,6 @@ void CPatchRData::BuildBlends() { m_BlendSplats.clear(); m_BlendVertices.clear(); - m_BlendVertexIndices.clear(); CTerrain* terrain = m_Patch->m_Parent; @@ -348,37 +347,29 @@ void CPatchRData::AddBlend(u16 i, u16 j, u8 shape) CalculateUV(dst.m_UVs, gx, gz); dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1]; - dst.m_LOSColor = vtx0.m_LOSColor; dst.m_Position = vtx0.m_Position; m_BlendVertices.push_back(dst); - m_BlendVertexIndices.push_back((j * vsize) + i); const SBaseVertex& vtx1 = m_Vertices[(j * vsize) + i + 1]; CalculateUV(dst.m_UVs, gx + 1, gz); dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1]; - dst.m_LOSColor = vtx1.m_LOSColor; dst.m_Position = vtx1.m_Position; m_BlendVertices.push_back(dst); - m_BlendVertexIndices.push_back((j * vsize) + i + 1); const SBaseVertex& vtx2 = m_Vertices[((j + 1) * vsize) + i + 1]; CalculateUV(dst.m_UVs, gx + 1, gz + 1); dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1]; - dst.m_LOSColor = vtx2.m_LOSColor; dst.m_Position = vtx2.m_Position; m_BlendVertices.push_back(dst); - m_BlendVertexIndices.push_back(((j + 1) * vsize) + i + 1); const SBaseVertex& vtx3 = m_Vertices[((j + 1) * vsize) + i]; CalculateUV(dst.m_UVs, gx, gz + 1); dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0]; dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1]; - dst.m_LOSColor = vtx3.m_LOSColor; dst.m_Position = vtx3.m_Position; m_BlendVertices.push_back(dst); - m_BlendVertexIndices.push_back(((j + 1) * vsize) + i); } void CPatchRData::BuildIndices() @@ -391,7 +382,6 @@ void CPatchRData::BuildIndices() // release existing indices and bins m_Indices.clear(); - m_ShadowMapIndices.clear(); m_Splats.clear(); // build grid of textures on this patch and boundaries of adjacent patches @@ -430,16 +420,6 @@ void CPatchRData::BuildIndices() } splat.m_IndexCount=m_Indices.size()-splat.m_IndexStart; } - - // build indices for the shadow map pass - for (ssize_t j=0;jCalcPosition(ix,iz,vertices[v].m_Position); - vertices[v].m_LOSColor = SColor4ub(0, 0, 0, 0); // will be set to the proper value in Update() CalculateUV(vertices[v].m_UVs, ix, iz); // Calculate diffuse lighting for this vertex @@ -514,81 +493,19 @@ void CPatchRData::Update() m_UpdateFlags=0; } - - // Update vertex colors, which are affected by LOS - - ssize_t px=m_Patch->m_X; - ssize_t pz=m_Patch->m_Z; - - CTerrain* terrain=m_Patch->m_Parent; - ssize_t vsize=PATCH_SIZE+1; - SColor4ub baseColour = terrain->GetBaseColour(); - - CmpPtr cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); - if (cmpRangeManager.null()) - { - for (ssize_t j = 0; j < vsize; ++j) - { - for (ssize_t i = 0; i < vsize; ++i) - { - ssize_t v = (j*vsize)+i; - m_Vertices[v].m_LOSColor = baseColour; - } - } - } - else - { - ICmpRangeManager::CLosQuerier los (cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID())); - - // this is very similar to BuildVertices(), but just for color - for (ssize_t j = 0; j < vsize; j++) - { - for (ssize_t i = 0; i < vsize; i++) - { - ssize_t ix = px * PATCH_SIZE + i; - ssize_t iz = pz * PATCH_SIZE + j; - ssize_t v = (j * vsize) + i; - - SColor4ub losMod; - if (los.IsVisible(ix, iz)) - losMod = baseColour; - else if (los.IsExplored(ix, iz)) - losMod = SColor4ub(178, 178, 178, 255); - else - losMod = SColor4ub(0, 0, 0, 255); - - m_Vertices[v].m_LOSColor = losMod; - } - } - } - - // upload base vertices into their vertex buffer - m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices); - - // update blend colors by copying them from vertex colors - for(size_t i=0; im_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]); - } } -void CPatchRData::RenderBase(bool losColor) +void CPatchRData::RenderBase() { debug_assert(m_UpdateFlags==0); SBaseVertex *base=(SBaseVertex *)m_VBBase->m_Owner->Bind(); // setup data pointers - GLsizei stride=sizeof(SBaseVertex); - glVertexPointer(3,GL_FLOAT,stride,&base->m_Position[0]); - glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor); - glTexCoordPointer(2,GL_FLOAT,stride,&base->m_UVs[0]); + GLsizei stride = sizeof(SBaseVertex); + glVertexPointer(3, GL_FLOAT, stride, &base->m_Position[0]); + glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor); + glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs[0]); // render each splat for (size_t i=0;im_Owner->Bind(); // setup data pointers - GLsizei stride=sizeof(SBaseVertex); + GLsizei stride = sizeof(SBaseVertex); glVertexPointer(3, GL_FLOAT, stride, &base->m_Position); - if (streamflags & STREAM_UV0) { + if (streamflags & STREAM_UV0) + { glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs); - } else if (streamflags & STREAM_POSTOUV0) { + } + else if (streamflags & STREAM_POSTOUV0) + { glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position); } + if (streamflags & STREAM_POSTOUV1) + { + pglClientActiveTextureARB(GL_TEXTURE1); + glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position); + pglClientActiveTextureARB(GL_TEXTURE0); + } + if (streamflags & STREAM_POSTOUV2) + { + pglClientActiveTextureARB(GL_TEXTURE2); + glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position); + pglClientActiveTextureARB(GL_TEXTURE0); + } + if (streamflags & STREAM_POSTOUV3) + { + pglClientActiveTextureARB(GL_TEXTURE3); + glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position); + pglClientActiveTextureARB(GL_TEXTURE0); + } if (streamflags & STREAM_COLOR) { - glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor); + glColorPointer(4, GL_UNSIGNED_BYTE, stride, &base->m_DiffuseColor); } // render all base splats at once @@ -660,7 +598,6 @@ void CPatchRData::RenderBlends() // invalid - see http://gcc.gnu.org/ml/gcc/2003-11/msg00281.html - but it // doesn't seem to be worth changing this code since it works anyway.)) glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position)); - glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_LOSColor)); pglClientActiveTextureARB(GL_TEXTURE0); glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0])); diff --git a/source/renderer/PatchRData.h b/source/renderer/PatchRData.h index 8f1a3ef4af..28cfa2bce5 100644 --- a/source/renderer/PatchRData.h +++ b/source/renderer/PatchRData.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* 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 @@ -37,10 +37,10 @@ public: ~CPatchRData(); void Update(); - void RenderBase(bool losColor); + void RenderBase(); void RenderBlends(); void RenderOutline(); - void RenderStreams(int streamflags, bool losColor); + void RenderStreams(int streamflags); void RenderPriorities(); private: @@ -62,15 +62,11 @@ private: SColor4ub m_DiffuseColor; // vertex uvs for base texture float m_UVs[2]; - // color modulation from LOS - SColor4ub m_LOSColor; }; struct SBlendVertex { // vertex position CVector3D m_Position; - // color modulation from LOS - SColor4ub m_LOSColor; // vertex uvs for base texture float m_UVs[2]; // vertex uvs for alpha texture @@ -108,19 +104,12 @@ private: // indices into base vertices for the base splats std::vector m_Indices; - // indices into base vertices for the shadow map pass - std::vector m_ShadowMapIndices; - // list of base splats to apply to this patch std::vector m_Splats; // vertices to use for blending transition texture passes std::vector m_BlendVertices; - // remembers the index in the m_Vertices array of each blend vertex, so that we can - // properly update its color for fog of war and shroud of darkness - std::vector m_BlendVertexIndices; - // splats used in blend pass std::vector m_BlendSplats; }; diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index e4e9dbefaf..917040338f 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* 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 @@ -434,11 +434,12 @@ CRenderer::~CRenderer() void CRenderer::EnumCaps() { // assume support for nothing - m_Caps.m_VBO=false; - m_Caps.m_TextureBorderClamp=false; - m_Caps.m_GenerateMipmaps=false; - m_Caps.m_VertexShader=false; - m_Caps.m_FragmentShader=false; + m_Caps.m_VBO = false; + m_Caps.m_TextureBorderClamp = false; + m_Caps.m_GenerateMipmaps = false; + m_Caps.m_VertexShader = false; + m_Caps.m_FragmentShader = false; + m_Caps.m_Shadows = false; m_Caps.m_DepthTextureShadows = false; m_Caps.m_FramebufferObject = false; @@ -448,12 +449,15 @@ void CRenderer::EnumCaps() m_Caps.m_VBO=true; } } + if (ogl_HaveExtension("GL_ARB_texture_border_clamp")) { m_Caps.m_TextureBorderClamp=true; } + if (ogl_HaveExtension("GL_SGIS_generate_mipmap")) { m_Caps.m_GenerateMipmaps=true; } + if (0 == ogl_HaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", NULL)) { if (ogl_HaveExtension("GL_ARB_vertex_shader")) @@ -462,6 +466,15 @@ void CRenderer::EnumCaps() m_Caps.m_FragmentShader=true; } + if (ogl_max_tex_units >= 3) + { + // To render shadows plus fog-of-war in a single lighting pass (see + // TerrainRenderer.cpp) we need >= 3 TMUs. Only really ancient hardware + // doesn't support that, so instead of implementing a compatible fallback + // we'll just disable shadows entirely unless there's enough TMUs. + m_Caps.m_Shadows = true; + } + if (0 == ogl_HaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", NULL)) { // According to Delphi3d.net, all relevant graphics chips that support depth textures // (i.e. Geforce3+, Radeon9500+, even i915) also have >= 4 TMUs, so this restriction @@ -470,6 +483,7 @@ void CRenderer::EnumCaps() if (ogl_max_tex_units >= 4) m_Caps.m_DepthTextureShadows = true; } + if (!m_Options.m_NoFramebufferObject) { if (ogl_HaveExtension("GL_EXT_framebuffer_object")) @@ -764,7 +778,7 @@ void CRenderer::BeginFrame() // choose model renderers for this frame int vertexType; - if (m_Options.m_Shadows && m->shadow->GetUseDepthTexture()) + if (m_Caps.m_Shadows && m_Options.m_Shadows && m->shadow->GetUseDepthTexture()) { vertexType = OnlyDiffuse; m->Model.ModNormal = m->Model.ModPlainLit; @@ -884,7 +898,7 @@ void CRenderer::RenderPatches() } // render all the patches, including blend pass - m->terrainRenderer->RenderTerrain(m_Options.m_Shadows ? m->shadow : 0); + m->terrainRenderer->RenderTerrain((m_Caps.m_Shadows && m_Options.m_Shadows) ? m->shadow : 0); if (m_TerrainRenderMode==WIREFRAME) { // switch wireframe off again @@ -1224,7 +1238,7 @@ void CRenderer::RenderSubmissions() m->overlayRenderer.PrepareForRendering(); PROFILE_END("prepare overlays"); - if (m_Options.m_Shadows) + if (m_Caps.m_Shadows && m_Options.m_Shadows) { RenderShadowMap(); } diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h index 54f1b78e17..eb18056701 100644 --- a/source/renderer/Renderer.h +++ b/source/renderer/Renderer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* 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 @@ -47,15 +47,18 @@ class CTextureManager; enum ERenderMode { WIREFRAME, SOLID, EDGED_FACES }; // stream flags -#define STREAM_POS 0x01 -#define STREAM_NORMAL 0x02 -#define STREAM_COLOR 0x04 -#define STREAM_UV0 0x08 -#define STREAM_UV1 0x10 -#define STREAM_UV2 0x20 -#define STREAM_UV3 0x40 -#define STREAM_POSTOUV0 0x80 -#define STREAM_TEXGENTOUV1 0x100 +#define STREAM_POS (1 << 0) +#define STREAM_NORMAL (1 << 1) +#define STREAM_COLOR (1 << 2) +#define STREAM_UV0 (1 << 3) +#define STREAM_UV1 (1 << 4) +#define STREAM_UV2 (1 << 5) +#define STREAM_UV3 (1 << 6) +#define STREAM_POSTOUV0 (1 << 7) +#define STREAM_POSTOUV1 (1 << 8) +#define STREAM_POSTOUV2 (1 << 9) +#define STREAM_POSTOUV3 (1 << 10) +#define STREAM_TEXGENTOUV1 (1 << 11) ////////////////////////////////////////////////////////////////////////////////////////// // SVertex3D: simple 3D vertex declaration @@ -152,6 +155,7 @@ public: bool m_GenerateMipmaps; bool m_VertexShader; bool m_FragmentShader; + bool m_Shadows; bool m_DepthTextureShadows; bool m_FramebufferObject; }; diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index 3b18a24130..09cb39d92c 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2010 Wildfire Games. +/* 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 @@ -24,6 +24,7 @@ #include "graphics/Camera.h" #include "graphics/LightEnv.h" +#include "graphics/LOSTexture.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "graphics/GameView.h" @@ -37,9 +38,6 @@ #include "ps/Profile.h" #include "ps/World.h" -#include "simulation2/Simulation2.h" -#include "simulation2/components/ICmpRangeManager.h" - #include "renderer/PatchRData.h" #include "renderer/Renderer.h" #include "renderer/ShadowMap.h" @@ -159,7 +157,6 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) // switch on required client states glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // render everything fullbright @@ -167,11 +164,9 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); // Set alpha to 1.0 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); @@ -183,7 +178,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) for(size_t i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); - patchdata->RenderBase(true); // with LOS color + patchdata->RenderBase(); } // render blends @@ -225,6 +220,9 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) // Now apply lighting const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); + pglClientActiveTextureARB(GL_TEXTURE0); + glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colours + glBlendFunc(GL_DST_COLOR, GL_ZERO); // GL_TEXTURE_ENV_COLOR requires four floats, so we shouldn't use the RGBColor directly @@ -234,6 +232,10 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) lightEnv.m_TerrainAmbientColor.Z, 1.f }; + + CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); + + int streamflags = STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0; if (!shadow) { @@ -244,7 +246,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) // so assume that's still valid to use. // (TODO: That's a bit of an ugly hack.) - // Shadow rendering disabled: Ambient + Diffuse + // Shadow rendering disabled: (Ambient + Diffuse) * LOS glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); @@ -256,7 +258,25 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); - + + losTexture.BindTexture(1); + pglClientActiveTextureARB(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + streamflags |= STREAM_POSTOUV1; + + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(&losTexture.GetTextureMatrix()._11); + glMatrixMode(GL_MODELVIEW); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); } else { @@ -271,7 +291,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) if (shadow->GetUseDepthTexture()) { - // Ambient + ShTranslucency * Diffuse * (1 - Shadow) + Diffuse * Shadow + // (Ambient + ShTranslucency * Diffuse * (1 - Shadow) + Diffuse * Shadow) * LOS glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR); @@ -313,11 +333,29 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); - + + losTexture.BindTexture(3); + pglClientActiveTextureARB(GL_TEXTURE3); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + streamflags |= STREAM_POSTOUV3; + + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(&losTexture.GetTextureMatrix()._11); + glMatrixMode(GL_MODELVIEW); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); } else { - // Ambient + Diffuse * Shadow + // (Ambient + Diffuse * Shadow) * LOS glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); @@ -341,6 +379,24 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); + losTexture.BindTexture(2); + pglClientActiveTextureARB(GL_TEXTURE2); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + streamflags |= STREAM_POSTOUV2; + + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(&losTexture.GetTextureMatrix()._11); + glMatrixMode(GL_MODELVIEW); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); } } @@ -350,25 +406,52 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow) for (size_t i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); - patchdata->RenderStreams(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0, false); + patchdata->RenderStreams(streamflags); } - + glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); // restore OpenGL state - if (shadow) + g_Renderer.BindTexture(1, 0); + if (!shadow) + { + pglClientActiveTextureARB(GL_TEXTURE1); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + } + else { if (shadow->GetUseDepthTexture()) - g_Renderer.BindTexture(2,0); + { + g_Renderer.BindTexture(2, 0); + g_Renderer.BindTexture(3, 0); + + pglClientActiveTextureARB(GL_TEXTURE3); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + } + else + { + g_Renderer.BindTexture(2, 0); + + pglClientActiveTextureARB(GL_TEXTURE2); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + } } - g_Renderer.BindTexture(1,0); pglClientActiveTextureARB(GL_TEXTURE0); pglActiveTextureARB(GL_TEXTURE0); glDepthMask(1); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); @@ -386,7 +469,7 @@ void TerrainRenderer::RenderPatches() for(size_t i = 0; i < m->visiblePatches.size(); ++i) { CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData(); - patchdata->RenderStreams(STREAM_POS, true); + patchdata->RenderStreams(STREAM_POS); } glDisableClientState(GL_VERTEX_ARRAY); } @@ -435,9 +518,7 @@ void TerrainRenderer::RenderWater() } CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); - CmpPtr cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY); - debug_assert(!cmpRangeManager.null()); - ICmpRangeManager::CLosQuerier los (cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID())); + CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); @@ -479,6 +560,23 @@ void TerrainRenderer::RenderWater() glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); + + // Multiply by LOS texture + losTexture.BindTexture(1); + pglClientActiveTextureARB(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glLoadMatrixf(&losTexture.GetTextureMatrix()._11); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); } // Set the proper LOD bias @@ -488,7 +586,6 @@ void TerrainRenderer::RenderWater() CVector3D camPos = camera.m_Orientation.GetTranslation(); GLint vertexDepth = 0; // water depth attribute, if using fancy water - GLint losMultiplier = 0; // LOS multiplier, if using fancy water if(fancy) { @@ -500,6 +597,8 @@ void TerrainRenderer::RenderWater() glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, WaterMgr->m_RefractionTexture ); + losTexture.BindTexture(3); + // Bind water shader and set arguments ogl_program_use( m->fancyWaterShader ); @@ -518,9 +617,11 @@ void TerrainRenderer::RenderWater() GLint translation = ogl_program_get_uniform_location( m->fancyWaterShader, "translation" ); GLint reflectionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMatrix" ); GLint refractionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMatrix" ); + GLint losMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "losMatrix" ); GLint normalMap = ogl_program_get_uniform_location( m->fancyWaterShader, "normalMap" ); GLint reflectionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMap" ); GLint refractionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMap" ); + GLint losMap = ogl_program_get_uniform_location( m->fancyWaterShader, "losMap" ); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); pglUniform3fvARB( ambient, 1, &lightEnv.m_TerrainAmbientColor.X ); @@ -537,13 +638,14 @@ void TerrainRenderer::RenderWater() pglUniform4fARB( translation, tx, ty, 0, 0 ); pglUniformMatrix4fvARB( reflectionMatrix, 1, false, &WaterMgr->m_ReflectionMatrix._11 ); pglUniformMatrix4fvARB( refractionMatrix, 1, false, &WaterMgr->m_RefractionMatrix._11 ); + pglUniformMatrix4fvARB( losMatrix, 1, false, &losTexture.GetTextureMatrix()._11 ); pglUniform1iARB( normalMap, 0 ); // texture unit 0 pglUniform1iARB( reflectionMap, 1 ); // texture unit 1 pglUniform1iARB( refractionMap, 2 ); // texture unit 2 + pglUniform1iARB( losMap, 3 ); // texture unit 3 pglUniform3fvARB( cameraPos, 1, &camPos.X ); vertexDepth = ogl_program_get_attrib_location( m->fancyWaterShader, "vertexDepth" ); - losMultiplier = ogl_program_get_attrib_location( m->fancyWaterShader, "losMultiplier" ); } float repeatPeriod = (fancy ? WaterMgr->m_RepeatPeriod : 16.0f); @@ -589,18 +691,9 @@ void TerrainRenderer::RenderWater() float terrainHeight = terrain->GetVertexGroundLevel(ix, iz); - float losMod; - if (los.IsVisible(ix, iz)) - losMod = 1.0f; - else if (los.IsExplored(ix, iz)) - losMod = 0.7f; - else - losMod = 0.0f; - if (fancy) { pglVertexAttrib1fARB(vertexDepth, WaterMgr->m_WaterHeight - terrainHeight); - pglVertexAttrib1fARB(losMultiplier, losMod); pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatPeriod, vertZ/repeatPeriod); glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ); } @@ -616,11 +709,12 @@ void TerrainRenderer::RenderWater() // Invert and set boundaries FresnelScalar = 1.f - (FresnelScalar * 0.6); - glColor4f(WaterMgr->m_WaterColor.r*losMod, - WaterMgr->m_WaterColor.g*losMod, - WaterMgr->m_WaterColor.b*losMod, + glColor4f(WaterMgr->m_WaterColor.r, + WaterMgr->m_WaterColor.g, + WaterMgr->m_WaterColor.b, alpha * FresnelScalar); pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatPeriod, vertZ/repeatPeriod); + pglMultiTexCoord3fARB(GL_TEXTURE1, vertX, WaterMgr->m_WaterHeight, vertZ); glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ); } @@ -630,25 +724,30 @@ void TerrainRenderer::RenderWater() } glEnd(); - if(fancy) + if (fancy) { - // Unbind the refraction/reflection textures and the shader + // Unbind the LOS/refraction/reflection textures and the shader - pglActiveTextureARB( GL_TEXTURE1_ARB ); - glBindTexture( GL_TEXTURE_2D, 0 ); - glDisable( GL_TEXTURE_2D ); + g_Renderer.BindTexture(3, 0); + g_Renderer.BindTexture(2, 0); + g_Renderer.BindTexture(1, 0); - pglActiveTextureARB( GL_TEXTURE2_ARB ); - glBindTexture( GL_TEXTURE_2D, 0 ); - glDisable( GL_TEXTURE_2D ); - - pglActiveTextureARB( GL_TEXTURE0_ARB ); + pglActiveTextureARB(GL_TEXTURE0_ARB); - ogl_program_use( 0 ); + ogl_program_use(0); } - if(!fancy) + if (!fancy) { + g_Renderer.BindTexture(1, 0); + pglClientActiveTextureARB(GL_TEXTURE1_ARB); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glLoadIdentity(); + + pglActiveTextureARB(GL_TEXTURE0_ARB); + pglClientActiveTextureARB(GL_TEXTURE0_ARB); + // Clean up the texture matrix and blend mode glLoadIdentity(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); diff --git a/source/simulation2/components/CCmpRangeManager.cpp b/source/simulation2/components/CCmpRangeManager.cpp index 55924cd51f..9c0e125795 100644 --- a/source/simulation2/components/CCmpRangeManager.cpp +++ b/source/simulation2/components/CCmpRangeManager.cpp @@ -882,6 +882,8 @@ public: */ inline bool LosIsOffWorld(ssize_t i, ssize_t j) { + const ssize_t edgeSize = 3; // number of vertexes around the edge that will be off-world + if (m_LosCircular) { // With a circular map, vertex is off-world if hypot(i - size/2, j - size/2) >= size/2: @@ -889,13 +891,19 @@ public: ssize_t dist2 = (i - m_TerrainVerticesPerSide/2)*(i - m_TerrainVerticesPerSide/2) + (j - m_TerrainVerticesPerSide/2)*(j - m_TerrainVerticesPerSide/2); - if (dist2 >= (m_TerrainVerticesPerSide/2)*(m_TerrainVerticesPerSide/2)) - return true; + ssize_t r = m_TerrainVerticesPerSide/2 - edgeSize + 1; + // subtract a bit from the radius to ensure nice + // SoD blurring around the edges of the map + + return (dist2 >= r*r); } + else + { + // With a square map, the outermost edge of the map should be off-world, + // so the SoD texture blends out nicely - // With a square map, nothing is off-world - - return false; + return (i < edgeSize || j < edgeSize || i >= m_TerrainVerticesPerSide-edgeSize || j >= m_TerrainVerticesPerSide-edgeSize); + } } /** diff --git a/source/simulation2/components/ICmpRangeManager.h b/source/simulation2/components/ICmpRangeManager.h index dd961b03d6..9dcd569b11 100644 --- a/source/simulation2/components/ICmpRangeManager.h +++ b/source/simulation2/components/ICmpRangeManager.h @@ -169,9 +169,10 @@ public: { private: friend class CCmpRangeManager; + friend class TestLOSTexture; CLosQuerier(int player, const std::vector& data, ssize_t verticesPerSide) : - m_Data(data), m_VerticesPerSide(verticesPerSide) + m_Data(&data[0]), m_VerticesPerSide(verticesPerSide) { if (player > 0 && player <= 16) m_PlayerMask = LOS_MASK << (2*(player-1)); @@ -192,7 +193,7 @@ public: debug_assert(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide); #endif // Check high bit of each bit-pair - if ((m_Data.at(j*m_VerticesPerSide + i) & m_PlayerMask) & 0xAAAAAAAAu) + if ((m_Data[j*m_VerticesPerSide + i] & m_PlayerMask) & 0xAAAAAAAAu) return true; else return false; @@ -208,7 +209,7 @@ public: debug_assert(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide); #endif // Check low bit of each bit-pair - if ((m_Data.at(j*m_VerticesPerSide + i) & m_PlayerMask) & 0x55555555u) + if ((m_Data[j*m_VerticesPerSide + i] & m_PlayerMask) & 0x55555555u) return true; else return false; @@ -216,7 +217,7 @@ public: private: u32 m_PlayerMask; - const std::vector& m_Data; + const u32* m_Data; ssize_t m_VerticesPerSide; };