diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index 33fcc0c7d0..df26bebdaf 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -62,12 +62,15 @@ force_s3tc_enable = true renderpath = default ;;;;; EXPERIMENTAL ;;;;; -; Prefer GLSL shaders over ARB shaders (not recommended). REQUIRES gentangents=true +; Prefer GLSL shaders over ARB shaders (not recommended). REQUIRES gentangents=true. preferglsl = false ; Generate tangents for normal and parallax mapping. REQUIRES preferglsl=true. Incompatible with gpuskinning. gentangents = false +; Use smooth LOS interpolation; REQUIRES preferglsl=true. +smoothlos = false + ; Quality level of shader effects (set to 0 for lowest) materialmgr.quality = 9.0 diff --git a/binaries/data/mods/public/shaders/effects/los_interp.xml b/binaries/data/mods/public/shaders/effects/los_interp.xml new file mode 100644 index 0000000000..ab6abeb44a --- /dev/null +++ b/binaries/data/mods/public/shaders/effects/los_interp.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/binaries/data/mods/public/shaders/glsl/los_interp.fs b/binaries/data/mods/public/shaders/glsl/los_interp.fs new file mode 100644 index 0000000000..13a7f4df2c --- /dev/null +++ b/binaries/data/mods/public/shaders/glsl/los_interp.fs @@ -0,0 +1,17 @@ +#version 110 + +varying vec2 v_tex; + +uniform sampler2D losTex1, losTex2; + +uniform vec3 delta; + + +void main(void) +{ + float los2 = texture2D(losTex1, v_tex).a; + float los1 = texture2D(losTex2, v_tex).a; + + gl_FragColor.a = mix(los1, los2, clamp(delta.r, 0.0, 1.0)); +} + diff --git a/binaries/data/mods/public/shaders/glsl/los_interp.vs b/binaries/data/mods/public/shaders/glsl/los_interp.vs new file mode 100644 index 0000000000..56c3169d0f --- /dev/null +++ b/binaries/data/mods/public/shaders/glsl/los_interp.vs @@ -0,0 +1,19 @@ +#version 110 + +uniform mat4 transform; +uniform vec2 losTransform; + +varying vec2 v_tex; + +attribute vec3 a_vertex; +attribute vec2 a_uv0; + + +varying vec2 v_los; + +void main() +{ + gl_Position = gl_Vertex; + + v_tex = vec2(gl_MultiTexCoord0); +} diff --git a/binaries/data/mods/public/shaders/glsl/los_interp.xml b/binaries/data/mods/public/shaders/glsl/los_interp.xml new file mode 100644 index 0000000000..f7cfe06aa4 --- /dev/null +++ b/binaries/data/mods/public/shaders/glsl/los_interp.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/source/graphics/LOSTexture.cpp b/source/graphics/LOSTexture.cpp index 68e2f42c5b..de3121882b 100644 --- a/source/graphics/LOSTexture.cpp +++ b/source/graphics/LOSTexture.cpp @@ -19,11 +19,14 @@ #include "LOSTexture.h" +#include "graphics/ShaderManager.h" #include "graphics/Terrain.h" #include "lib/bits.h" +#include "ps/CLogger.h" #include "ps/Game.h" #include "ps/Profile.h" #include "renderer/Renderer.h" +#include "renderer/TimeManager.h" #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpRangeManager.h" #include "simulation2/components/ICmpTerrain.h" @@ -48,8 +51,23 @@ The blurred bitmap is then uploaded into a GL texture for use by the renderer. static const size_t g_BlurSize = 7; CLOSTexture::CLOSTexture(CSimulation2& simulation) : - m_Simulation(simulation), m_Dirty(true), m_Texture(0), m_MapSize(0), m_TextureSize(0) + m_Simulation(simulation), m_Dirty(true), m_Texture(0), m_smoothFbo(0), m_MapSize(0), m_TextureSize(0), whichTex(true) { + if (g_Renderer.m_Options.m_SmoothLOS) + { + m_smoothShader = g_Renderer.GetShaderManager().LoadEffect("los_interp"); + CShaderProgramPtr shader = m_smoothShader->GetShader(); + + if (m_smoothShader && shader) + { + pglGenFramebuffersEXT(1, &m_smoothFbo); + } + else + { + LOGERROR(L"Failed to load SmoothLOS shader, disabling."); + g_Renderer.m_Options.m_SmoothLOS = false; + } + } } CLOSTexture::~CLOSTexture() @@ -61,6 +79,11 @@ CLOSTexture::~CLOSTexture() void CLOSTexture::DeleteTexture() { glDeleteTextures(1, &m_Texture); + if (g_Renderer.m_Options.m_SmoothLOS) + { + glDeleteTextures(1, &m_TextureSmooth1); + glDeleteTextures(1, &m_TextureSmooth2); + } m_Texture = 0; } @@ -80,6 +103,71 @@ void CLOSTexture::BindTexture(int unit) g_Renderer.BindTexture(unit, m_Texture); } +GLuint CLOSTexture::GetTextureSmooth() +{ + if (!g_Renderer.m_Options.m_SmoothLOS) + return GetTexture(); + else + return whichTex ? m_TextureSmooth1 : m_TextureSmooth2; +} + +void CLOSTexture::InterpolateLOS() +{ + if (!g_Renderer.m_Options.m_SmoothLOS) + return; + + if (m_Dirty) + { + RecomputeTexture(0); + m_Dirty = false; + } + + GLint originalFBO; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &originalFBO); + + pglBindFramebufferEXT(GL_FRAMEBUFFER, m_smoothFbo); + pglFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + whichTex ? m_TextureSmooth2 : m_TextureSmooth1, 0); + + GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + LOGWARNING(L"LOS framebuffer object incomplete: 0x%04X", status); + } + + m_smoothShader->BeginPass(); + CShaderProgramPtr shader = m_smoothShader->GetShader(); + + shader->Bind(); + + shader->BindTexture("losTex1", m_Texture); + shader->BindTexture("losTex2", whichTex ? m_TextureSmooth1 : m_TextureSmooth2); + + shader->Uniform("delta", (float)g_Renderer.GetTimeManager().GetFrameDelta() * 4.0f, 0.0f, 0.0f, 0.0f); + + glPushAttrib(GL_VIEWPORT_BIT); + glViewport(0, 0, m_TextureSize, m_TextureSize); + + glBegin(GL_QUADS); + glColor4f(1.f, 1.f, 1.f, 1.f); + glTexCoord2f(1.0, 1.0); glVertex2f(1,1); + glTexCoord2f(0.0, 1.0); glVertex2f(-1,1); + glTexCoord2f(0.0, 0.0); glVertex2f(-1,-1); + glTexCoord2f(1.0, 0.0); glVertex2f(1,-1); + glEnd(); + + glPopAttrib(); + shader->Unbind(); + m_smoothShader->EndPass(); + + pglFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + + pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, originalFBO); + + whichTex = !whichTex; +} + + GLuint CLOSTexture::GetTexture() { if (m_Dirty) @@ -114,20 +202,41 @@ void CLOSTexture::ConstructTexture(int unit) m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize + g_BlurSize - 1); glGenTextures(1, &m_Texture); - g_Renderer.BindTexture(unit, m_Texture); // Initialise texture with SoD colour, for the areas we don't // overwrite with glTexSubImage2D later - u8* texData = new u8[m_TextureSize * m_TextureSize]; - memset(texData, 0x00, m_TextureSize * m_TextureSize); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texData); - delete[] texData; + u8* texData = new u8[m_TextureSize * m_TextureSize * 4]; + memset(texData, 0x00, m_TextureSize * m_TextureSize * 4); + + if (g_Renderer.m_Options.m_SmoothLOS) + { + glGenTextures(1, &m_TextureSmooth1); + glGenTextures(1, &m_TextureSmooth2); + + g_Renderer.BindTexture(unit, m_TextureSmooth1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 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); + g_Renderer.BindTexture(unit, m_TextureSmooth2); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 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); + } + + g_Renderer.BindTexture(unit, m_Texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 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); - + + delete[] texData; + { // Texture matrix: We want to map // world pos (0, y, 0) (i.e. first vertex) @@ -166,8 +275,12 @@ void CLOSTexture::RecomputeTexture(int unit) DeleteTexture(); } + bool recreated = false; if (!m_Texture) + { ConstructTexture(unit); + recreated = true; + } PROFILE("recompute LOS texture"); @@ -182,6 +295,14 @@ void CLOSTexture::RecomputeTexture(int unit) GenerateBitmap(los, &losData[0], m_MapSize, m_MapSize); + if (g_Renderer.m_Options.m_SmoothLOS && recreated) + { + g_Renderer.BindTexture(unit, m_TextureSmooth1); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize + g_BlurSize - 1, m_MapSize + g_BlurSize - 1, GL_ALPHA, GL_UNSIGNED_BYTE, &losData[0]); + g_Renderer.BindTexture(unit, m_TextureSmooth2); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize + g_BlurSize - 1, m_MapSize + g_BlurSize - 1, GL_ALPHA, GL_UNSIGNED_BYTE, &losData[0]); + } + 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]); } diff --git a/source/graphics/LOSTexture.h b/source/graphics/LOSTexture.h index 70ec1aa11a..14df5d1c59 100644 --- a/source/graphics/LOSTexture.h +++ b/source/graphics/LOSTexture.h @@ -20,6 +20,9 @@ #include "maths/Matrix3D.h" #include "simulation2/components/ICmpRangeManager.h" +#include "graphics/ShaderManager.h" + + class CSimulation2; /** @@ -55,6 +58,9 @@ public: * The texture is in 8-bit ALPHA format. */ GLuint GetTexture(); + + void InterpolateLOS(); + GLuint GetTextureSmooth(); /** * Returns a matrix to map (x,y,z) world coordinates onto (u,v) LOS texture @@ -83,6 +89,12 @@ private: bool m_Dirty; GLuint m_Texture; + GLuint m_TextureSmooth1, m_TextureSmooth2; + + bool whichTex; + + GLuint m_smoothFbo; + CShaderTechniquePtr m_smoothShader; ssize_t m_MapSize; // vertexes per side GLsizei m_TextureSize; // texels per side diff --git a/source/renderer/RenderModifiers.cpp b/source/renderer/RenderModifiers.cpp index 457b6b4ba2..bd2ed99045 100644 --- a/source/renderer/RenderModifiers.cpp +++ b/source/renderer/RenderModifiers.cpp @@ -95,7 +95,7 @@ void ShaderRenderModifier::BeginPass(const CShaderProgramPtr& shader) if (shader->GetTextureBinding("losTex").Active()) { CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); - shader->BindTexture("losTex", los.GetTexture()); + shader->BindTexture("losTex", los.GetTextureSmooth()); // Don't bother sending the whole matrix, we just need two floats (scale and translation) shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); } diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index bf0baa87e4..0e4ccee5e9 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -46,6 +46,7 @@ #include "graphics/Camera.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" +#include "graphics/LOSTexture.h" #include "graphics/MaterialManager.h" #include "graphics/Model.h" #include "graphics/ModelDef.h" @@ -435,12 +436,14 @@ CRenderer::CRenderer() m_Options.m_ForceAlphaTest = false; m_Options.m_GPUSkinning = false; m_Options.m_GenTangents = false; + m_Options.m_SmoothLOS = false; // TODO: be more consistent in use of the config system CFG_GET_USER_VAL("preferglsl", Bool, m_Options.m_PreferGLSL); CFG_GET_USER_VAL("forcealphatest", Bool, m_Options.m_ForceAlphaTest); CFG_GET_USER_VAL("gpuskinning", Bool, m_Options.m_GPUSkinning); CFG_GET_USER_VAL("gentangents", Bool, m_Options.m_GenTangents); + CFG_GET_USER_VAL("smoothlos", Bool, m_Options.m_SmoothLOS); #if CONFIG2_GLES // Override config option since GLES only supports GLSL @@ -1358,6 +1361,8 @@ void CRenderer::RenderParticles() void CRenderer::RenderSubmissions() { PROFILE3("render submissions"); + + GetScene().GetLOSTexture().InterpolateLOS(); CShaderDefines context = m->globalContext; diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h index 7f012c5b95..f1406dc369 100644 --- a/source/renderer/Renderer.h +++ b/source/renderer/Renderer.h @@ -132,6 +132,7 @@ public: bool m_GPUSkinning; bool m_Silhouettes; bool m_GenTangents; + bool m_SmoothLOS; } m_Options; struct Caps { diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index f077b94b0a..2ede913377 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -477,7 +477,7 @@ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shader->Uniform("sunColor", lightEnv.m_SunColor); CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); - shader->BindTexture("losTex", los.GetTexture()); + shader->BindTexture("losTex", los.GetTextureSmooth()); shader->Uniform("losTransform", los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); shader->Uniform("ambient", lightEnv.m_TerrainAmbientColor); @@ -709,7 +709,7 @@ bool TerrainRenderer::RenderFancyWater() m->fancyWaterShader->BindTexture("reflectionMap", WaterMgr->m_ReflectionTexture); m->fancyWaterShader->BindTexture("refractionMap", WaterMgr->m_RefractionTexture); - m->fancyWaterShader->BindTexture("losMap", losTexture.GetTexture()); + m->fancyWaterShader->BindTexture("losMap", losTexture.GetTextureSmooth()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); m->fancyWaterShader->Uniform("ambient", lightEnv.m_TerrainAmbientColor); diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp index 0d5c4971ef..643c200297 100644 --- a/source/simulation2/components/CCmpVisualActor.cpp +++ b/source/simulation2/components/CCmpVisualActor.cpp @@ -617,7 +617,13 @@ void CCmpVisualActor::UpdateVisibility() else { CmpPtr cmpRangeManager(GetSimContext(), SYSTEM_ENTITY); - m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityId(), GetSimContext().GetCurrentDisplayedPlayer()); + // Uncomment the following lines to prevent the models from popping into existence + // near the LOS boundary. Is rather resource intensive. + //if (cmpVision->GetRetainInFog()) + // m_Visibility = ICmpRangeManager::VIS_VISIBLE; + //else + m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityId(), + GetSimContext().GetCurrentDisplayedPlayer()); } } else