diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg
index 0caff11d44..d18a3d026b 100644
--- a/binaries/data/config/default.cfg
+++ b/binaries/data/config/default.cfg
@@ -79,6 +79,9 @@ gentangents = false
; Use smooth LOS interpolation; REQUIRES preferglsl=true.
smoothlos = false
+; Use screen-space postprocessing filters (HDR, bloom, DOF, etc). Incompatible with fixed renderpath.
+postproc = false
+
; Quality level of shader effects (set to 10 to display effects)
materialmgr.quality = 0.0
diff --git a/binaries/data/mods/public/maps/scenarios/Campaign Test Map.xml b/binaries/data/mods/public/maps/scenarios/Campaign Test Map.xml
index df37b06fbb..f2e7a04125 100644
--- a/binaries/data/mods/public/maps/scenarios/Campaign Test Map.xml
+++ b/binaries/data/mods/public/maps/scenarios/Campaign Test Map.xml
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a3b12fc6b3da1dbef90348606bcf1e12a0e7e2a8b9eb751bba1cedb064622d99
-size 190657
+oid sha256:3e91e18a42622d72c099acee2decf0e18027e88d4437b86570faa06d9136696f
+size 190919
diff --git a/binaries/data/mods/public/maps/scenarios/Zagros Mountains 01.xml b/binaries/data/mods/public/maps/scenarios/Zagros Mountains 01.xml
index 15969c55f5..bb799e6800 100644
--- a/binaries/data/mods/public/maps/scenarios/Zagros Mountains 01.xml
+++ b/binaries/data/mods/public/maps/scenarios/Zagros Mountains 01.xml
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:56d0eda8fe274afa3fa7b00ff35428183c0830411e0a22a078a475b9d55ac830
-size 706864
+oid sha256:d87b6e084d998147a8245803d50778c9dfbd4a58b3a4a355c15704ca69429a53
+size 707151
diff --git a/binaries/data/mods/public/shaders/effects/bloom.xml b/binaries/data/mods/public/shaders/effects/bloom.xml
new file mode 100644
index 0000000000..49107f8af9
--- /dev/null
+++ b/binaries/data/mods/public/shaders/effects/bloom.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/effects/model_transparent.xml b/binaries/data/mods/public/shaders/effects/model_transparent.xml
index 8a03fe1358..f3e1d4be1e 100644
--- a/binaries/data/mods/public/shaders/effects/model_transparent.xml
+++ b/binaries/data/mods/public/shaders/effects/model_transparent.xml
@@ -89,7 +89,7 @@
-
+
@@ -98,7 +98,7 @@
-
+
@@ -106,7 +106,7 @@
-
+
diff --git a/binaries/data/mods/public/shaders/effects/postproc/DOF.xml b/binaries/data/mods/public/shaders/effects/postproc/DOF.xml
new file mode 100644
index 0000000000..b02e0e6000
--- /dev/null
+++ b/binaries/data/mods/public/shaders/effects/postproc/DOF.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/effects/postproc/hdr.xml b/binaries/data/mods/public/shaders/effects/postproc/hdr.xml
new file mode 100644
index 0000000000..cb5a31d647
--- /dev/null
+++ b/binaries/data/mods/public/shaders/effects/postproc/hdr.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/glsl/bloom.fs b/binaries/data/mods/public/shaders/glsl/bloom.fs
new file mode 100644
index 0000000000..1f91b44fbb
--- /dev/null
+++ b/binaries/data/mods/public/shaders/glsl/bloom.fs
@@ -0,0 +1,107 @@
+#version 110
+
+varying vec2 v_tex;
+
+uniform sampler2D renderedTex;
+
+uniform vec2 texSize;
+
+
+void main()
+{
+ #if BLOOM_NOP
+ gl_FragColor = texture2D(renderedTex, v_tex);
+ #endif
+
+ #if BLOOM_PASS_H
+ vec4 colour;
+
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(-2.5, 0.0)) / texSize) * 0.05;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(-1.5, 0.0)) / texSize) * 0.1;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(-0.5, 0.0)) / texSize) * 0.2;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2( 0.0, 0.0)) / texSize) * 0.3;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2( 0.5, 0.0)) / texSize) * 0.2;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2( 1.5, 0.0)) / texSize) * 0.1;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2( 2.5, 0.0)) / texSize) * 0.05;
+
+ gl_FragColor.rgb = colour.rgb;
+ gl_FragColor.a = 1.0;
+ #endif
+
+ #if BLOOM_PASS_V
+ vec4 colour;
+
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(0.0, -2.5)) / texSize) * 0.05;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(0.0, -1.5)) / texSize) * 0.1;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(0.0, -0.5)) / texSize) * 0.2;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(0.0, 0.0)) / texSize) * 0.3;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(0.0, 0.5)) / texSize) * 0.2;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(0.0, 1.5)) / texSize) * 0.1;
+ colour += texture2D(renderedTex, (gl_FragCoord.xy + vec2(0.0, 2.5)) / texSize) * 0.05;
+
+ gl_FragColor.rgb = colour.rgb;
+ gl_FragColor.a = 1.0;
+ #endif
+
+}
+
+
+
+/*varying vec2 v_tex;
+
+uniform sampler2D bgl_RenderedTexture;
+uniform sampler2D bgl_DepthTexture;
+uniform sampler2D bgl_LuminanceTexture;
+uniform float bgl_RenderedTextureWidth;
+uniform float bgl_RenderedTextureHeight;
+
+#define PI 3.14159265
+
+float width = bgl_RenderedTextureWidth; //texture width
+float height = bgl_RenderedTextureHeight; //texture height
+
+vec2 texCoord = v_tex;
+vec2 texcoord = v_tex;
+
+
+float BRIGHT_PASS_THRESHOLD = 0.6;
+float BRIGHT_PASS_OFFSET = 0.6;
+
+#define blurclamp 0.0015
+#define bias 0.01
+
+#define KERNEL_SIZE 3.0
+
+
+
+vec4 bright(vec2 coo)
+{
+ vec4 color = texture2D(bgl_RenderedTexture, coo);
+
+ color = max(color - BRIGHT_PASS_THRESHOLD, 0.0);
+
+ return color / (color + BRIGHT_PASS_OFFSET);
+}
+
+
+void main0(void)
+{
+ vec2 blur = vec2(clamp( bias, -blurclamp, blurclamp ));
+
+ vec4 col = vec4( 0, 0, 0, 0 );
+ for ( float x = -KERNEL_SIZE + 1.0; x < KERNEL_SIZE; x += 1.0 )
+ {
+ for ( float y = -KERNEL_SIZE + 1.0; y < KERNEL_SIZE; y += 1.0 )
+ {
+ col += bright( texcoord + vec2( blur.x * x, blur.y * y ) );
+ }
+ }
+ col /= ((KERNEL_SIZE+KERNEL_SIZE)-1.0)*((KERNEL_SIZE+KERNEL_SIZE)-1.0);
+
+ //gl_FragColor = col + texture2D(bgl_RenderedTexture, texcoord);
+
+ //col *= 0.9;
+ gl_FragColor = 1.0 - (1.0 - col) * (1.0 - texture2D(bgl_RenderedTexture, texcoord));
+
+}*/
+
diff --git a/binaries/data/mods/public/shaders/glsl/bloom.xml b/binaries/data/mods/public/shaders/glsl/bloom.xml
new file mode 100644
index 0000000000..2f901bb5f6
--- /dev/null
+++ b/binaries/data/mods/public/shaders/glsl/bloom.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/glsl/dof.fs b/binaries/data/mods/public/shaders/glsl/dof.fs
new file mode 100644
index 0000000000..ebdfb114b3
--- /dev/null
+++ b/binaries/data/mods/public/shaders/glsl/dof.fs
@@ -0,0 +1,63 @@
+#version 120
+
+uniform sampler2D renderedTex;
+uniform sampler2D depthTex;
+uniform sampler2D blurTex2;
+uniform sampler2D blurTex4;
+uniform sampler2D blurTex8;
+
+uniform float width;
+uniform float height;
+
+uniform float zNear;
+uniform float zFar;
+
+uniform float brightness;
+uniform float hdr;
+uniform float saturation;
+uniform float bloom;
+
+varying vec2 v_tex;
+
+
+float linearizeDepth(float depth)
+{
+ return -zFar * zNear / (depth * (zFar - zNear) - zFar);
+}
+
+
+void main(void)
+{
+ vec3 colour = texture2D(renderedTex, v_tex).rgb;
+
+ vec3 blur2 = texture2D(blurTex2, v_tex).rgb;
+ vec3 blur4 = texture2D(blurTex4, v_tex).rgb;
+ vec3 blur8 = texture2D(blurTex8, v_tex).rgb;
+
+ float depth = texture2D(depthTex, v_tex).r;
+
+ float midDepth = texture2D(depthTex, vec2(0.5, 0.5)).r;
+ midDepth += texture2D(depthTex, vec2(0.4, 0.4)).r;
+ midDepth += texture2D(depthTex, vec2(0.4, 0.6)).r;
+ midDepth += texture2D(depthTex, vec2(0.6, 0.4)).r;
+ midDepth += texture2D(depthTex, vec2(0.6, 0.6)).r;
+
+ midDepth /= 5.0;
+
+
+ float lDepth = linearizeDepth(depth);
+ float lMidDepth = linearizeDepth(midDepth);
+ float amount = abs(lDepth - lMidDepth);
+
+ amount = clamp(amount / (lMidDepth * BLUR_FOV), 0.0, 1.0);
+
+ colour = (amount >= 0.0 && amount < 0.25) ? mix(colour, blur2, (amount - 0.0) / (0.25)) : colour;
+ colour = (amount >= 0.25 && amount < 0.50) ? mix(blur2, blur4, (amount - 0.25) / (0.25)) : colour;
+ colour = (amount >= 0.50 && amount < 0.75) ? mix(blur4, blur8, (amount - 0.50) / (0.25)) : colour;
+ colour = (amount >= 0.75 && amount <= 1.00) ? blur8 : colour;
+
+ gl_FragColor.rgb = colour;
+ gl_FragColor.a = 1.0;
+}
+
+
diff --git a/binaries/data/mods/public/shaders/glsl/dof.xml b/binaries/data/mods/public/shaders/glsl/dof.xml
new file mode 100644
index 0000000000..c3212bf968
--- /dev/null
+++ b/binaries/data/mods/public/shaders/glsl/dof.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/glsl/hdr.fs b/binaries/data/mods/public/shaders/glsl/hdr.fs
new file mode 100644
index 0000000000..91f9c693e1
--- /dev/null
+++ b/binaries/data/mods/public/shaders/glsl/hdr.fs
@@ -0,0 +1,50 @@
+#version 120
+
+uniform sampler2D renderedTex;
+uniform sampler2D depthTex;
+uniform sampler2D blurTex2;
+uniform sampler2D blurTex4;
+uniform sampler2D blurTex8;
+
+uniform float width;
+uniform float height;
+
+uniform float brightness;
+uniform float hdr;
+uniform float saturation;
+uniform float bloom;
+
+varying vec2 v_tex;
+
+
+void main(void)
+{
+
+
+ vec3 colour = texture2D(renderedTex, v_tex).rgb;
+ vec3 bloomv2 = texture2D(blurTex2, v_tex).rgb;
+ vec3 bloomv4 = texture2D(blurTex4, v_tex).rgb;
+ vec3 bloomv8 = texture2D(blurTex8, v_tex).rgb;
+
+ bloomv2 = max(bloomv2 - bloom, vec3(0.0));
+ bloomv4 = max(bloomv4 - bloom, vec3(0.0));
+ bloomv8 = max(bloomv8 - bloom, vec3(0.0));
+
+ vec3 bloomv = (bloomv2 + bloomv4 + bloomv8) / 3.0;
+
+ colour = max(bloomv, colour);
+
+ colour += vec3(brightness);
+
+ colour -= vec3(0.5);
+ colour *= vec3(hdr);
+ colour += vec3(0.5);
+
+ colour = mix(vec3(dot(colour, vec3(0.299, 0.587, 0.114))), colour, saturation);
+
+
+ gl_FragColor.rgb = colour;
+ gl_FragColor.a = 1.0;
+}
+
+
diff --git a/binaries/data/mods/public/shaders/glsl/hdr.vs b/binaries/data/mods/public/shaders/glsl/hdr.vs
new file mode 100644
index 0000000000..a4acbb9885
--- /dev/null
+++ b/binaries/data/mods/public/shaders/glsl/hdr.vs
@@ -0,0 +1,10 @@
+#version 110
+
+varying vec2 v_tex;
+
+void main()
+{
+ gl_Position = gl_Vertex;
+
+ v_tex = vec2(gl_MultiTexCoord0);
+}
diff --git a/binaries/data/mods/public/shaders/glsl/hdr.xml b/binaries/data/mods/public/shaders/glsl/hdr.xml
new file mode 100644
index 0000000000..b17956761c
--- /dev/null
+++ b/binaries/data/mods/public/shaders/glsl/hdr.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/binaries/data/mods/public/shaders/glsl/model_common.fs b/binaries/data/mods/public/shaders/glsl/model_common.fs
index 3a9d434b55..d6bb9dd0a4 100644
--- a/binaries/data/mods/public/shaders/glsl/model_common.fs
+++ b/binaries/data/mods/public/shaders/glsl/model_common.fs
@@ -31,6 +31,9 @@ uniform vec3 ambient;
uniform vec3 sunColor;
uniform vec3 sunDir;
+uniform vec3 fogColor;
+uniform vec2 fogParams;
+
varying vec4 v_lighting;
varying vec2 v_tex;
varying vec2 v_los;
@@ -89,6 +92,22 @@ float get_shadow()
#endif
}
+vec3 get_fog(vec3 color)
+{
+ float density = fogParams.x;
+ float maxFog = fogParams.y;
+
+ const float LOG2 = 1.442695;
+ float z = gl_FragCoord.z / gl_FragCoord.w;
+ float fogFactor = exp2(-density * density * z * z * LOG2);
+
+ fogFactor = fogFactor * (1.0 - maxFog) + maxFog;
+
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+
+ return mix(fogColor, color, fogFactor);
+}
+
void main()
{
vec2 coord = v_tex;
@@ -244,6 +263,8 @@ void main()
color = mix(texdiffuse, color, specular.a);
#endif
+ color = get_fog(color);
+
#if !IGNORE_LOS
float los = texture2D(losTex, v_los).a;
los = los < 0.03 ? 0.0 : los;
diff --git a/binaries/data/mods/public/shaders/glsl/terrain_common.fs b/binaries/data/mods/public/shaders/glsl/terrain_common.fs
index 929e638f46..f54880d40d 100644
--- a/binaries/data/mods/public/shaders/glsl/terrain_common.fs
+++ b/binaries/data/mods/public/shaders/glsl/terrain_common.fs
@@ -22,6 +22,9 @@ uniform vec3 ambient;
uniform vec3 sunColor;
uniform vec3 sunDir;
+uniform vec3 fogColor;
+uniform vec2 fogParams;
+
uniform vec2 textureTransform;
varying vec3 v_lighting;
@@ -140,6 +143,21 @@ vec4 triplanarNormals(sampler2D sampler, vec3 wpos)
}
#endif
+vec3 get_fog(vec3 color)
+{
+ float density = fogParams.x;
+ float maxFog = fogParams.y;
+
+ const float LOG2 = 1.442695;
+ float z = gl_FragCoord.z / gl_FragCoord.w;
+ float fogFactor = exp2(-density * density * z * z * LOG2);
+
+ fogFactor = fogFactor * (1.0 - maxFog) + maxFog;
+
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+
+ return mix(fogColor, color, fogFactor);
+}
void main()
{
@@ -218,6 +236,8 @@ void main()
color = mix(texdiffuse, color, specular.a);
#endif
+ color = get_fog(color);
+
float los = texture2D(losTex, v_los).a;
los = los < 0.03 ? 0.0 : los;
color *= los;
diff --git a/binaries/data/mods/public/shaders/glsl/water_high.fs b/binaries/data/mods/public/shaders/glsl/water_high.fs
index 1d177337de..4427579b38 100644
--- a/binaries/data/mods/public/shaders/glsl/water_high.fs
+++ b/binaries/data/mods/public/shaders/glsl/water_high.fs
@@ -17,9 +17,28 @@ uniform float fullDepth; // Depth at which to use full murkiness (shallower wat
uniform vec3 reflectionTint; // Tint for reflection (used for really muddy water)
uniform float reflectionTintStrength; // Strength of reflection tint (how much of it to mix in)
+uniform vec3 fogColor;
+uniform vec2 fogParams;
+
varying vec3 worldPos;
varying float waterDepth;
+vec3 get_fog(vec3 color)
+{
+ float density = fogParams.x;
+ float maxFog = fogParams.y;
+
+ const float LOG2 = 1.442695;
+ float z = gl_FragCoord.z / gl_FragCoord.w;
+ float fogFactor = exp2(-density * density * z * z * LOG2);
+
+ fogFactor = fogFactor * (1.0 - maxFog) + maxFog;
+
+ fogFactor = clamp(fogFactor, 0.0, 1.0);
+
+ return mix(fogColor, color, fogFactor);
+}
+
void main()
{
vec3 n, l, h, v; // Normal, light vector, half-vector and view vector (vector to eye)
@@ -55,7 +74,11 @@ void main()
losMod = texture2D(losMap, gl_TexCoord[3].st).a;
losMod = losMod < 0.03 ? 0.0 : losMod;
- gl_FragColor.rgb = mix(refrColor + 0.3*specular, reflColor + specular, fresnel) * losMod;
+ gl_FragColor.rgb = mix(refrColor + 0.3*specular, reflColor + specular, fresnel);
+
+ gl_FragColor.rgb = get_fog(gl_FragColor.rgb);
+
+ gl_FragColor.rgb *= losMod;
// Make alpha vary based on both depth (so it blends with the shore) and view angle (make it
// become opaque faster at lower view angles so we can't look "underneath" the water plane)
diff --git a/source/graphics/LightEnv.cpp b/source/graphics/LightEnv.cpp
index 07234e3013..8634ef60fd 100644
--- a/source/graphics/LightEnv.cpp
+++ b/source/graphics/LightEnv.cpp
@@ -32,7 +32,11 @@ CLightEnv::CLightEnv()
m_LightingModel("standard"),
m_SunColor(1.5, 1.5, 1.5),
m_TerrainAmbientColor(0x50/255.f, 0x60/255.f, 0x85/255.f),
- m_UnitsAmbientColor(0x80/255.f, 0x80/255.f, 0x80/255.f)
+ m_UnitsAmbientColor(0x80/255.f, 0x80/255.f, 0x80/255.f),
+ m_FogColor(0xCC/255.f, 0xCC/255.f, 0xE5/255.f),
+ m_FogFactor(0.000),
+ m_FogMax(0.5),
+ m_Brightness(0.0), m_Contrast(1.0), m_Saturation(0.99), m_Bloom(0.1999)
{
CalculateSunDirection();
}
diff --git a/source/graphics/LightEnv.h b/source/graphics/LightEnv.h
index 272fa7bf90..1e3d0399c4 100644
--- a/source/graphics/LightEnv.h
+++ b/source/graphics/LightEnv.h
@@ -69,6 +69,12 @@ public:
RGBColor m_SunColor;
RGBColor m_TerrainAmbientColor;
RGBColor m_UnitsAmbientColor;
+ RGBColor m_FogColor;
+
+ float m_FogFactor;
+ float m_FogMax;
+
+ float m_Brightness, m_Contrast, m_Saturation, m_Bloom;
public:
CLightEnv();
@@ -134,7 +140,14 @@ public:
m_LightingModel == o.m_LightingModel &&
m_SunColor == o.m_SunColor &&
m_TerrainAmbientColor == o.m_TerrainAmbientColor &&
- m_UnitsAmbientColor == o.m_UnitsAmbientColor;
+ m_UnitsAmbientColor == o.m_UnitsAmbientColor &&
+ m_FogColor == o.m_FogColor &&
+ m_FogFactor == o.m_FogFactor &&
+ m_FogMax == o.m_FogMax &&
+ m_Brightness == o.m_Brightness &&
+ m_Contrast == o.m_Contrast &&
+ m_Saturation == o.m_Saturation &&
+ m_Bloom == o.m_Bloom;
}
bool operator!=(const CLightEnv& o) const
diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp
index eea9de6d9d..a142ce55a3 100644
--- a/source/graphics/MapReader.cpp
+++ b/source/graphics/MapReader.cpp
@@ -36,6 +36,7 @@
#include "ps/LoaderThunks.h"
#include "ps/World.h"
#include "ps/XML/Xeromyces.h"
+#include "renderer/PostprocManager.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
@@ -56,13 +57,15 @@ CMapReader::CMapReader()
cur_terrain_tex = 0; // important - resets generator state
// Maps that don't override the default probably want the old lighting model
- m_LightEnv.SetLightingModel("old");
+ //m_LightEnv.SetLightingModel("old");
+ //pPostproc->SetPostEffect(L"default");
+
}
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
WaterManager* pWaterMan_, SkyManager* pSkyMan_,
- CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_,
+ CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,
CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities)
{
// latch parameters (held until DelayedLoadFinished)
@@ -73,6 +76,7 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
pSkyMan = pSkyMan_;
pCinema = pCinema_;
pTrigMan = pTrigMan_;
+ pPostproc = pPostproc_;
pSimulation2 = pSimulation2_;
pSimContext = pSimContext_;
m_PlayerID = playerID_;
@@ -106,6 +110,10 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
// delete all existing entities
if (pSimulation2)
pSimulation2->ResetState();
+
+ // reset post effects
+ if (pPostproc)
+ pPostproc->SetPostEffect(L"default");
// load map settings script
RegMemFun(this, &CMapReader::LoadScriptSettings, L"CMapReader::LoadScriptSettings", 50);
@@ -133,7 +141,7 @@ void CMapReader::LoadMap(const VfsPath& pathname, CTerrain *pTerrain_,
// LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful
void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain *pTerrain_,
WaterManager* pWaterMan_, SkyManager* pSkyMan_,
- CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_,
+ CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,
CSimulation2 *pSimulation2_, int playerID_)
{
// latch parameters (held until DelayedLoadFinished)
@@ -146,6 +154,7 @@ void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted&
pSkyMan = pSkyMan_;
pCinema = pCinema_;
pTrigMan = pTrigMan_;
+ pPostproc = pPostproc_;
pSimulation2 = pSimulation2_;
pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL;
m_PlayerID = playerID_;
@@ -558,6 +567,7 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(lightingmodel);
+ EL(posteffect);
EL(skyset);
EL(suncolour);
EL(sunelevation);
@@ -575,6 +585,15 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
EL(tint);
EL(reflectiontint);
EL(reflectiontintstrength);
+ EL(fog);
+ EL(fogcolour);
+ EL(fogfactor);
+ EL(fogthickness);
+ EL(postproc);
+ EL(brightness);
+ EL(contrast);
+ EL(saturation);
+ EL(bloom);
AT(r); AT(g); AT(b);
#undef AT
#undef EL
@@ -587,7 +606,7 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
if (element_name == el_lightingmodel)
{
- m_MapReader.m_LightEnv.SetLightingModel(element.GetText());
+ // NOP - obsolete.
}
else if (element_name == el_skyset)
{
@@ -623,6 +642,56 @@ void CXMLReader::ReadEnvironment(XMBElement parent)
attrs.GetNamedItem(at_g).ToFloat(),
attrs.GetNamedItem(at_b).ToFloat());
}
+ else if (element_name == el_fog)
+ {
+ XERO_ITER_EL(element, fog)
+ {
+ int element_name = fog.GetNodeName();
+ if (element_name == el_fogcolour)
+ {
+ XMBAttributeList attrs = fog.GetAttributes();
+ m_MapReader.m_LightEnv.m_FogColor = RGBColor(
+ attrs.GetNamedItem(at_r).ToFloat(),
+ attrs.GetNamedItem(at_g).ToFloat(),
+ attrs.GetNamedItem(at_b).ToFloat());
+ }
+ else if (element_name == el_fogfactor)
+ {
+ m_MapReader.m_LightEnv.m_FogFactor = fog.GetText().ToFloat();
+ }
+ else if (element_name == el_fogthickness)
+ {
+ m_MapReader.m_LightEnv.m_FogMax = fog.GetText().ToFloat();
+ }
+ }
+ }
+ else if (element_name == el_postproc)
+ {
+ XERO_ITER_EL(element, postproc)
+ {
+ int element_name = postproc.GetNodeName();
+ if (element_name == el_brightness)
+ {
+ m_MapReader.m_LightEnv.m_Brightness = postproc.GetText().ToFloat();
+ }
+ else if (element_name == el_contrast)
+ {
+ m_MapReader.m_LightEnv.m_Contrast = postproc.GetText().ToFloat();
+ }
+ else if (element_name == el_saturation)
+ {
+ m_MapReader.m_LightEnv.m_Saturation = postproc.GetText().ToFloat();
+ }
+ else if (element_name == el_bloom)
+ {
+ m_MapReader.m_LightEnv.m_Bloom = postproc.GetText().ToFloat();
+ }
+ else if (element_name == el_posteffect)
+ {
+ m_MapReader.pPostproc->SetPostEffect(postproc.GetText().FromUTF8());
+ }
+ }
+ }
else if (element_name == el_water)
{
XERO_ITER_EL(element, waterbody)
@@ -1321,7 +1390,9 @@ int CMapReader::ParseEnvironment()
return 0;
}
- m_LightEnv.SetLightingModel("standard");
+ //m_LightEnv.SetLightingModel("standard");
+ if (pPostproc)
+ pPostproc->SetPostEffect(L"default");
std::wstring skySet;
GET_ENVIRONMENT_PROPERTY(envObj.get(), SkySet, skySet)
diff --git a/source/graphics/MapReader.h b/source/graphics/MapReader.h
index 740b417d35..169e08c0b2 100644
--- a/source/graphics/MapReader.h
+++ b/source/graphics/MapReader.h
@@ -32,6 +32,7 @@ class WaterManager;
class SkyManager;
class CLightEnv;
class CCinemaManager;
+class CPostprocManager;
class CTriggerManager;
class CSimulation2;
class CSimContext;
@@ -53,10 +54,10 @@ public:
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void LoadMap(const VfsPath& pathname, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
- CCinemaManager*, CTriggerManager*, CSimulation2*, const CSimContext*, int playerID, bool skipEntities);
+ CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc, CSimulation2*, const CSimContext*,
+ int playerID, bool skipEntities);
- void LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*,
- CCinemaManager*, CTriggerManager*, CSimulation2*, int playerID);
+ void LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain*, WaterManager*, SkyManager*, CLightEnv*, CGameView*, CCinemaManager*, CTriggerManager*, CPostprocManager* pPostproc_, CSimulation2*, int playerID);
private:
// Load script settings for use by scripts
@@ -129,6 +130,7 @@ private:
CTerrain* pTerrain;
WaterManager* pWaterMan;
SkyManager* pSkyMan;
+ CPostprocManager* pPostproc;
CLightEnv* pLightEnv;
CGameView* pGameView;
CCinemaManager* pCinema;
diff --git a/source/graphics/MapWriter.cpp b/source/graphics/MapWriter.cpp
index 39a28b52e7..6cd4c4da43 100644
--- a/source/graphics/MapWriter.cpp
+++ b/source/graphics/MapWriter.cpp
@@ -33,6 +33,7 @@
#include "ps/Loader.h"
#include "ps/Filesystem.h"
#include "ps/XML/XMLWriter.h"
+#include "renderer/PostprocManager.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
@@ -53,6 +54,7 @@ CMapWriter::CMapWriter()
void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,
WaterManager* pWaterMan, SkyManager* pSkyMan,
CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,
+ CPostprocManager* pPostproc,
CSimulation2* pSimulation2)
{
CFilePacker packer(FILE_VERSION, "PSMP");
@@ -64,7 +66,7 @@ void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,
packer.Write(pathname);
VfsPath pathnameXML = pathname.ChangeExtension(L".xml");
- WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema, pSimulation2);
+ WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema, pPostproc, pSimulation2);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -173,6 +175,7 @@ void CMapWriter::PackTerrain(CFilePacker& packer, CTerrain* pTerrain)
void CMapWriter::WriteXML(const VfsPath& filename,
WaterManager* pWaterMan, SkyManager* pSkyMan,
CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,
+ CPostprocManager* pPostproc,
CSimulation2* pSimulation2)
{
XML_Start();
@@ -190,7 +193,6 @@ void CMapWriter::WriteXML(const VfsPath& filename,
{
XML_Element("Environment");
- XML_Setting("LightingModel", pLightEnv->GetLightingModel());
XML_Setting("SkySet", pSkyMan->GetSkySet());
{
XML_Element("SunColour");
@@ -218,6 +220,17 @@ void CMapWriter::WriteXML(const VfsPath& filename,
XML_Attribute("g", pLightEnv->m_UnitsAmbientColor.Y);
XML_Attribute("b", pLightEnv->m_UnitsAmbientColor.Z);
}
+ {
+ XML_Element("Fog");
+ XML_Setting("FogFactor", pLightEnv->m_FogFactor);
+ XML_Setting("FogThickness", pLightEnv->m_FogMax);
+ {
+ XML_Element("FogColour");
+ XML_Attribute("r", pLightEnv->m_FogColor.X);
+ XML_Attribute("g", pLightEnv->m_FogColor.Y);
+ XML_Attribute("b", pLightEnv->m_FogColor.Z);
+ }
+ }
{
XML_Element("Water");
@@ -251,6 +264,17 @@ void CMapWriter::WriteXML(const VfsPath& filename,
XML_Setting("ReflectionTintStrength", pWaterMan->m_ReflectionTintStrength);
}
}
+
+ {
+ XML_Element("Postproc");
+ {
+ XML_Setting("Brightness", pLightEnv->m_Brightness);
+ XML_Setting("Contrast", pLightEnv->m_Contrast);
+ XML_Setting("Saturation", pLightEnv->m_Saturation);
+ XML_Setting("Bloom", pLightEnv->m_Bloom);
+ XML_Setting("PostEffect", pPostproc->GetPostEffect());
+ }
+ }
}
{
diff --git a/source/graphics/MapWriter.h b/source/graphics/MapWriter.h
index 2317cfd12e..c66f390208 100644
--- a/source/graphics/MapWriter.h
+++ b/source/graphics/MapWriter.h
@@ -29,6 +29,7 @@ class CLightEnv;
class CTerrain;
class CCamera;
class CCinemaManager;
+class CPostprocManager;
class CTriggerManager;
class WaterManager;
class SkyManager;
@@ -46,7 +47,8 @@ public:
void SaveMap(const VfsPath& pathname, CTerrain* pTerr,
WaterManager* pWaterMan, SkyManager* pSkyMan,
CLightEnv* pLightEnv, CCamera* pCamera,
- CCinemaManager* pCinema, CSimulation2* pSimulation2);
+ CCinemaManager* pCinema, CPostprocManager* pPostproc,
+ CSimulation2* pSimulation2);
private:
// PackMap: pack the current world into a raw data stream
@@ -62,7 +64,8 @@ private:
// WriteXML: output some other data (entities, etc) in XML format
void WriteXML(const VfsPath& pathname, WaterManager* pWaterMan,
SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera,
- CCinemaManager* pCinema, CSimulation2* pSimulation2);
+ CCinemaManager* pCinema, CPostprocManager* pPostproc,
+ CSimulation2* pSimulation2);
// void WriteTriggerGroup(XMLWriter_File& xml_file_, const MapTriggerGroup& group,
// const std::list& groupList);
// void WriteTrigger(XMLWriter_File& xml_file_, const MapTrigger& trigger);
diff --git a/source/lib/external_libraries/glext_funcs.h b/source/lib/external_libraries/glext_funcs.h
index da147929f7..38c6721e9b 100644
--- a/source/lib/external_libraries/glext_funcs.h
+++ b/source/lib/external_libraries/glext_funcs.h
@@ -181,6 +181,8 @@ FUNC(void, glFramebufferTexture3DEXT, (GLenum target, GLenum attachment, GLenum
FUNC(void, glFramebufferRenderbufferEXT, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer))
FUNC(void, glGetFramebufferAttachmentParameterivEXT, (GLenum target, GLenum attachment, GLenum pname, GLint *params))
FUNC(void, glGenerateMipmapEXT, (GLenum target))
+FUNC(void, glBlitFramebufferEXT, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter))
+FUNC(void, glDrawBuffers, (GLsizei n, const GLenum *bufs))
// GL_ARB_vertex_program, GL_ARB_fragment_program
FUNC(void, glProgramStringARB, (GLenum target, GLenum format, GLsizei len, const GLvoid *string))
diff --git a/source/ps/World.cpp b/source/ps/World.cpp
index 5c9f95096c..4321f7484c 100644
--- a/source/ps/World.cpp
+++ b/source/ps/World.cpp
@@ -82,7 +82,8 @@ void CWorld::RegisterInit(const CStrW& mapFile, int playerID)
CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL,
&g_LightEnv, m_pGame->GetView(),
m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL,
- pTriggerManager, m_pGame->GetSimulation2(), &m_pGame->GetSimulation2()->GetSimContext(), playerID, false);
+ pTriggerManager, CRenderer::IsInitialised() ? &g_Renderer.GetPostprocManager() : NULL,
+ m_pGame->GetSimulation2(), &m_pGame->GetSimulation2()->GetSimContext(), playerID, false);
// fails immediately, or registers for delay loading
}
catch (PSERROR_File& err)
@@ -106,7 +107,8 @@ void CWorld::RegisterInitRMS(const CStrW& scriptFile, const CScriptValRooted& se
CRenderer::IsInitialised() ? g_Renderer.GetSkyManager() : NULL,
&g_LightEnv, m_pGame->GetView(),
m_pGame->GetView() ? m_pGame->GetView()->GetCinema() : NULL,
- pTriggerManager, m_pGame->GetSimulation2(), playerID);
+ pTriggerManager, CRenderer::IsInitialised() ? &g_Renderer.GetPostprocManager() : NULL,
+ m_pGame->GetSimulation2(), playerID);
// registers for delay loading
}
diff --git a/source/renderer/PostprocManager.cpp b/source/renderer/PostprocManager.cpp
new file mode 100644
index 0000000000..373ee35469
--- /dev/null
+++ b/source/renderer/PostprocManager.cpp
@@ -0,0 +1,488 @@
+/* Copyright (C) 2012 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 "lib/ogl.h"
+#include "maths/MathUtil.h"
+
+#include "gui/GUIutil.h"
+#include "lib/bits.h"
+#include "ps/CLogger.h"
+#include "ps/Filesystem.h"
+#include "ps/Game.h"
+#include "ps/World.h"
+
+#include "graphics/GameView.h"
+#include "graphics/LightEnv.h"
+#include "graphics/ShaderManager.h"
+
+#include "renderer/PostprocManager.h"
+#include "renderer/Renderer.h"
+
+
+CPostprocManager::CPostprocManager()
+ : m_IsInitialised(false), m_PingFbo(0), m_PongFbo(0), m_PostProcEffect(L"default"), m_ColourTex1(0), m_ColourTex2(0),
+ m_DepthTex(0), m_BloomFbo(0), m_BlurTex2a(0), m_BlurTex2b(0), m_BlurTex4a(0), m_BlurTex4b(0),
+ m_BlurTex8a(0), m_BlurTex8b(0), m_WhichBuffer(true)
+{
+}
+
+CPostprocManager::~CPostprocManager()
+{
+ Cleanup();
+}
+
+
+void CPostprocManager::Initialize()
+{
+ RecreateBuffers();
+ m_IsInitialised = true;
+ SetPostEffect(L"default");
+}
+
+void CPostprocManager::Cleanup()
+{
+ if (m_IsInitialised)
+ {
+ if (m_PingFbo) pglDeleteFramebuffersEXT(1, &m_PingFbo);
+ if (m_PongFbo) pglDeleteFramebuffersEXT(1, &m_PongFbo);
+ if (m_BloomFbo) pglDeleteFramebuffersEXT(1, &m_BloomFbo);
+ m_PingFbo = m_PongFbo = m_BloomFbo = 0;
+
+ if (m_ColourTex1) glDeleteTextures(1, &m_ColourTex1);
+ if (m_ColourTex2) glDeleteTextures(1, &m_ColourTex2);
+ if (m_DepthTex) glDeleteTextures(1, &m_DepthTex);
+ m_ColourTex1 = m_ColourTex2 = m_DepthTex = 0;
+
+ if (m_BlurTex2a) glDeleteTextures(1, &m_BlurTex2a);
+ if (m_BlurTex2b) glDeleteTextures(1, &m_BlurTex2b);
+ if (m_BlurTex4a) glDeleteTextures(1, &m_BlurTex4a);
+ if (m_BlurTex4b) glDeleteTextures(1, &m_BlurTex4b);
+ if (m_BlurTex8a) glDeleteTextures(1, &m_BlurTex8a);
+ if (m_BlurTex8b) glDeleteTextures(1, &m_BlurTex8b);
+ m_BlurTex2a = m_BlurTex2b = m_BlurTex4a = m_BlurTex4b = m_BlurTex8a = m_BlurTex8b = 0;
+ }
+}
+
+void CPostprocManager::RecreateBuffers()
+{
+ Cleanup();
+
+ m_Width = g_Renderer.GetWidth();
+ m_Height = g_Renderer.GetHeight();
+
+ #define GEN_BUFFER_RGBA(name, w, h) \
+ glGenTextures(1, (GLuint*)&name); \
+ glBindTexture(GL_TEXTURE_2D, name); \
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, \
+ 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);
+
+ // Two fullscreen ping-pong textures.
+ GEN_BUFFER_RGBA(m_ColourTex1, m_Width, m_Height);
+ GEN_BUFFER_RGBA(m_ColourTex2, m_Width, m_Height);
+
+ // Textures for several blur sizes. It would be possible to reuse
+ // m_BlurTex2b, thus avoiding the need for m_BlurTex4b and m_BlurTex8b, though given
+ // that these are fairly small it's probably not worth complicating the coordinates passed
+ // to the blur helper functions.
+ GEN_BUFFER_RGBA(m_BlurTex2a, m_Width / 2, m_Height / 2);
+ GEN_BUFFER_RGBA(m_BlurTex2b, m_Width / 2, m_Height / 2);
+
+ GEN_BUFFER_RGBA(m_BlurTex4a, m_Width / 4, m_Height / 4);
+ GEN_BUFFER_RGBA(m_BlurTex4b, m_Width / 4, m_Height / 4);
+
+ GEN_BUFFER_RGBA(m_BlurTex8a, m_Width / 8, m_Height / 8);
+ GEN_BUFFER_RGBA(m_BlurTex8b, m_Width / 8, m_Height / 8);
+
+ #undef GEN_BUFFER_RGBA
+
+ // Allocate the Depth/Stencil texture.
+ glGenTextures(1, (GLuint*)&m_DepthTex);
+ glBindTexture(GL_TEXTURE_2D, m_DepthTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_Width, m_Height,
+ 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
+ GL_NONE);
+ 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);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Set up the framebuffers with some initial textures.
+
+ pglGenFramebuffersEXT(1, &m_PingFbo);
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ColourTex1, 0);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthTex, 0);
+
+ GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ LOGWARNING(L"Framebuffer object incomplete (A): 0x%04X", status);
+ }
+
+ pglGenFramebuffersEXT(1, &m_PongFbo);
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ColourTex2, 0);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthTex, 0);
+
+ status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ LOGWARNING(L"Framebuffer object incomplete (B): 0x%04X", status);
+ }
+
+ pglGenFramebuffersEXT(1, &m_BloomFbo);
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_BloomFbo);
+ /*pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_BloomTex1, 0);
+
+ status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ LOGWARNING(L"Framebuffer object incomplete (B): 0x%04X", status);
+ }*/
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+}
+
+
+void CPostprocManager::ApplyBlurDownscale2x(GLuint inTex, GLuint outTex, int inWidth, int inHeight)
+{
+ // Bind inTex to framebuffer for rendering.
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_BloomFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, outTex, 0);
+
+ // Get bloom shader with instructions to simply copy texels.
+ CShaderDefines defines;
+ defines.Add("BLOOM_NOP", "1");
+ CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern("bloom"),
+ g_Renderer.GetSystemShaderDefines(), defines);
+
+ tech->BeginPass();
+ CShaderProgramPtr shader = tech->GetShader();
+
+ GLuint renderedTex = inTex;
+
+ // Cheat by creating high quality mipmaps for inTex, so the copying operation actually
+ // produces good scaling due to hardware filtering.
+ glBindTexture(GL_TEXTURE_2D, renderedTex);
+ pglGenerateMipmapEXT(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ shader->BindTexture("renderedTex", renderedTex);
+
+ glPushAttrib(GL_VIEWPORT_BIT);
+ glViewport(0, 0, inWidth / 2, inHeight / 2);
+
+ 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();
+ tech->EndPass();
+}
+
+void CPostprocManager::ApplyBlurGauss(GLuint inOutTex, GLuint tempTex, int inWidth, int inHeight)
+{
+ // Set tempTex as our rendering target.
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_BloomFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tempTex, 0);
+
+ // Get bloom shader, for a horizontal Gaussian blur pass.
+ CShaderDefines defines2;
+ defines2.Add("BLOOM_PASS_H", "1");
+ CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern("bloom"),
+ g_Renderer.GetSystemShaderDefines(), defines2);
+
+ tech->BeginPass();
+ CShaderProgramPtr shader = tech->GetShader();
+ shader->BindTexture("renderedTex", inOutTex);
+ shader->Uniform("texSize", inWidth, inHeight, 0.0f, 0.0f);
+
+ glPushAttrib(GL_VIEWPORT_BIT);
+ glViewport(0, 0, inWidth, inHeight);
+
+ 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();
+ tech->EndPass();
+
+ // Set result texture as our render target.
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_BloomFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, inOutTex, 0);
+
+ // Get bloom shader, for a vertical Gaussian blur pass.
+ CShaderDefines defines3;
+ defines3.Add("BLOOM_PASS_V", "1");
+ tech = g_Renderer.GetShaderManager().LoadEffect(CStrIntern("bloom"),
+ g_Renderer.GetSystemShaderDefines(), defines3);
+
+ tech->BeginPass();
+ shader = tech->GetShader();
+
+ // Our input texture to the shader is the output of the horizontal pass.
+ shader->BindTexture("renderedTex", tempTex);
+ shader->Uniform("texSize", inWidth, inHeight, 0.0f, 0.0f);
+
+ glPushAttrib(GL_VIEWPORT_BIT);
+ glViewport(0, 0, inWidth, inHeight);
+
+ 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();
+ tech->EndPass();
+}
+
+void CPostprocManager::ApplyBlur()
+{
+ glDisable(GL_BLEND);
+
+ GLint originalFBO;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &originalFBO);
+
+ int width = m_Width, height = m_Height;
+
+ #define SCALE_AND_BLUR(tex1, tex2, temptex) \
+ ApplyBlurDownscale2x(tex1, tex2, width, height); \
+ width /= 2; \
+ height /= 2; \
+ ApplyBlurGauss(tex2, temptex, width, height);
+
+ // We do the same thing for each scale, incrementally adding more and more blur.
+ SCALE_AND_BLUR(m_WhichBuffer ? m_ColourTex1 : m_ColourTex2, m_BlurTex2a, m_BlurTex2b);
+ SCALE_AND_BLUR(m_BlurTex2a, m_BlurTex4a, m_BlurTex4b);
+ SCALE_AND_BLUR(m_BlurTex4a, m_BlurTex8a, m_BlurTex8b);
+
+ #undef SCALE_AND_BLUR
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, originalFBO);
+}
+
+
+void CPostprocManager::CaptureRenderOutput()
+{
+ // clear both FBOs and leave m_PingFbo selected for rendering;
+ // m_WhichBuffer stays true at this point
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
+ pglDrawBuffers(1, buffers);
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ pglDrawBuffers(1, buffers);
+
+ m_WhichBuffer = true;
+}
+
+
+void CPostprocManager::ReleaseRenderOutput()
+{
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ // we blit to screen from the previous active buffer
+ if (m_WhichBuffer)
+ pglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_PingFbo);
+ else
+ pglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_PongFbo);
+
+ pglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+ pglBlitFramebufferEXT(0, 0, m_Width, m_Height, 0, 0, m_Width, m_Height,
+ GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
+ pglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+}
+
+
+void CPostprocManager::LoadEffect(CStrW &name)
+{
+ if (!m_IsInitialised)
+ return;
+
+ if (name != L"default")
+ {
+ CStrW n = L"postproc/" + name;
+ m_PostProcTech = g_Renderer.GetShaderManager().LoadEffect(n.ToUTF8().c_str());
+ }
+
+ m_PostProcEffect = name;
+}
+
+
+void CPostprocManager::ApplyEffect(CShaderTechniquePtr &shaderTech1, int pass)
+{
+ // select the other FBO for rendering
+ if (!m_WhichBuffer)
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo);
+ else
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
+
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(GL_FALSE);
+
+ shaderTech1->BeginPass(pass);
+ CShaderProgramPtr shader = shaderTech1->GetShader(pass);
+
+ shader->Bind();
+
+ // Use the textures from the current FBO as input to the shader.
+ // We also bind a bunch of other textures and parameters, but since
+ // this only happens once per frame the overhead is negligible.
+ if (m_WhichBuffer)
+ shader->BindTexture("renderedTex", m_ColourTex1);
+ else
+ shader->BindTexture("renderedTex", m_ColourTex2);
+
+ shader->BindTexture("depthTex", m_DepthTex);
+
+ shader->BindTexture("blurTex2", m_BlurTex2a);
+ shader->BindTexture("blurTex4", m_BlurTex4a);
+ shader->BindTexture("blurTex8", m_BlurTex8a);
+
+ shader->Uniform("width", m_Width);
+ shader->Uniform("height", m_Height);
+ shader->Uniform("zNear", g_Game->GetView()->GetNear());
+ shader->Uniform("zFar", g_Game->GetView()->GetFar());
+
+ shader->Uniform("brightness", g_LightEnv.m_Brightness);
+ shader->Uniform("hdr", g_LightEnv.m_Contrast);
+ shader->Uniform("saturation", g_LightEnv.m_Saturation);
+ shader->Uniform("bloom", g_LightEnv.m_Bloom);
+
+ 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();
+
+ shader->Unbind();
+
+ shaderTech1->EndPass(pass);
+
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+
+ m_WhichBuffer = !m_WhichBuffer;
+}
+
+void CPostprocManager::ApplyPostproc()
+{
+ if (!m_IsInitialised)
+ return;
+
+ // Don't do anything if we are using the default effect.
+ if (m_PostProcEffect == L"default")
+ {
+ return;
+ }
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0);
+
+ GLenum buffers[] = { GL_COLOR_ATTACHMENT0 };
+ pglDrawBuffers(1, buffers);
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0);
+ pglDrawBuffers(1, buffers);
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+
+ // First render blur textures. Note that this only happens ONLY ONCE, before any effects are applied!
+ // (This may need to change depending on future usage, however that will have a fps hit)
+ ApplyBlur();
+
+ for (int pass = 0; pass < m_PostProcTech->GetNumPasses(); ++pass)
+ {
+ ApplyEffect(m_PostProcTech, pass);
+ }
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PongFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthTex, 0);
+
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_PingFbo);
+ pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthTex, 0);
+}
+
+
+// Generate list of available effect-sets
+std::vector CPostprocManager::GetPostEffects() const
+{
+ std::vector effects;
+
+ const VfsPath path(L"shaders/effects/postproc/");
+
+ VfsPaths pathnames;
+ if(vfs::GetPathnames(g_VFS, path, 0, pathnames) < 0)
+ {
+ LOGERROR(L"Error finding Post effects in '%ls'", path.string().c_str());
+ }
+
+ for(size_t i = 0; i < pathnames.size(); i++)
+ {
+ if (pathnames[i].Extension() != L".xml")
+ continue;
+
+ effects.push_back(pathnames[i].Basename().string());
+ }
+
+ // Add the default "null" effect to the list.
+ effects.push_back(L"default");
+
+ sort(effects.begin(), effects.end());
+
+ return effects;
+}
+
+void CPostprocManager::SetPostEffect(CStrW name)
+{
+ LoadEffect(name);
+}
diff --git a/source/renderer/PostprocManager.h b/source/renderer/PostprocManager.h
new file mode 100644
index 0000000000..3c3d090ad8
--- /dev/null
+++ b/source/renderer/PostprocManager.h
@@ -0,0 +1,115 @@
+/* Copyright (C) 2012 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 .
+ */
+
+#ifndef INCLUDED_POSTPROCMANAGER
+#define INCLUDED_POSTPROCMANAGER
+
+
+#include "graphics/ShaderTechnique.h"
+
+class CPostprocManager
+{
+private:
+
+ // Two framebuffers, that we flip between at each shader pass.
+ GLuint m_PingFbo, m_PongFbo;
+
+ // Unique colour textures for the framebuffers.
+ GLuint m_ColourTex1, m_ColourTex2;
+
+ // The framebuffers share a depth/stencil texture.
+ GLuint m_DepthTex;
+
+ // A framebuffer and textures x2 for each blur level we render.
+ GLuint m_BloomFbo, m_BlurTex2a, m_BlurTex2b, m_BlurTex4a, m_BlurTex4b, m_BlurTex8a, m_BlurTex8b;
+
+ // Indicates which of the ping-pong buffers is used for reading and which for drawing.
+ bool m_WhichBuffer;
+
+ // The name and shader technique we are using. "default" name means no technique is used
+ // (i.e. while we do allocate the buffers, no effects are rendered).
+ CStrW m_PostProcEffect;
+ CShaderTechniquePtr m_PostProcTech;
+
+ // The current screen dimensions in pixels.
+ int m_Width, m_Height;
+
+ // Is the postproc manager initialised? Buffers created? Default effect loaded?
+ bool m_IsInitialised;
+
+ // Creates blur textures at various scales, for bloom, DOF, etc.
+ void ApplyBlur();
+
+ // High quality GPU image scaling to half size. outTex must have exactly half the size
+ // of inTex. inWidth and inHeight are the dimensions of inTex in texels.
+ void ApplyBlurDownscale2x(GLuint inTex, GLuint outTex, int inWidth, int inHeight);
+
+ // GPU-based Gaussian blur in two passes. inOutTex contains the input image and will be filled
+ // with the blurred image. tempTex must have the same size as inOutTex.
+ // inWidth and inHeight are the dimensions of the images in texels.
+ void ApplyBlurGauss(GLuint inOutTex, GLuint tempTex, int inWidth, int inHeight);
+
+ // Applies a pass of a given effect to the entire current framebuffer. The shader is
+ // provided with a number of general-purpose variables, including the rendered screen so far,
+ // the depth buffer, a number of blur textures, the screen size, the zNear/zFar planes and
+ // some other parameters used by the optional bloom/HDR pass.
+ void ApplyEffect(CShaderTechniquePtr &shaderTech1, int pass);
+
+public:
+ CPostprocManager();
+ ~CPostprocManager();
+
+ // Create all buffers/textures in GPU memory and set default effect.
+ void Initialize();
+
+ // Delete all allocated buffers/textures from GPU memory.
+ void Cleanup();
+
+ // Delete existing buffers/textures and create them again, using a new screen size if needed.
+ // (the textures are also attached to the framebuffers)
+ void RecreateBuffers();
+
+ // Loads a new postproc effect. "default" effect does NOT load anything.
+ void LoadEffect(CStrW &name);
+
+ // Returns a list of xml files found in shaders/effects/postproc.
+ std::vector GetPostEffects() const;
+
+ // Returns the name of the current effect.
+ inline const CStrW& GetPostEffect() const
+ {
+ return m_PostProcEffect;
+ }
+
+ // Matching setter that calls LoadEffect.
+ void SetPostEffect(CStrW name);
+
+ // Clears the two colour buffers and depth buffer, and redirects all rendering
+ // to our textures instead of directly to the system framebuffer.
+ void CaptureRenderOutput();
+
+ // First renders blur textures, then calls ApplyEffect for each effect pass,
+ // ping-ponging the buffers at each step.
+ void ApplyPostproc();
+
+ // Blits the final postprocessed texture to the system framebuffer. The system framebuffer
+ // is selected as the output buffer. Should be called before silhouette rendering.
+ void ReleaseRenderOutput();
+};
+
+
+#endif //INCLUDED_POSTPROCMANAGER
diff --git a/source/renderer/RenderModifiers.cpp b/source/renderer/RenderModifiers.cpp
index bd2ed99045..1b299b0d9e 100644
--- a/source/renderer/RenderModifiers.cpp
+++ b/source/renderer/RenderModifiers.cpp
@@ -90,6 +90,9 @@ void ShaderRenderModifier::BeginPass(const CShaderProgramPtr& shader)
shader->Uniform("ambient", GetLightEnv()->m_UnitsAmbientColor);
shader->Uniform("sunDir", GetLightEnv()->GetSunDir());
shader->Uniform("sunColor", GetLightEnv()->m_SunColor);
+
+ shader->Uniform("fogColor", GetLightEnv()->m_FogColor);
+ shader->Uniform("fogParams", GetLightEnv()->m_FogFactor, GetLightEnv()->m_FogMax, 0.f, 0.f);
}
if (shader->GetTextureBinding("losTex").Active())
diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp
index db8858b747..dc487fe3e9 100644
--- a/source/renderer/Renderer.cpp
+++ b/source/renderer/Renderer.cpp
@@ -276,6 +276,9 @@ public:
/// Shadow map
ShadowMap shadow;
+
+ /// Postprocessing effect manager
+ CPostprocManager postprocManager;
/// Various model renderers
struct Models
@@ -437,6 +440,7 @@ CRenderer::CRenderer()
m_Options.m_GPUSkinning = false;
m_Options.m_GenTangents = false;
m_Options.m_SmoothLOS = false;
+ m_Options.m_Postproc = false;
m_Options.m_ShowSky = false;
// TODO: be more consistent in use of the config system
@@ -445,6 +449,7 @@ CRenderer::CRenderer()
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);
+ CFG_GET_USER_VAL("postproc", Bool, m_Options.m_Postproc);
#if CONFIG2_GLES
// Override config option since GLES only supports GLSL
@@ -642,6 +647,9 @@ bool CRenderer::Open(int width, int height)
// Let component renderers perform one-time initialization after graphics capabilities and
// the shader path have been determined.
m->overlayRenderer.Initialize();
+
+ if (m_Options.m_Postproc)
+ m->postprocManager.Initialize();
return true;
}
@@ -654,6 +662,9 @@ void CRenderer::Resize(int width,int height)
m_Width = width;
m_Height = height;
+
+ if (m_Options.m_Postproc)
+ m->postprocManager.RecreateBuffers();
}
//////////////////////////////////////////////////////////////////////////////////////////
@@ -1376,6 +1387,9 @@ void CRenderer::RenderSubmissions()
PROFILE3("render submissions");
GetScene().GetLOSTexture().InterpolateLOS();
+
+ if (m_Options.m_Postproc)
+ m->postprocManager.CaptureRenderOutput();
CShaderDefines context = m->globalContext;
@@ -1495,6 +1509,12 @@ void CRenderer::RenderSubmissions()
RenderParticles();
ogl_WarnIfError();
}
+
+ if (m_Options.m_Postproc)
+ {
+ m->postprocManager.ApplyPostproc();
+ m->postprocManager.ReleaseRenderOutput();
+ }
if (m_Options.m_Silhouettes)
{
@@ -1519,6 +1539,7 @@ void CRenderer::RenderSubmissions()
// render overlays that should appear on top of all other objects
m->overlayRenderer.RenderForegroundOverlays(m_ViewCamera);
ogl_WarnIfError();
+
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2035,3 +2056,8 @@ CMaterialManager& CRenderer::GetMaterialManager()
{
return m->materialManager;
}
+
+CPostprocManager& CRenderer::GetPostprocManager()
+{
+ return m->postprocManager;
+}
\ No newline at end of file
diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h
index 4119594e07..ebb15f7600 100644
--- a/source/renderer/Renderer.h
+++ b/source/renderer/Renderer.h
@@ -30,6 +30,7 @@
#include "ps/Singleton.h"
#include "scripting/ScriptableObject.h"
+#include "renderer/PostprocManager.h"
#include "renderer/Scene.h"
#include "renderer/TimeManager.h"
@@ -135,6 +136,7 @@ public:
bool m_GenTangents;
bool m_SmoothLOS;
bool m_ShowSky;
+ bool m_Postproc;
} m_Options;
struct Caps {
@@ -291,6 +293,8 @@ public:
CShaderDefines GetSystemShaderDefines();
CTimeManager& GetTimeManager();
+
+ CPostprocManager& GetPostprocManager();
/**
* GetCapabilities: Return which OpenGL capabilities are available and enabled.
diff --git a/source/renderer/ShadowMap.cpp b/source/renderer/ShadowMap.cpp
index 8139073c8a..6c39851c48 100644
--- a/source/renderer/ShadowMap.cpp
+++ b/source/renderer/ShadowMap.cpp
@@ -83,6 +83,9 @@ struct ShadowMapInternals
// Copy of renderer's standard view camera, saved between
// BeginRender and EndRender while we replace it with the shadow camera
CCamera SavedViewCamera;
+
+ // Save the caller's FBO so it can be restored
+ GLint SavedViewFBO;
// Helper functions
void CalcShadowMatrices();
@@ -318,6 +321,9 @@ void ShadowMapInternals::CreateTexture()
pglDeleteFramebuffersEXT(1, &Framebuffer);
Framebuffer = 0;
}
+
+ // save the caller's FBO
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &SavedViewFBO);
pglGenFramebuffersEXT(1, &Framebuffer);
@@ -433,7 +439,7 @@ void ShadowMapInternals::CreateTexture()
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
- pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, SavedViewFBO);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
@@ -450,6 +456,9 @@ void ShadowMapInternals::CreateTexture()
void ShadowMap::BeginRender()
{
// HACK HACK: this depends in non-obvious ways on the behaviour of the caller
+
+ // save caller's FBO
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m->SavedViewFBO);
// Calc remaining shadow matrices
m->CalcShadowMatrices();
@@ -502,7 +511,7 @@ void ShadowMap::EndRender()
{
PROFILE("unbind framebuffer");
- pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->SavedViewFBO);
}
glViewport(0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight());
diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp
index 00f8978cdf..6b35aa76cd 100644
--- a/source/renderer/TerrainRenderer.cpp
+++ b/source/renderer/TerrainRenderer.cpp
@@ -487,6 +487,9 @@ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap*
shader->Uniform("ambient", lightEnv.m_TerrainAmbientColor);
shader->Uniform("sunColor", lightEnv.m_SunColor);
+
+ shader->Uniform("fogColor", lightEnv.m_FogColor);
+ shader->Uniform("fogParams", lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);
}
void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, ShadowMap* shadow, bool filtered)
@@ -702,6 +705,8 @@ bool TerrainRenderer::RenderFancyWater()
m->fancyWaterShader->Uniform("refractionMatrix", WaterMgr->m_RefractionMatrix);
m->fancyWaterShader->Uniform("losMatrix", losTexture.GetTextureMatrix());
m->fancyWaterShader->Uniform("cameraPos", camPos);
+ m->fancyWaterShader->Uniform("fogColor", lightEnv.m_FogColor);
+ m->fancyWaterShader->Uniform("fogParams", lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
diff --git a/source/simulation2/Simulation2.cpp b/source/simulation2/Simulation2.cpp
index 0021e59266..6a99b8a42b 100644
--- a/source/simulation2/Simulation2.cpp
+++ b/source/simulation2/Simulation2.cpp
@@ -385,7 +385,7 @@ void CSimulation2Impl::Update(int turnLength, const std::vectorLoadMap(mapfilename, &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure
+ NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure
}
else
{
diff --git a/source/simulation2/components/tests/test_Pathfinder.h b/source/simulation2/components/tests/test_Pathfinder.h
index 4a6c7aa089..92677617c5 100644
--- a/source/simulation2/components/tests/test_Pathfinder.h
+++ b/source/simulation2/components/tests/test_Pathfinder.h
@@ -68,7 +68,7 @@ public:
CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself
LDR_BeginRegistering();
- mapReader->LoadMap(L"maps/scenarios/Median Oasis.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL,
+ mapReader->LoadMap(L"maps/scenarios/Median Oasis.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&sim2, &sim2.GetSimContext(), -1, false);
LDR_EndRegistering();
TS_ASSERT_OK(LDR_NonprogressiveLoad());
diff --git a/source/simulation2/tests/test_Serializer.h b/source/simulation2/tests/test_Serializer.h
index 6601472a72..c6ebb1cc17 100644
--- a/source/simulation2/tests/test_Serializer.h
+++ b/source/simulation2/tests/test_Serializer.h
@@ -506,7 +506,7 @@ public:
CMapReader* mapReader = new CMapReader(); // it'll call "delete this" itself
LDR_BeginRegistering();
- mapReader->LoadMap(L"maps/scenarios/Latium.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL,
+ mapReader->LoadMap(L"maps/scenarios/Latium.pmp", &terrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&sim2, &sim2.GetSimContext(), -1, false);
LDR_EndRegistering();
TS_ASSERT_OK(LDR_NonprogressiveLoad());
diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
index b91bdb300e..6583cc3832 100644
--- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
+++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
@@ -227,13 +227,29 @@ EnvironmentSidebar::EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow*
sunSizer->Add(new VariableSliderBox(this, _("Sun elevation"), g_EnvironmentSettings.sunelevation, -M_PIf/2, M_PIf/2), wxSizerFlags().Expand());
sunSizer->Add(new VariableSliderBox(this, _("Sun overbrightness"), g_EnvironmentSettings.sunoverbrightness, 1.0f, 3.0f), wxSizerFlags().Expand());
- sunSizer->Add(m_LightingModelList = new VariableListBox(this, _("Light model"), g_EnvironmentSettings.lightingmodel), wxSizerFlags().Expand());
+ sunSizer->Add(m_PostEffectList = new VariableListBox(this, _("Post Effect"), g_EnvironmentSettings.posteffect), wxSizerFlags().Expand());
+
+ wxSizer* fogSizer = new wxGridSizer(2);
+ m_MainSizer->Add(fogSizer, wxSizerFlags().Expand().Border(wxTOP, 8));
+
+ fogSizer->Add(new VariableSliderBox(this, _("Fog Factor"), g_EnvironmentSettings.fogfactor, 0.0, 0.01), wxSizerFlags().Expand());
+ fogSizer->Add(new VariableSliderBox(this, _("Fog Thickness"), g_EnvironmentSettings.fogmax, 0.5, 0.0), wxSizerFlags().Expand());
m_MainSizer->Add(new LightControl(this, wxSize(150, 150), g_EnvironmentSettings));
m_MainSizer->Add(m_SkyList = new VariableListBox(this, _("Sky set"), g_EnvironmentSettings.skyset), wxSizerFlags().Expand());
+
+ wxSizer* SSSizer = new wxGridSizer(2);
+ m_MainSizer->Add(SSSizer, wxSizerFlags().Expand().Border(wxTOP, 8));
+
+ SSSizer->Add(new VariableSliderBox(this, _("Brightness"), g_EnvironmentSettings.brightness, -0.5, 0.5), wxSizerFlags().Expand());
+ SSSizer->Add(new VariableSliderBox(this, _("Contrast (HDR)"), g_EnvironmentSettings.contrast, 0.5, 1.5), wxSizerFlags().Expand());
+ SSSizer->Add(new VariableSliderBox(this, _("Saturation"), g_EnvironmentSettings.saturation, 0.0, 1.0), wxSizerFlags().Expand());
+ SSSizer->Add(new VariableSliderBox(this, _("Bloom"), g_EnvironmentSettings.bloom, 0.2, 0.0), wxSizerFlags().Expand());
+
m_MainSizer->Add(new VariableColourBox(this, _("Sun colour"), g_EnvironmentSettings.suncolour), wxSizerFlags().Expand());
m_MainSizer->Add(new VariableColourBox(this, _("Terrain ambient colour"), g_EnvironmentSettings.terraincolour), wxSizerFlags().Expand());
m_MainSizer->Add(new VariableColourBox(this, _("Object ambient colour"), g_EnvironmentSettings.unitcolour), wxSizerFlags().Expand());
+ m_MainSizer->Add(new VariableColourBox(this, _("Fog colour"), g_EnvironmentSettings.fogcolour), wxSizerFlags().Expand());
m_Conn = g_EnvironmentSettings.RegisterObserver(0, &SendToGame);
}
@@ -245,17 +261,15 @@ void EnvironmentSidebar::OnFirstDisplay()
AtlasMessage::qGetSkySets qry_skysets;
qry_skysets.Post();
m_SkyList->SetChoices(*qry_skysets.skysets);
+
+ AtlasMessage::qGetPostEffects qry_effects;
+ qry_effects.Post();
+ m_PostEffectList->SetChoices(*qry_effects.posteffects);
AtlasMessage::qGetEnvironmentSettings qry_env;
qry_env.Post();
g_EnvironmentSettings = qry_env.settings;
-
- std::vector lightingModels;
- lightingModels.push_back(L"standard");
- m_LightingModelList->SetChoices(lightingModels);
-
-
g_EnvironmentSettings.NotifyObservers();
}
diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h
index f51a4babd3..b53bd80751 100644
--- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h
+++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h
@@ -32,7 +32,7 @@ protected:
virtual void OnFirstDisplay();
private:
- VariableListBox* m_LightingModelList;
+ VariableListBox* m_PostEffectList;
VariableListBox* m_SkyList;
ObservableScopedConnection m_Conn;
};
diff --git a/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
index d302e0d940..68faa9c317 100644
--- a/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
+++ b/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
@@ -61,10 +61,18 @@ sEnvironmentSettings GetSettings()
sunrotation -= (float)M_PI*2;
s.sunrotation = sunrotation;
s.sunelevation = g_LightEnv.GetElevation();
-
- s.lightingmodel = CStr(g_LightEnv.GetLightingModel()).FromUTF8();
+
+ s.posteffect = g_Renderer.GetPostprocManager().GetPostEffect();
s.skyset = g_Renderer.GetSkyManager()->GetSkySet();
+
+ s.fogfactor = g_LightEnv.m_FogFactor;
+ s.fogmax = g_LightEnv.m_FogMax;
+
+ s.brightness = g_LightEnv.m_Brightness;
+ s.contrast = g_LightEnv.m_Contrast;
+ s.saturation = g_LightEnv.m_Saturation;
+ s.bloom = g_LightEnv.m_Bloom;
// RGBColor (CVector3D) colours
#define COLOUR(A, B) A = Colour((int)(B.X*255), (int)(B.Y*255), (int)(B.Z*255))
@@ -78,6 +86,7 @@ sEnvironmentSettings GetSettings()
COLOUR(s.suncolour, g_LightEnv.m_SunColor);
COLOUR(s.terraincolour, g_LightEnv.m_TerrainAmbientColor);
COLOUR(s.unitcolour, g_LightEnv.m_UnitsAmbientColor);
+ COLOUR(s.fogcolour, g_LightEnv.m_FogColor);
#undef COLOUR
return s;
@@ -104,19 +113,31 @@ void SetSettings(const sEnvironmentSettings& s)
g_LightEnv.SetRotation(s.sunrotation);
g_LightEnv.SetElevation(s.sunelevation);
-
- g_LightEnv.SetLightingModel(CStrW(*s.lightingmodel).ToUTF8());
+
+ CStrW posteffect = *s.posteffect;
+ if (posteffect.length() == 0)
+ posteffect = L"default";
+ g_Renderer.GetPostprocManager().SetPostEffect(posteffect);
CStrW skySet = *s.skyset;
if (skySet.length() == 0)
skySet = L"default";
g_Renderer.GetSkyManager()->SetSkySet(skySet);
+
+ g_LightEnv.m_FogFactor = s.fogfactor;
+ g_LightEnv.m_FogMax = s.fogmax;
+
+ g_LightEnv.m_Brightness = s.brightness;
+ g_LightEnv.m_Contrast = s.contrast;
+ g_LightEnv.m_Saturation = s.saturation;
+ g_LightEnv.m_Bloom = s.bloom;
#define COLOUR(A, B) B = RGBColor(A->r/255.f, A->g/255.f, A->b/255.f)
COLOUR(s.suncolour, g_LightEnv.m_SunColor);
g_LightEnv.m_SunColor *= s.sunoverbrightness;
COLOUR(s.terraincolour, g_LightEnv.m_TerrainAmbientColor);
COLOUR(s.unitcolour, g_LightEnv.m_UnitsAmbientColor);
+ COLOUR(s.fogcolour, g_LightEnv.m_FogColor);
#undef COLOUR
}
@@ -159,4 +180,11 @@ QUERYHANDLER(GetSkySets)
msg->skysets = std::vector(skies.begin(), skies.end());
}
+
+QUERYHANDLER(GetPostEffects)
+{
+ std::vector effects = g_Renderer.GetPostprocManager().GetPostEffects();
+ msg->posteffects = std::vector(effects.begin(), effects.end());
+}
+
}
diff --git a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp
index 25978920b3..b4fd27d951 100644
--- a/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp
+++ b/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp
@@ -231,6 +231,7 @@ MESSAGEHANDLER(SaveMap)
g_Game->GetWorld()->GetTerrain(),
g_Renderer.GetWaterManager(), g_Renderer.GetSkyManager(),
&g_LightEnv, g_Game->GetView()->GetCamera(), g_Game->GetView()->GetCinema(),
+ &g_Renderer.GetPostprocManager(),
g_Game->GetSimulation2());
}
diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h
index 7a3d4c8239..75892f459e 100644
--- a/source/tools/atlas/GameInterface/Messages.h
+++ b/source/tools/atlas/GameInterface/Messages.h
@@ -432,13 +432,22 @@ struct sEnvironmentSettings
// support different lighting models ("old" for the version compatible with old scenarios,
// "standard" for the new normal model that supports much brighter lighting)
- Shareable lightingmodel;
+ Shareable posteffect;
Shareable skyset;
Shareable suncolour;
Shareable terraincolour;
Shareable unitcolour;
+ Shareable fogcolour;
+
+ Shareable fogfactor;
+ Shareable fogmax;
+
+ Shareable brightness;
+ Shareable contrast;
+ Shareable saturation;
+ Shareable bloom;
};
SHAREABLE_STRUCT(sEnvironmentSettings);
#endif
@@ -459,6 +468,12 @@ QUERY(GetSkySets,
((std::vector, skysets))
);
+QUERY(GetPostEffects,
+ // no inputs
+ ,
+ ((std::vector, posteffects))
+ );
+
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////