diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index 54e8a3210b..c95b791f5c 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -41,8 +41,15 @@ bpp = 0 ; System settings: -fancywater = true -superfancywater = false +waternormals = true +waterbinormals = true +waterrealdepth = true +waterfoam = false +watercoastalwaves = false +waterrefraction = true +waterreflection = true +watershadows = false + shadows = true shadowpcf = true vsync = false diff --git a/binaries/data/mods/public/gui/options/options.xml b/binaries/data/mods/public/gui/options/options.xml index 08b2086d3e..6c2622b798 100644 --- a/binaries/data/mods/public/gui/options/options.xml +++ b/binaries/data/mods/public/gui/options/options.xml @@ -23,7 +23,7 @@ Game Options - + Enable Shadows @@ -40,15 +40,15 @@ - Enable Water Reflections + - Enable Music - + Enable Music + if (this.checked) startMusic(); else stopMusic(); diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml index 9260c12f6b..7dab4454ed 100644 --- a/binaries/data/mods/public/gui/session/session.xml +++ b/binaries/data/mods/public/gui/session/session.xml @@ -300,7 +300,7 @@ - - Enable Water Reflections - - if (renderer.fancyWater) this.checked = true; else this.checked = false; - renderer.fancyWater = this.checked; + + Water - 3D Waviness + + if (renderer.waternormal) this.checked = true; else this.checked = false; + renderer.waternormal = this.checked; - - Enable Advanced Water - - if (renderer.superFancyWater) this.checked = true; else this.checked = false; - renderer.superFancyWater = this.checked; + + Water - HQ Waviness + + if (renderer.waterbinormal) this.checked = true; else this.checked = false; + renderer.waterbinormal = this.checked; + + + + Water - Use Actual Depth + + if (renderer.waterrealdepth) this.checked = true; else this.checked = false; + renderer.waterrealdepth = this.checked; + + + + Water - Enable Reflections + + if (renderer.waterreflection) this.checked = true; else this.checked = false; + renderer.waterreflection = this.checked; + + Water - Enable Refraction + + if (renderer.waterrefraction) this.checked = true; else this.checked = false; + renderer.waterrefraction = this.checked; + + + + Water - Enable Shore Foam + + if (renderer.waterfoam) this.checked = true; else this.checked = false; + renderer.waterfoam = this.checked; + + + + Water - Enable Shore Waves + + if (renderer.watercoastalwaves) this.checked = true; else this.checked = false; + renderer.watercoastalwaves = this.checked; + + + + Water - Use Surface Shadows + + if (renderer.watershadow) this.checked = true; else this.checked = false; + renderer.watershadow = this.checked; + + - Enable Particles - + Enable Particles + if (renderer.particles) this.checked = true; else this.checked = false; renderer.particles = this.checked; - Enable Unit Silhouettes - + Enable Unit Silhouettes + if (renderer.silhouettes) this.checked = true; else this.checked = false; renderer.silhouettes = this.checked; - Enable Music - + Enable Music + if (this.checked) global.music.start(); else global.music.stop(); - Developer Overlay - + Developer Overlay + toggleDeveloperOverlay(); diff --git a/binaries/data/mods/public/shaders/glsl/water_high.fs b/binaries/data/mods/public/shaders/glsl/water_high.fs index 447253f1c0..7b31747e31 100644 --- a/binaries/data/mods/public/shaders/glsl/water_high.fs +++ b/binaries/data/mods/public/shaders/glsl/water_high.fs @@ -4,9 +4,6 @@ uniform vec3 ambient; uniform vec3 sunDir; uniform vec3 sunColor; uniform vec3 cameraPos; -uniform sampler2D normalMap; -uniform sampler2D reflectionMap; -uniform sampler2D refractionMap; uniform sampler2D losMap; uniform float shininess; // Blinn-Phong specular strength uniform float specularStrength; // Scaling for specular reflection (specular color is (this,this,this)) @@ -23,57 +20,64 @@ uniform vec2 fogParams; uniform vec2 screenSize; uniform float time; -#if USE_SUPERFANCYWATER - uniform sampler2D heightmap; - uniform sampler2D infoTex; - uniform sampler2D normalMap2; - uniform sampler2D Foam; - uniform sampler2D depthTex; - uniform sampler2D waveTex; - - #if USE_SHADOW - varying vec4 v_shadow; - #if USE_SHADOW_SAMPLER - uniform sampler2DShadow shadowTex; - #if USE_SHADOW_PCF - uniform vec4 shadowScale; - #endif - #else - uniform sampler2D shadowTex; - #endif - #endif -#endif - - varying vec3 worldPos; varying float waterDepth; -#if USE_SUPERFANCYWATER -float get_shadow(vec4 coords) -{ - #if USE_SHADOW && !DISABLE_RECEIVE_SHADOWS - #if USE_SHADOW_SAMPLER - #if USE_SHADOW_PCF - vec2 offset = fract(coords.xy - 0.5); - vec4 size = vec4(offset + 1.0, 2.0 - offset); - vec4 weight = (vec4(2.0 - 1.0 / size.xy, 1.0 / size.zw - 1.0) + (coords.xy - offset).xyxy) * shadowScale.zwzw; - return (1.0/9.0)*dot(size.zxzx*size.wwyy, - vec4(shadow2D(shadowTex, vec3(weight.zw, coords.z)).r, - shadow2D(shadowTex, vec3(weight.xw, coords.z)).r, - shadow2D(shadowTex, vec3(weight.zy, coords.z)).r, - shadow2D(shadowTex, vec3(weight.xy, coords.z)).r)); - #else - return shadow2D(shadowTex, coords.xyz).r; - #endif - #else - if (coords.z >= 1.0) - return 1.0; - return (coords.z <= texture2D(shadowTex, coords.xy).x ? 1.0 : 0.0); +uniform sampler2D normalMap; + +#if USE_BINORMALS + uniform sampler2D normalMap2; +#endif +#if USE_REFLECTION + uniform sampler2D reflectionMap; +#endif +#if USE_REFRACTION + uniform sampler2D refractionMap; +#endif +#if USE_REAL_DEPTH + uniform sampler2D depthTex; +#endif +#if USE_FOAM || USE_WAVES + uniform sampler2D heightmap; + uniform sampler2D infoTex; + uniform sampler2D Foam; + uniform sampler2D waveTex; +#endif +#if USE_SHADOWS + varying vec4 v_shadow; + #if USE_SHADOW_SAMPLER + uniform sampler2DShadow shadowTex; + #if USE_SHADOW_PCF + uniform vec4 shadowScale; #endif #else - return 1.0; + uniform sampler2D shadowTex; #endif -} + float get_shadow(vec4 coords) + { + #if USE_SHADOWS && !DISABLE_RECEIVE_SHADOWS + #if USE_SHADOW_SAMPLER + #if USE_SHADOW_PCF + vec2 offset = fract(coords.xy - 0.5); + vec4 size = vec4(offset + 1.0, 2.0 - offset); + vec4 weight = (vec4(2.0 - 1.0 / size.xy, 1.0 / size.zw - 1.0) + (coords.xy - offset).xyxy) * shadowScale.zwzw; + return (1.0/9.0)*dot(size.zxzx*size.wwyy, + vec4(shadow2D(shadowTex, vec3(weight.zw, coords.z)).r, + shadow2D(shadowTex, vec3(weight.xw, coords.z)).r, + shadow2D(shadowTex, vec3(weight.zy, coords.z)).r, + shadow2D(shadowTex, vec3(weight.xy, coords.z)).r)); + #else + return shadow2D(shadowTex, coords.xyz).r; + #endif + #else + if (coords.z >= 1.0) + return 1.0; + return (coords.z <= texture2D(shadowTex, coords.xy).x ? 1.0 : 0.0); + #endif + #else + return 1.0; + #endif + } #endif vec3 get_fog(vec3 color) @@ -94,7 +98,7 @@ vec3 get_fog(vec3 color) void main() { - #if USE_SUPERFANCYWATER + #if USE_FOAM || USE_WAVES vec4 heightmapval = texture2D(heightmap, gl_TexCoord[3].zw); vec2 beachOrientation = heightmapval.rb - vec2(0.5,0.5); float distToShore = heightmapval.a; @@ -108,32 +112,39 @@ void main() vec3 reflColor, refrColor, specular; float losMod; + float wavyFactor = waviness * 0.125; + + // always done cause this is also used, even when not using normals, by the refraction. vec3 ww = texture2D(normalMap, (gl_TexCoord[0].st) * mix(2.0,0.8,waviness/10.0) +gl_TexCoord[0].zw).xzy; - #if USE_SUPERFANCYWATER - vec3 ww2 = texture2D(normalMap2, (gl_TexCoord[0].st) * mix(2.0,0.8,waviness/10.0) +gl_TexCoord[0].zw).xzy; - ww = mix(ww, ww2, mod(time * 60.0, 8.0) / 8.0); - vec3 waves = texture2D(waveTex, gl_FragCoord.xy/screenSize).rbg - vec3(0.5,0.5,0.5); - float waveFoam = 0.0;//texture2D(waveTex, gl_FragCoord.xy/screenSize).a; - n = normalize(mix(waves, ww - vec3(0.5, 0.5, 0.5) , clamp(distToShore*3.0,0.4,1.0))); + #if USE_NORMALS + #if USE_BINORMALS + vec3 ww2 = texture2D(normalMap2, (gl_TexCoord[0].st) * mix(2.0,0.8,waviness/10.0) +gl_TexCoord[0].zw).xzy; + ww = mix(ww, ww2, mod(time * 60.0, 8.0) / 8.0); + #endif + + #if USE_WAVES + vec3 waves = texture2D(waveTex, gl_FragCoord.xy/screenSize).rbg - vec3(0.5,0.5,0.5); + float waveFoam = 0.0;//texture2D(waveTex, gl_FragCoord.xy/screenSize).a; + n = normalize(mix(waves, ww - vec3(0.5, 0.5, 0.5) , clamp(distToShore*3.0,0.4,1.0))); + #else + n = normalize(ww - vec3(0.5, 0.5, 0.5)); + #endif + n = mix(vec3(0.0,1.0,0.0),n,wavyFactor); #else - n = normalize(ww - vec3(0.5, 0.5, 0.5)); + n = vec3(0.0,1.0,0.0); #endif - float wavyFactor = waviness * 0.125; - - n = mix(vec3(0.0,1.0,0.0),n,wavyFactor); - l = -sunDir; v = normalize(cameraPos - worldPos); h = normalize(l + v); - ndotl = (dot(n, l) + 1.0) /2.0; + ndotl = (dot(n, l) + 1.0)/2.0; ndoth = dot(n, h); ndotv = dot(n, v); - #if USE_SUPERFANCYWATER + #if USE_REAL_DEPTH // Don't change these two. They should match the values in the config (TODO: dec uniforms). float zNear = 2.0; float zFar = 4096.0; @@ -165,9 +176,9 @@ void main() float distoFactor = clamp(waterDepth2/10.0,0.0,7.0); #endif - fresnel = pow(1.0 - ndotv, 1.3333);// approximation + fresnel = pow(1.0 - ndotv, 1.3333); // approximation - #if USE_SUPERFANCYWATER + #if USE_FOAM // texture is rotated 90°, moves slowly. vec2 foam1RC = vec2(-gl_TexCoord[0].t,gl_TexCoord[0].s)*1.3 - 0.012*n.xz + vec2(time*0.004,time*0.003); // texture is not rotated, moves twice faster in the opposite direction, translated. @@ -181,33 +192,50 @@ void main() if ((1.0 - finalFoam.r) >= wavyFactor) finalFoam = vec3(0.0); - - // waves bypass the regular foam restrictions. - finalFoam += min( max(0.0,-waves.b) * texture2D(Foam, foam1RC).r, 1.0)*3.0 * max(0.0,wavyFactor-0.1); + #if USE_WAVES && USE_NORMALS + // waves bypass the regular foam restrictions. + finalFoam += min( max(0.0,-waves.b) * texture2D(Foam, foam1RC).r, 1.0)*3.0 * max(0.0,wavyFactor-0.1); + #endif finalFoam *= sunColor; #endif - #if USE_SUPERFANCYWATER - refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * distoFactor) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords + #if USE_REFRACTION + #if USE_REAL_DEPTH + refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * distoFactor) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords + vec3 refColor = texture2D(refractionMap, refrCoords).rgb; + float luminance = (1.0 - clamp((waterDepth2/mix(300.0,1.0, pow(murkiness,0.2) )), 0.0, 1.0)); + float colorExtinction = clamp(waterDepth2*murkiness/5.0,0.0,1.0); + refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance); + #else + refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * 6.0) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords + // fake it. It won't look as good but it will give a similar result. + float perceivedDepth = waterDepth / v.y; + vec3 refColor = texture2D(refractionMap, refrCoords).rgb; + float luminance = (1.0 - clamp((perceivedDepth/mix(300.0,1.0, pow(murkiness,0.2) )), 0.0, 1.0)); + float colorExtinction = clamp(perceivedDepth*murkiness/5.0,0.0,1.0); + refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance); + #endif #else - refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * 6.0) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords + float alphaCoeff = 0.0; + #if USE_REAL_DEPTH + alphaCoeff = clamp(waterDepth2*murkiness/5.0,0.0,1.0); + #else + alphaCoeff = clamp((waterDepth / v.y)*murkiness/5.0,0.0,1.0); + #endif + refrColor = color; #endif - reflCoords = clamp( (0.5*gl_TexCoord[1].xy + 10.0*n.xz) / gl_TexCoord[1].w + 0.5,0.0,1.0); // Unbias texture coords - reflColor = mix(texture2D(reflectionMap, reflCoords).rgb, sunColor * reflectionTint, reflectionTintStrength); + #if !USE_NORMALS + // we're not using normals. Simulate by applying a B&W effect. + refrColor *= (ww*2.0).x; + #endif - #if USE_SUPERFANCYWATER - vec3 refColor = texture2D(refractionMap, refrCoords).rgb; - float luminance = (1.0 - clamp((waterDepth2/mix(300.0,1.0, pow(murkiness,0.2) )), 0.0, 1.0)); - float colorExtinction = clamp(waterDepth2*murkiness/5.0,0.0,1.0); - refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance); + #if USE_REFLECTION + reflCoords = clamp( (0.5*gl_TexCoord[1].xy + 10.0*n.xz) / gl_TexCoord[1].w + 0.5,0.0,1.0); // Unbias texture coords + reflColor = mix(texture2D(reflectionMap, reflCoords).rgb, sunColor * reflectionTint, reflectionTintStrength); #else - // fake it. It won't look as good but it will give a similar result. - float perceivedDepth = waterDepth / v.y; - vec3 refColor = texture2D(refractionMap, refrCoords).rgb; - float luminance = (1.0 - clamp((perceivedDepth/mix(300.0,1.0, pow(murkiness,0.2) )), 0.0, 1.0)); - float colorExtinction = clamp(perceivedDepth*murkiness/5.0,0.0,1.0); - refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance); + // TODO: implement some sort of skybox rendering. + reflColor = mix( (sunColor + vec3(0.565,0.843,0.961))/1.85, reflectionTint, reflectionTintStrength); #endif specular = pow(ndoth, shininess) * sunColor * specularStrength; @@ -215,23 +243,54 @@ void main() losMod = texture2D(losMap, gl_TexCoord[3].st).a; losMod = losMod < 0.03 ? 0.0 : losMod; - #if USE_SUPERFANCYWATER && USE_SHADOW + vec3 colour; + #if USE_SHADOWS float shadow = get_shadow(vec4(v_shadow.xy - 8.0*waviness*n.xz, v_shadow.zw)); float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + (murkiness * 0.15)); - vec3 colour = mix(refrColor*(shadow/5.0 + 0.8) + fresnel*shadow*specular, reflColor + fresnel*shadow*specular, fresShadow) + max(ndotl,0.4)*(finalFoam)*(shadow/2.0 + 0.5); - colour = mix( texture2D(refractionMap, (0.5*gl_TexCoord[2].xy) / gl_TexCoord[2].w + 0.5).rgb ,colour, clamp(waterDepth2,0.0,1.0)); + #if USE_FOAM + colour = mix(refrColor*(shadow/5.0 + 0.8) + fresnel*shadow*specular, reflColor + fresnel*shadow*specular, fresShadow) + max(ndotl,0.4)*(finalFoam)*(shadow/2.0 + 0.5); + #else + colour = mix(refrColor*(shadow/5.0 + 0.8) + fresnel*shadow*specular, reflColor + fresnel*shadow*specular, fresShadow); + #endif #else - vec3 colour = mix(refrColor + fresnel*specular, reflColor + fresnel*specular, fresnel); + #if USE_FOAM + colour = mix(refrColor + fresnel*specular, reflColor + fresnel*specular, fresnel) + max(ndotl,0.4)*(finalFoam); + #else + colour = mix(refrColor + fresnel*specular, reflColor + fresnel*specular, fresnel); + #endif + #endif + + #if USE_REFRACTION + #if USE_REAL_DEPTH + colour = mix(texture2D(refractionMap, (0.5*gl_TexCoord[2].xy) / gl_TexCoord[2].w + 0.5).rgb ,colour, clamp(waterDepth2,0.0,1.0)); + #else + colour = mix(texture2D(refractionMap, (0.5*gl_TexCoord[2].xy) / gl_TexCoord[2].w + 0.5).rgb ,colour, clamp(perceivedDepth,0.0,1.0)); + #endif + #else + // I'm not even sure what this does. + //colour = mix( texture2D(refractionMap, (0.5*gl_TexCoord[2].xy) / gl_TexCoord[2].w + 0.5).rgb ,colour, clamp(perceivedDepth,0.0,1.0)); #endif gl_FragColor.rgb = get_fog(colour) * losMod; - #if USE_SUPERFANCYWATER - gl_FragColor.a = clamp(distToShore-0.012,waterDepth2/2.0,1.0) + finalFoam.r * losMod; + #if USE_REAL_DEPTH + float alpha = clamp(waterDepth2*(5.0*murkiness),0.0,1.0); + #if !USE_REFRACTION + alpha *= alphaCoeff; + #endif + #if USE_FOAM + alpha += finalFoam.r * losMod; + #endif + gl_FragColor.a = alpha; #else // 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) - t = 18.0 * max(0.0, 0.9 - v.y); - gl_FragColor.a = clamp(0.15 * waterDepth * (1.2 + t + fresnel),0.0,1.0); + t = 30.0 * max(0.0, 0.9 - v.y); + float alpha = clamp(0.15 * waterDepth * (1.2 + t + fresnel),0.0,1.0); + #if !USE_REFRACTION + gl_FragColor.a = alpha * alphaCoeff; + #else + gl_FragColor.a = alpha; + #endif #endif } diff --git a/binaries/data/mods/public/shaders/glsl/water_high.vs b/binaries/data/mods/public/shaders/glsl/water_high.vs index 81916c5286..7d25354b20 100644 --- a/binaries/data/mods/public/shaders/glsl/water_high.vs +++ b/binaries/data/mods/public/shaders/glsl/water_high.vs @@ -17,8 +17,9 @@ uniform float mapSize; varying vec3 worldPos; varying float waterDepth; -varying vec4 v_shadow; - +#if USE_SHADOW + varying vec4 v_shadow; +#endif attribute vec3 a_vertex; attribute vec4 a_encodedDepth; @@ -34,7 +35,7 @@ void main() gl_TexCoord[3].zw = vec2(a_vertex.xz)/mapSize; - #if USE_SHADOW && USE_SUPERFANCYWATER + #if USE_SHADOW && USE_SHADOWS v_shadow = shadowTransform * vec4(a_vertex, 1.0); #if USE_SHADOW_SAMPLER && USE_SHADOW_PCF v_shadow.xy *= shadowScale.xy; diff --git a/source/ps/GameSetup/Config.cpp b/source/ps/GameSetup/Config.cpp index 9fbc00ffbc..dddde93dd1 100644 --- a/source/ps/GameSetup/Config.cpp +++ b/source/ps/GameSetup/Config.cpp @@ -38,8 +38,16 @@ CStr g_PlayerName = ""; bool g_Shadows = false; bool g_ShadowPCF = false; -bool g_FancyWater = false; -bool g_SuperFancyWater = false; + +bool g_WaterNormal = false; +bool g_WaterBinormal = false; +bool g_WaterRealDepth = false; +bool g_WaterFoam = false; +bool g_WaterCoastalWaves = false; +bool g_WaterRefraction = false; +bool g_WaterReflection = false; +bool g_WaterShadows = false; + bool g_Particles = false; bool g_Silhouettes = false; bool g_ShowSky = false; @@ -81,11 +89,20 @@ static void LoadGlobals() CFG_GET_USER_VAL("playername", String, g_PlayerName); CFG_GET_USER_VAL("shadows", Bool, g_Shadows); CFG_GET_USER_VAL("shadowpcf", Bool, g_ShadowPCF); - CFG_GET_USER_VAL("fancywater", Bool, g_FancyWater); - CFG_GET_USER_VAL("superfancywater", Bool, g_SuperFancyWater); - if (g_SuperFancyWater && !g_FancyWater) { - g_SuperFancyWater = false; - } + + CFG_GET_USER_VAL("waternormals",Bool, g_WaterNormal); + CFG_GET_USER_VAL("waterbinormals",Bool, g_WaterBinormal); + if (g_WaterBinormal && !g_WaterNormal) + g_WaterBinormal = false; + CFG_GET_USER_VAL("waterrealdepth",Bool, g_WaterRealDepth); + CFG_GET_USER_VAL("waterfoam",Bool, g_WaterFoam); + CFG_GET_USER_VAL("watercoastalwaves",Bool, g_WaterCoastalWaves); + if (g_WaterCoastalWaves && !g_WaterNormal) + g_WaterCoastalWaves = false; + CFG_GET_USER_VAL("waterrefraction",Bool, g_WaterRefraction); + CFG_GET_USER_VAL("waterreflection",Bool, g_WaterReflection); + CFG_GET_USER_VAL("watershadows",Bool, g_WaterShadows); + CFG_GET_USER_VAL("renderpath", String, g_RenderPath); CFG_GET_USER_VAL("particles", Bool, g_Particles); CFG_GET_USER_VAL("silhouettes", Bool, g_Silhouettes); diff --git a/source/ps/GameSetup/Config.h b/source/ps/GameSetup/Config.h index 74b5456242..9a07835aa3 100644 --- a/source/ps/GameSetup/Config.h +++ b/source/ps/GameSetup/Config.h @@ -49,10 +49,24 @@ extern CStr g_PlayerName; // flag to switch on shadows extern bool g_Shadows; -// flag to switch on reflective/refractive water -extern bool g_FancyWater; -// flag to switch on foam, normal interpolation, shadows on water -extern bool g_SuperFancyWater; + +// Use real normals for ocean-wave rendering, instead of applying them as a flat texture. +extern bool g_WaterNormal; +// Use interpolated normals, smoother. Requires g_WaterNormal. +extern bool g_WaterBinormal; +// Use real depth for water rendering. +extern bool g_WaterRealDepth; +// Show foam near the shores depending on waviness. +extern bool g_WaterFoam; +// Show coastal breaking waves. +extern bool g_WaterCoastalWaves; +// Use a real refraction map and not transparency. +extern bool g_WaterRefraction; +// Use a real reflection map and not a skybox texture. +extern bool g_WaterReflection; +// Enable on-water shadows. +extern bool g_WaterShadows; + // flag to switch on shadow PCF extern bool g_ShadowPCF; // flag to switch on particles rendering diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index e59d83594a..dd25a8a9c5 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -574,8 +574,16 @@ static void InitRenderer() // set renderer options from command line options - NOVBO must be set before opening the renderer g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO, g_NoGLVBO); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, g_Shadows); - g_Renderer.SetOptionBool(CRenderer::OPT_FANCYWATER, g_FancyWater); - g_Renderer.SetOptionBool(CRenderer::OPT_SUPERFANCYWATER, (g_SuperFancyWater && g_FancyWater) ); + + g_Renderer.SetOptionBool(CRenderer::OPT_WATERNORMAL, g_WaterNormal); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERBINORMAL, g_WaterBinormal); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERREALDEPTH, g_WaterRealDepth); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERFOAM, g_WaterFoam); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERCOASTALWAVES, g_WaterCoastalWaves); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERREFLECTION, g_WaterRefraction); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERREFRACTION, g_WaterReflection); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERSHADOW, g_WaterShadows); + g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath)); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCF, g_ShadowPCF); g_Renderer.SetOptionBool(CRenderer::OPT_PARTICLES, g_Particles); diff --git a/source/ps/GameSetup/HWDetect.cpp b/source/ps/GameSetup/HWDetect.cpp index 4092778dbb..fadbff70bd 100644 --- a/source/ps/GameSetup/HWDetect.cpp +++ b/source/ps/GameSetup/HWDetect.cpp @@ -123,16 +123,31 @@ void SetDisableShadowPCF(void* UNUSED(cbdata), bool disabled) void SetDisableFancyWater(void* UNUSED(cbdata), bool disabled) { - if (!IsOverridden("fancywater")) { - g_FancyWater = !disabled; - g_SuperFancyWater = !disabled; + if (!IsOverridden("waternormals")) + { + g_WaterNormal = !disabled; + if (!g_WaterNormal) + g_WaterBinormal = false; } + if (!IsOverridden("waterbinormals")) + g_WaterBinormal = !disabled; + if (!IsOverridden("waterrealdepth")) + g_WaterRealDepth = !disabled; + if (!IsOverridden("waterfoam")) + g_WaterFoam = !disabled; + if (!IsOverridden("watercoastalwaves")) + g_WaterCoastalWaves = !disabled; + if (!IsOverridden("waterrefraction")) + g_WaterRefraction = !disabled; + if (!IsOverridden("waterreflection")) + g_WaterReflection = !disabled; + if (!IsOverridden("watershadows")) + g_WaterShadows = !disabled; } void SetRenderPath(void* UNUSED(cbdata), std::string renderpath) { - if (!IsOverridden("fancywater")) - g_RenderPath = renderpath; + g_RenderPath = renderpath; } void RunHardwareDetection() diff --git a/source/renderer/Renderer.cpp b/source/renderer/Renderer.cpp index 5f211ac664..7d985614a4 100644 --- a/source/renderer/Renderer.cpp +++ b/source/renderer/Renderer.cpp @@ -429,8 +429,6 @@ CRenderer::CRenderer() m_Options.m_NoVBO = false; m_Options.m_RenderPath = RP_DEFAULT; - m_Options.m_FancyWater = false; - m_Options.m_SuperFancyWater = false; m_Options.m_Shadows = false; m_Options.m_ShadowAlphaFix = true; m_Options.m_ARBProgramShadow = true; @@ -469,8 +467,16 @@ CRenderer::CRenderer() m_Stats.Reset(); AddLocalProperty(L"particles", &m_Options.m_Particles, false); - AddLocalProperty(L"fancyWater", &m_Options.m_FancyWater, false); - AddLocalProperty(L"superFancyWater", &m_Options.m_SuperFancyWater, false); + + AddLocalProperty(L"waternormal", &m_Options.m_WaterNormal, false); + AddLocalProperty(L"waterbinormal", &m_Options.m_WaterBinormal, false); + AddLocalProperty(L"waterrealdepth", &m_Options.m_WaterRealDepth, false); + AddLocalProperty(L"waterreflection", &m_Options.m_WaterReflection, false); + AddLocalProperty(L"waterrefraction", &m_Options.m_WaterRefraction, false); + AddLocalProperty(L"waterfoam", &m_Options.m_WaterFoam, false); + AddLocalProperty(L"watercoastalwaves", &m_Options.m_WaterCoastalWaves, false); + AddLocalProperty(L"watershadow", &m_Options.m_WaterShadow, false); + AddLocalProperty(L"horizonHeight", &m->skyManager.m_HorizonHeight, false); AddLocalProperty(L"waterMurkiness", &m->waterManager.m_Murkiness, false); AddLocalProperty(L"waterReflTintStrength", &m->waterManager.m_ReflectionTintStrength, false); @@ -682,11 +688,29 @@ void CRenderer::SetOptionBool(enum Option opt,bool value) m_Options.m_Shadows = value; MakeShadersDirty(); break; - case OPT_FANCYWATER: - m_Options.m_FancyWater = value; + case OPT_WATERNORMAL: + m_Options.m_WaterNormal = value; break; - case OPT_SUPERFANCYWATER: - m_Options.m_SuperFancyWater = value; + case OPT_WATERBINORMAL: + m_Options.m_WaterBinormal = value; + break; + case OPT_WATERREALDEPTH: + m_Options.m_WaterRealDepth = value; + break; + case OPT_WATERFOAM: + m_Options.m_WaterFoam = value; + break; + case OPT_WATERCOASTALWAVES: + m_Options.m_WaterCoastalWaves = value; + break; + case OPT_WATERREFLECTION: + m_Options.m_WaterReflection = value; + break; + case OPT_WATERREFRACTION: + m_Options.m_WaterRefraction = value; + break; + case OPT_WATERSHADOW: + m_Options.m_WaterShadow = value; break; case OPT_SHADOWPCF: m_Options.m_ShadowPCF = value; @@ -716,10 +740,22 @@ bool CRenderer::GetOptionBool(enum Option opt) const return m_Options.m_NoVBO; case OPT_SHADOWS: return m_Options.m_Shadows; - case OPT_FANCYWATER: - return m_Options.m_FancyWater; - case OPT_SUPERFANCYWATER: - return m_Options.m_SuperFancyWater; + case OPT_WATERNORMAL: + return m_Options.m_WaterNormal; + case OPT_WATERBINORMAL: + return m_Options.m_WaterBinormal; + case OPT_WATERREALDEPTH: + return m_Options.m_WaterRealDepth; + case OPT_WATERFOAM: + return m_Options.m_WaterFoam; + case OPT_WATERCOASTALWAVES: + return m_Options.m_WaterCoastalWaves; + case OPT_WATERREFLECTION: + return m_Options.m_WaterReflection; + case OPT_WATERREFRACTION: + return m_Options.m_WaterRefraction; + case OPT_WATERSHADOW: + return m_Options.m_WaterShadow; case OPT_SHADOWPCF: return m_Options.m_ShadowPCF; case OPT_PARTICLES: @@ -815,10 +851,6 @@ void CRenderer::BeginFrame() if (m->ShadersDirty) ReloadShaders(); - // this is ugly, I should do otherwise - if (g_GameRestarted) - m_WaterManager->m_NeedsReloading = true; - m->Model.ModShader->SetShadowMap(&m->shadow); m->Model.ModShader->SetLightEnv(m_LightEnv); } @@ -1446,9 +1478,13 @@ void CRenderer::RenderSubmissions() waterScissor = m->terrainRenderer.ScissorWater(m_ViewCamera.GetViewProjection()); if (waterScissor.GetVolume() > 0 && m_WaterManager->WillRenderFancyWater()) { - SScreenRect reflectionScissor = RenderReflections(context, waterScissor); - SScreenRect refractionScissor = RenderRefractions(context, waterScissor); - + SScreenRect reflectionScissor; + if (m_Options.m_WaterReflection) + reflectionScissor = RenderReflections(context, waterScissor); + SScreenRect refractionScissor; + if (m_Options.m_WaterRefraction) + refractionScissor = RenderRefractions(context, waterScissor); + PROFILE3_GPU("water scissor"); SScreenRect dirty; dirty.x1 = std::min(reflectionScissor.x1, refractionScissor.x1); diff --git a/source/renderer/Renderer.h b/source/renderer/Renderer.h index d58c738d61..3a05447c83 100644 --- a/source/renderer/Renderer.h +++ b/source/renderer/Renderer.h @@ -80,8 +80,14 @@ public: enum Option { OPT_NOVBO, OPT_SHADOWS, - OPT_FANCYWATER, - OPT_SUPERFANCYWATER, + OPT_WATERNORMAL, + OPT_WATERBINORMAL, + OPT_WATERREALDEPTH, + OPT_WATERFOAM, + OPT_WATERCOASTALWAVES, + OPT_WATERREFLECTION, + OPT_WATERREFRACTION, + OPT_WATERSHADOW, OPT_SHADOWPCF, OPT_PARTICLES, OPT_SILHOUETTES, @@ -124,8 +130,16 @@ public: struct Options { bool m_NoVBO; bool m_Shadows; - bool m_FancyWater; - bool m_SuperFancyWater; + + bool m_WaterNormal; + bool m_WaterBinormal; + bool m_WaterRealDepth; + bool m_WaterFoam; + bool m_WaterCoastalWaves; + bool m_WaterRefraction; + bool m_WaterReflection; + bool m_WaterShadow; + RenderPath m_RenderPath; bool m_ShadowAlphaFix; bool m_ARBProgramShadow; diff --git a/source/renderer/TerrainRenderer.cpp b/source/renderer/TerrainRenderer.cpp index 46e69aa58a..0cf7db4516 100644 --- a/source/renderer/TerrainRenderer.cpp +++ b/source/renderer/TerrainRenderer.cpp @@ -651,28 +651,36 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, ShadowMap* WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CShaderDefines defines = context; - // deactivate superfancy in Atlas so it won't lag. - bool superFancy = WaterMgr->WillRenderSuperFancyWater() && !g_AtlasGameLoop->running; - // Don't use m_NeedsReloading since that also restarts the map texture generation (slow). - bool reload = false; - if (superFancy && !WaterMgr->m_RunningSuperFancy) - { - reload = true; - WaterMgr->m_RunningSuperFancy = true; - } - else if (!superFancy && WaterMgr->m_RunningSuperFancy) - { - reload = true; - WaterMgr->m_RunningSuperFancy = false; - } + WaterMgr->updateQuality(); + // If we're using fancy water, make sure its shader is loaded - if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading || reload) + if (!m->fancyWaterShader || (WaterMgr->m_NeedsReloading && (!g_AtlasGameLoop->running || !WaterMgr->m_TerrainChangeThisTurn) )) { - if (superFancy) - defines.Add("USE_SUPERFANCYWATER", "1"); - else - defines.Add("USE_SUPERFANCYWATER", "0"); + if(WaterMgr->m_WaterNormal) + defines.Add("USE_NORMALS","1"); + if(WaterMgr->m_WaterBinormal) + defines.Add("USE_BINORMALS","1"); + if(WaterMgr->m_WaterRealDepth) + defines.Add("USE_REAL_DEPTH","1"); + if(WaterMgr->m_WaterFoam) + defines.Add("USE_FOAM","1"); + if(WaterMgr->m_WaterCoastalWaves) + defines.Add("USE_WAVES","1"); + if(WaterMgr->m_WaterRefraction) + defines.Add("USE_REFRACTION","1"); + if(WaterMgr->m_WaterReflection) + defines.Add("USE_REFLECTION","1"); + if(shadow && WaterMgr->m_WaterShadows) + defines.Add("USE_SHADOWS","1"); + + m->wavesShader = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", defines); + if (!m->wavesShader) + { + LOGERROR(L"Failed to load waves shader. Deactivating waves.\n"); + g_Renderer.SetOptionBool(CRenderer::OPT_WATERCOASTALWAVES, false); + defines.Add("USE_WAVES","0"); + } // haven't updated the ARB shader yet so I'll always load the GLSL /*if (!g_Renderer.m_Options.m_PreferGLSL && !superFancy) @@ -680,32 +688,34 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, ShadowMap* else*/ m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_high", defines); - m->wavesShader = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", defines); - if (!m->fancyWaterShader) { LOGERROR(L"Failed to load water shader. Falling back to non-fancy water.\n"); - g_Renderer.m_Options.m_FancyWater = false; - g_Renderer.m_Options.m_SuperFancyWater = false; + WaterMgr->m_RenderWater = false; return false; } - if (WaterMgr->m_NeedsReloading) + // we need to actually recompute the whole map settings. + if (WaterMgr->m_NeedsFullReloading) { delete[] WaterMgr->m_Heightmap; WaterMgr->m_Heightmap = NULL; glDeleteTextures(1, &WaterMgr->m_HeightmapTexture); WaterMgr->m_NeedsReloading = false; + WaterMgr->m_NeedsFullReloading = false; WaterMgr->m_waveTT = 0; WaterMgr->m_depthTT = 0; } } + if (g_AtlasGameLoop->running) + WaterMgr->m_TerrainChangeThisTurn = false; + CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture(); GLuint depthTex; - // TODO: ultimately, this should go in the water manager for readibility. - if (superFancy) + // creating the real depth texture using the depth buffer. + if (WaterMgr->m_WaterRealDepth) { if (WaterMgr->m_depthTT == 0) { @@ -725,12 +735,11 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, ShadowMap* glCopyTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0); glBindTexture(GL_TEXTURE_2D, 0); - - // If we have not already calculated the heightmap, do it once and for all. - // TODO: in Atlas this is recomputed every frame, which is way too slow. Temporary fix is disabling it in Atlas. - if (WaterMgr->m_Heightmap == NULL) - WaterMgr->CreateSuperfancyInfo(); } + // Calculating the advanced informations about Foam and all if the quality calls for it. + if (WaterMgr->m_Heightmap == NULL && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves)) + WaterMgr->CreateSuperfancyInfo(); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); @@ -743,8 +752,8 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, ShadowMap* GLuint FramebufferName = 0; - // rendering waves to a framebuffer if super fancy water is activated - if (superFancy) + // rendering waves to a framebuffer + if (WaterMgr->m_WaterCoastalWaves) { // Save the post-processing framebuffer. GLint fbo; @@ -803,36 +812,41 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, ShadowMap* m->fancyWaterShader->Bind(); - m->fancyWaterShader->BindTexture("normalMap", WaterMgr->m_NormalMap[curTex]); - if (superFancy) - { - m->fancyWaterShader->BindTexture("normalMap2", WaterMgr->m_NormalMap[nexTex]); - m->fancyWaterShader->BindTexture("Foam", WaterMgr->m_Foam); - m->fancyWaterShader->BindTexture("heightmap", WaterMgr->m_HeightmapTexture); - m->fancyWaterShader->BindTexture("infoTex", WaterMgr->m_OtherInfoTex); - m->fancyWaterShader->BindTexture("depthTex", WaterMgr->m_depthTT); - m->fancyWaterShader->BindTexture("waveTex", WaterMgr->m_waveTT); - - m->fancyWaterShader->Uniform("mapSize", (float)(WaterMgr->m_TexSize)); - } // Shift the texture coordinates by these amounts to make the water "flow" float tx = -fmod(time, 81.0 / (WaterMgr->m_Waviness/20.0 + 0.8) )/(81.0/ (WaterMgr->m_Waviness/20.0 + 0.8) ); float ty = -fmod(time, 34.0 / (WaterMgr->m_Waviness/20.0 + 0.8) )/(34.0/ (WaterMgr->m_Waviness/20.0 + 0.8) ); - + float repeatPeriod = WaterMgr->m_RepeatPeriod; - + const CCamera& camera = g_Renderer.GetViewCamera(); CVector3D camPos = camera.m_Orientation.GetTranslation(); - // Bind reflection and refraction textures + m->fancyWaterShader->BindTexture("normalMap", WaterMgr->m_NormalMap[curTex]); + if (WaterMgr->m_WaterBinormal) + m->fancyWaterShader->BindTexture("normalMap2", WaterMgr->m_NormalMap[nexTex]); + if (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves) + { + m->fancyWaterShader->BindTexture("Foam", WaterMgr->m_Foam); + m->fancyWaterShader->BindTexture("heightmap", WaterMgr->m_HeightmapTexture); + m->fancyWaterShader->BindTexture("infoTex", WaterMgr->m_OtherInfoTex); + m->fancyWaterShader->Uniform("mapSize", (float)(WaterMgr->m_TexSize)); + } + if (WaterMgr->m_WaterRealDepth) + m->fancyWaterShader->BindTexture("depthTex", WaterMgr->m_depthTT); + if (WaterMgr->m_WaterCoastalWaves) + m->fancyWaterShader->BindTexture("waveTex", WaterMgr->m_waveTT); + if (WaterMgr->m_WaterReflection) m->fancyWaterShader->BindTexture("reflectionMap", WaterMgr->m_ReflectionTexture); + if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->BindTexture("refractionMap", WaterMgr->m_RefractionTexture); m->fancyWaterShader->BindTexture("losMap", losTexture.GetTextureSmooth()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); + + // TODO: only bind what's really needed for that. m->fancyWaterShader->Uniform("sunDir", lightEnv.GetSunDir()); m->fancyWaterShader->Uniform("sunColor", lightEnv.m_SunColor.X); m->fancyWaterShader->Uniform("color", WaterMgr->m_WaterColor); @@ -854,7 +868,7 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, ShadowMap* m->fancyWaterShader->Uniform("time", (float)time); m->fancyWaterShader->Uniform("screenSize", (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f); - if (shadow && superFancy) + if (shadow && WaterMgr->m_WaterShadows) { m->fancyWaterShader->BindTexture("shadowTex", shadow->GetTexture()); m->fancyWaterShader->Uniform("shadowTransform", shadow->GetTextureMatrix()); @@ -989,8 +1003,10 @@ void TerrainRenderer::RenderWater(const CShaderDefines& context, ShadowMap* shad { WaterManager* WaterMgr = g_Renderer.GetWaterManager(); - if (!WaterMgr->WillRenderFancyWater() || !RenderFancyWater(context, shadow)) + if (!WaterMgr->WillRenderFancyWater()) RenderSimpleWater(); + else + RenderFancyWater(context, shadow); } void TerrainRenderer::RenderPriorities() diff --git a/source/renderer/WaterManager.cpp b/source/renderer/WaterManager.cpp index 67c3895b16..ce729e704e 100644 --- a/source/renderer/WaterManager.cpp +++ b/source/renderer/WaterManager.cpp @@ -77,8 +77,19 @@ WaterManager::WaterManager() m_Heightmap = NULL; m_HeightmapTexture = 0; m_OtherInfoTex = NULL; - m_RunningSuperFancy = WillRenderSuperFancyWater(); + + m_WaterNormal = false; + m_WaterBinormal = false; + m_WaterRealDepth = false; + m_WaterFoam = false; + m_WaterCoastalWaves = false; + m_WaterRefraction = false; + m_WaterReflection = false; + m_WaterShadows = false; + m_NeedsReloading = false; + m_NeedsFullReloading = false; + m_TerrainChangeThisTurn = false; m_VBWaves = NULL; m_VBWavesIndices = NULL; @@ -269,84 +280,6 @@ void WaterManager::CreateSuperfancyInfo() waveForceHQ[j*mapSize + i] = 255 - color * 7; } } - - // okay let's create the waves squares. i'll divide the map in arbitrary squares - // For each of these squares, check if waves are needed. - // If yes, look for the best positionning (in order to have a nice blending with the shore) - // Then clean-up: remove squares that are too close to each other - - std::vector waveSquares; - - int size = 8; // I think this is the size of the squares. - for (int i = 0; i < mapSize/size; ++i) - { - for (int j = 0; j < mapSize/size; ++j) - { - - int landTexel = 0; - int waterTexel = 0; - CVector3D avnormal (0.0f,0.0f,0.0f); - CVector2D landPosition(0.0f,0.0f); - CVector2D waterPosition(0.0f,0.0f); - for (int xx = 0; xx < size; ++xx) - { - for (int yy = 0; yy < size; ++yy) - { - if (terrain->GetVertexGroundLevel(i*size+xx,j*size+yy) > this->m_WaterHeight) - { - landTexel++; - landPosition += CVector2D(i*size+xx,j*size+yy); - } - else - { - waterPosition += CVector2D(i*size+xx,j*size+yy); - waterTexel++; - } - avnormal += terrain->CalcExactNormal( (i*size+xx)*4.0f,(j*size+yy)*4.0f); - } - } - landPosition /= landTexel; - waterPosition /= waterTexel; - - avnormal[1] = 1.0f; - avnormal.Normalize(); - avnormal[1] = 0.0f; - - if (landTexel < size/2) - continue; - // this should help ensure that the shore is pretty flat. - if (avnormal.Length() <= 0.2f) - continue; - - // To get the best position for squares, I start at the mean "ocean" position - // And step by step go to the mean "land" position. I keep the position where I change from water to land. - // If this never happens, the square is scrapped. - if (terrain->GetExactGroundLevel(waterPosition.X*4.0f,waterPosition.Y*4.0f) > this->m_WaterHeight) - continue; - - CVector2D squarePos(-1,-1); - for (u8 i = 0; i < 40; i++) - { - squarePos = landPosition * (i/40.0f) + waterPosition * (1.0f-(i/40.0f)); - if (terrain->GetExactGroundLevel(squarePos.X*4.0f,squarePos.Y*4.0f) > this->m_WaterHeight) - break; - } - if (squarePos.X == -1) - continue; - - u8 enter = 1; - // okaaaaaay. Got a square. Check for proximity. - for (unsigned long i = 0; i < waveSquares.size(); i++) - { - if ( CVector2D(waveSquares[i]-squarePos).LengthSquared() < 100) { - enter = 0; - break; - } - } - if (enter == 1) - waveSquares.push_back(squarePos); - } - } // this creates information for waves and stores it in a texture. // texture "newHeightmap" is [x vector of wave direction, coefficient of "water raise", y vector of waves, distance to shore.] // texture "OtherInfo" stores the intensity of actual waves and of foam. @@ -459,82 +392,204 @@ void WaterManager::CreateSuperfancyInfo() glBindTexture(GL_TEXTURE_2D, 0); - // Actually create the waves' meshes. - std::vector waves_vertex_data; - std::vector waves_indices; - - // loop through each square point. Look in the square around it, calculate the normal - // create the square. - for (unsigned long i = 0; i < waveSquares.size(); i++) + if (this->m_WaterCoastalWaves) { - CVector2D pos(waveSquares[i]); + // okay let's create the waves squares. i'll divide the map in arbitrary squares + // For each of these squares, check if waves are needed. + // If yes, look for the best positionning (in order to have a nice blending with the shore) + // Then clean-up: remove squares that are too close to each other - CVector3D avgnorm(0.0f,0.0f,0.0f); - for (int xx = -size/2; xx < size/2; ++xx) + std::vector waveSquares; + + int size = 8; // I think this is the size of the squares. + for (int i = 0; i < mapSize/size; ++i) { - for (int yy = -size/2; yy < size/2; ++yy) + for (int j = 0; j < mapSize/size; ++j) { - avgnorm += terrain->CalcExactNormal((pos.X+xx)*4.0f,(pos.Y+yy)*4.0f); + + int landTexel = 0; + int waterTexel = 0; + CVector3D avnormal (0.0f,0.0f,0.0f); + CVector2D landPosition(0.0f,0.0f); + CVector2D waterPosition(0.0f,0.0f); + for (int xx = 0; xx < size; ++xx) + { + for (int yy = 0; yy < size; ++yy) + { + if (terrain->GetVertexGroundLevel(i*size+xx,j*size+yy) > this->m_WaterHeight) + { + landTexel++; + landPosition += CVector2D(i*size+xx,j*size+yy); + } + else + { + waterPosition += CVector2D(i*size+xx,j*size+yy); + waterTexel++; + } + avnormal += terrain->CalcExactNormal( (i*size+xx)*4.0f,(j*size+yy)*4.0f); + } + } + landPosition /= landTexel; + waterPosition /= waterTexel; + + avnormal[1] = 1.0f; + avnormal.Normalize(); + avnormal[1] = 0.0f; + + if (landTexel < size/2) + continue; + // this should help ensure that the shore is pretty flat. + if (avnormal.Length() <= 0.2f) + continue; + + // To get the best position for squares, I start at the mean "ocean" position + // And step by step go to the mean "land" position. I keep the position where I change from water to land. + // If this never happens, the square is scrapped. + if (terrain->GetExactGroundLevel(waterPosition.X*4.0f,waterPosition.Y*4.0f) > this->m_WaterHeight) + continue; + + CVector2D squarePos(-1,-1); + for (u8 i = 0; i < 40; i++) + { + squarePos = landPosition * (i/40.0f) + waterPosition * (1.0f-(i/40.0f)); + if (terrain->GetExactGroundLevel(squarePos.X*4.0f,squarePos.Y*4.0f) > this->m_WaterHeight) + break; + } + if (squarePos.X == -1) + continue; + + u8 enter = 1; + // okaaaaaay. Got a square. Check for proximity. + for (unsigned long i = 0; i < waveSquares.size(); i++) + { + if ( CVector2D(waveSquares[i]-squarePos).LengthSquared() < 100) { + enter = 0; + break; + } + } + if (enter == 1) + waveSquares.push_back(squarePos); } } - avgnorm[1] = 0.1f; - // okay crank out a square. - // we have the direction of the square. We'll get the perpendicular vector too - CVector2D perp(-avgnorm[2],avgnorm[0]); - perp = perp.Normalized(); - avgnorm = avgnorm.Normalized(); + + // Actually create the waves' meshes. + std::vector waves_vertex_data; + std::vector waves_indices; - SWavesVertex vertex[4]; - vertex[0].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y + perp.Y*(size/2.2f) - avgnorm[2]*1.0f); - vertex[0].m_Position *= 4.0f; - vertex[0].m_Position.Y = this->m_WaterHeight + 1.0f; - vertex[0].m_UV[1] = 1; - vertex[0].m_UV[0] = 0; + // loop through each square point. Look in the square around it, calculate the normal + // create the square. + for (unsigned long i = 0; i < waveSquares.size(); i++) + { + CVector2D pos(waveSquares[i]); + + CVector3D avgnorm(0.0f,0.0f,0.0f); + for (int xx = -size/2; xx < size/2; ++xx) + { + for (int yy = -size/2; yy < size/2; ++yy) + { + avgnorm += terrain->CalcExactNormal((pos.X+xx)*4.0f,(pos.Y+yy)*4.0f); + } + } + avgnorm[1] = 0.1f; + // okay crank out a square. + // we have the direction of the square. We'll get the perpendicular vector too + CVector2D perp(-avgnorm[2],avgnorm[0]); + perp = perp.Normalized(); + avgnorm = avgnorm.Normalized(); + + SWavesVertex vertex[4]; + vertex[0].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y + perp.Y*(size/2.2f) - avgnorm[2]*1.0f); + vertex[0].m_Position *= 4.0f; + vertex[0].m_Position.Y = this->m_WaterHeight + 1.0f; + vertex[0].m_UV[1] = 1; + vertex[0].m_UV[0] = 0; + + vertex[1].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y - perp.Y*(size/2.2f) - avgnorm[2]*1.0f); + vertex[1].m_Position *= 4.0f; + vertex[1].m_Position.Y = this->m_WaterHeight + 1.0f; + vertex[1].m_UV[1] = 1; + vertex[1].m_UV[0] = 1; + + vertex[3].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y + perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f)); + vertex[3].m_Position *= 4.0f; + vertex[3].m_Position.Y = this->m_WaterHeight + 1.0f; + vertex[3].m_UV[1] = 0; + vertex[3].m_UV[0] = 0; + + vertex[2].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y - perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f)); + vertex[2].m_Position *= 4.0f; + vertex[2].m_Position.Y = this->m_WaterHeight + 1.0f; + vertex[2].m_UV[1] = 0; + vertex[2].m_UV[0] = 1; + + waves_indices.push_back(waves_vertex_data.size()); + waves_vertex_data.push_back(vertex[0]); + waves_indices.push_back(waves_vertex_data.size()); + waves_vertex_data.push_back(vertex[1]); + waves_indices.push_back(waves_vertex_data.size()); + waves_vertex_data.push_back(vertex[2]); + waves_indices.push_back(waves_vertex_data.size()); + waves_vertex_data.push_back(vertex[3]); + } - vertex[1].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y - perp.Y*(size/2.2f) - avgnorm[2]*1.0f); - vertex[1].m_Position *= 4.0f; - vertex[1].m_Position.Y = this->m_WaterHeight + 1.0f; - vertex[1].m_UV[1] = 1; - vertex[1].m_UV[0] = 1; + // waves + // allocate vertex buffer + this->m_VBWaves = g_VBMan.Allocate(sizeof(SWavesVertex), waves_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); + this->m_VBWaves->m_Owner->UpdateChunkVertices(this->m_VBWaves, &waves_vertex_data[0]); - vertex[3].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y + perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f)); - vertex[3].m_Position *= 4.0f; - vertex[3].m_Position.Y = this->m_WaterHeight + 1.0f; - vertex[3].m_UV[1] = 0; - vertex[3].m_UV[0] = 0; - - vertex[2].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y - perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f)); - vertex[2].m_Position *= 4.0f; - vertex[2].m_Position.Y = this->m_WaterHeight + 1.0f; - vertex[2].m_UV[1] = 0; - vertex[2].m_UV[0] = 1; - - waves_indices.push_back(waves_vertex_data.size()); - waves_vertex_data.push_back(vertex[0]); - waves_indices.push_back(waves_vertex_data.size()); - waves_vertex_data.push_back(vertex[1]); - waves_indices.push_back(waves_vertex_data.size()); - waves_vertex_data.push_back(vertex[2]); - waves_indices.push_back(waves_vertex_data.size()); - waves_vertex_data.push_back(vertex[3]); + // Construct indices buffer + this->m_VBWavesIndices = g_VBMan.Allocate(sizeof(GLushort), waves_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); + this->m_VBWavesIndices->m_Owner->UpdateChunkVertices(this->m_VBWavesIndices, &waves_indices[0]); + } +} + +//////////////////////////////////////////////////////////////////////// +// This will set the bools properly +void WaterManager::updateQuality() +{ + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERNORMAL) != m_WaterNormal) { + m_WaterNormal = g_Renderer.GetOptionBool(CRenderer::OPT_WATERNORMAL); + m_NeedsReloading = true; + } + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERBINORMAL) != m_WaterBinormal) { + m_WaterBinormal = g_Renderer.GetOptionBool(CRenderer::OPT_WATERBINORMAL); + m_NeedsReloading = true; + } + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH) != m_WaterRealDepth) { + m_WaterRealDepth = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH); + m_NeedsReloading = true; + } + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERFOAM) != m_WaterFoam) { + m_WaterFoam = g_Renderer.GetOptionBool(CRenderer::OPT_WATERFOAM); + m_NeedsReloading = true; + m_NeedsFullReloading = true; + } + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERCOASTALWAVES) != m_WaterCoastalWaves) { + m_WaterCoastalWaves = g_Renderer.GetOptionBool(CRenderer::OPT_WATERCOASTALWAVES); + m_NeedsReloading = true; + m_NeedsFullReloading = true; + } + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION) != m_WaterRefraction) { + m_WaterRefraction = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION); + m_NeedsReloading = true; + } + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION) != m_WaterReflection) { + m_WaterReflection = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION); + m_NeedsReloading = true; + } + if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERSHADOW) != m_WaterShadows) { + m_WaterShadows = g_Renderer.GetOptionBool(CRenderer::OPT_WATERSHADOW); + m_NeedsReloading = true; } - - // waves - // allocate vertex buffer - this->m_VBWaves = g_VBMan.Allocate(sizeof(SWavesVertex), waves_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER); - this->m_VBWaves->m_Owner->UpdateChunkVertices(this->m_VBWaves, &waves_vertex_data[0]); - - // Construct indices buffer - this->m_VBWavesIndices = g_VBMan.Allocate(sizeof(GLushort), waves_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER); - this->m_VBWavesIndices->m_Owner->UpdateChunkVertices(this->m_VBWavesIndices, &waves_indices[0]); } bool WaterManager::WillRenderFancyWater() { - return (g_Renderer.GetCapabilities().m_FragmentShader && g_Renderer.GetOptionBool(CRenderer::OPT_FANCYWATER)); + if (!g_Renderer.GetCapabilities().m_FragmentShader) + return false; + if (g_Renderer.GetRenderPath() == 1) + return false; + if (!m_RenderWater) + return false; + return true; } -bool WaterManager::WillRenderSuperFancyWater() -{ - return (WillRenderFancyWater() && g_Renderer.GetOptionBool(CRenderer::OPT_SUPERFANCYWATER)); -} - diff --git a/source/renderer/WaterManager.h b/source/renderer/WaterManager.h index 8ae1465447..0a30728d91 100644 --- a/source/renderer/WaterManager.h +++ b/source/renderer/WaterManager.h @@ -65,8 +65,23 @@ public: int m_WaterCurrentTex; CColor m_WaterColor; bool m_RenderWater; - bool m_RunningSuperFancy; + + // Those variables register the current quality level. If there is a change, I have to recompile the shader. + bool m_WaterNormal; + bool m_WaterBinormal; + bool m_WaterRealDepth; + bool m_WaterFoam; + bool m_WaterCoastalWaves; + bool m_WaterRefraction; + bool m_WaterReflection; + bool m_WaterShadows; + bool m_NeedsReloading; + // requires also recreating the super fancy information. + bool m_NeedsFullReloading; + // used only by Atlas to know when to update the water map. + bool m_TerrainChangeThisTurn; + bool m_WaterScroll; float m_WaterHeight; float m_WaterMaxAlpha; @@ -131,17 +146,16 @@ public: */ void CreateSuperfancyInfo(); + /** + * Updates the settings to the one from the renderer, and sets m_NeedsReloading. + */ + void updateQuality(); + /** * Returns true if fancy water shaders will be used (i.e. the hardware is capable * and it hasn't been configured off) */ bool WillRenderFancyWater(); - - /** - * Returns true if super fancy water shaders will be used (i.e. the hardware is capable - * and it hasn't been configured off) - */ - bool WillRenderSuperFancyWater(); }; diff --git a/source/tools/webservices/userreport/views_private.py b/source/tools/webservices/userreport/views_private.py index 3d0b3e1d8b..fb4d5499c1 100644 --- a/source/tools/webservices/userreport/views_private.py +++ b/source/tools/webservices/userreport/views_private.py @@ -86,7 +86,6 @@ def report_performance(request): mapname = 'unknown' # e.g. random maps msecs = None shadows = False - fancywater = False for name,table in json['profiler'].items(): m = re.match(r'Profiling Information for: root \(Time in node: (\d+\.\d+) msec/frame\)', name) if m: @@ -101,18 +100,11 @@ def report_performance(request): except (KeyError, TypeError): pass - try: - if float(table['data']['render'][0]['render submissions'][0]['render reflections'][2]): - fancywater = True - except (KeyError, TypeError): - pass - if msecs is None: return None options = [] if shadows: options.append('S') - if fancywater: options.append('W') return { 'msecs': msecs,