From 1fb7889539c2d199dfecddf308782b738d28653c Mon Sep 17 00:00:00 2001 From: myconid Date: Mon, 15 Oct 2012 10:34:23 +0000 Subject: [PATCH] Postproc manager This was SVN commit r12755. --- binaries/data/config/default.cfg | 3 + .../maps/scenarios/Campaign Test Map.xml | 4 +- .../maps/scenarios/Zagros Mountains 01.xml | 4 +- .../mods/public/shaders/effects/bloom.xml | 9 + .../shaders/effects/model_transparent.xml | 6 +- .../public/shaders/effects/postproc/DOF.xml | 12 + .../public/shaders/effects/postproc/hdr.xml | 9 + .../data/mods/public/shaders/glsl/bloom.fs | 107 ++++ .../data/mods/public/shaders/glsl/bloom.xml | 11 + binaries/data/mods/public/shaders/glsl/dof.fs | 63 +++ .../data/mods/public/shaders/glsl/dof.xml | 13 + binaries/data/mods/public/shaders/glsl/hdr.fs | 50 ++ binaries/data/mods/public/shaders/glsl/hdr.vs | 10 + .../data/mods/public/shaders/glsl/hdr.xml | 13 + .../mods/public/shaders/glsl/model_common.fs | 21 + .../public/shaders/glsl/terrain_common.fs | 20 + .../mods/public/shaders/glsl/water_high.fs | 25 +- source/graphics/LightEnv.cpp | 6 +- source/graphics/LightEnv.h | 15 +- source/graphics/MapReader.cpp | 81 ++- source/graphics/MapReader.h | 8 +- source/graphics/MapWriter.cpp | 28 +- source/graphics/MapWriter.h | 7 +- source/lib/external_libraries/glext_funcs.h | 2 + source/ps/World.cpp | 6 +- source/renderer/PostprocManager.cpp | 488 ++++++++++++++++++ source/renderer/PostprocManager.h | 115 +++++ source/renderer/RenderModifiers.cpp | 3 + source/renderer/Renderer.cpp | 26 + source/renderer/Renderer.h | 4 + source/renderer/ShadowMap.cpp | 13 +- source/renderer/TerrainRenderer.cpp | 5 + source/simulation2/Simulation2.cpp | 2 +- .../components/tests/test_Pathfinder.h | 2 +- source/simulation2/tests/test_Serializer.h | 2 +- .../Sections/Environment/Environment.cpp | 28 +- .../Sections/Environment/Environment.h | 2 +- .../Handlers/EnvironmentHandlers.cpp | 36 +- .../GameInterface/Handlers/MapHandlers.cpp | 1 + source/tools/atlas/GameInterface/Messages.h | 17 +- 40 files changed, 1235 insertions(+), 42 deletions(-) create mode 100644 binaries/data/mods/public/shaders/effects/bloom.xml create mode 100644 binaries/data/mods/public/shaders/effects/postproc/DOF.xml create mode 100644 binaries/data/mods/public/shaders/effects/postproc/hdr.xml create mode 100644 binaries/data/mods/public/shaders/glsl/bloom.fs create mode 100644 binaries/data/mods/public/shaders/glsl/bloom.xml create mode 100644 binaries/data/mods/public/shaders/glsl/dof.fs create mode 100644 binaries/data/mods/public/shaders/glsl/dof.xml create mode 100644 binaries/data/mods/public/shaders/glsl/hdr.fs create mode 100644 binaries/data/mods/public/shaders/glsl/hdr.vs create mode 100644 binaries/data/mods/public/shaders/glsl/hdr.xml create mode 100644 source/renderer/PostprocManager.cpp create mode 100644 source/renderer/PostprocManager.h 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)) + ); + ////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////