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