fixes #1572 and fixes #932

Changes the water rendering in game to support higher quality effects
and to look nicer.

This was SVN commit r12802.
This commit is contained in:
wraitii 2012-10-31 18:42:17 +00:00
parent 97b46d402c
commit fb035d08e3
16 changed files with 903 additions and 134 deletions

View file

@ -42,6 +42,7 @@ bpp = 0
; System settings:
fancywater = true
superfancywater = false
shadows = true
shadowpcf = true
vsync = false

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:72dedbfe5e17dd1cc29ccf0007bb4ec2d3fb7a332bbf694b689253b24bf40863
size 235798

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:54c2830f69861022c5654049cf5c6d2db57d52b3878a3e40ccaccc387ae7d40e
size 33090

View file

@ -298,77 +298,84 @@
<!-- ================================ ================================ -->
<!-- Settings Window -->
<!-- ================================ ================================ -->
<object name="settingsDialogPanel"
style="StoneDialog"
type="image"
size="50%-180 50%-200 50%+180 50%+100"
hidden="true"
>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">Settings</object>
<object style="TranslucentPanelThinBorder"
type="image"
size="32 32 100%-32 100%-70"
>
<!-- Settings / shadows -->
<object size="0 10 100%-80 35" type="text" style="RightLabelText" ghost="true">Enable Shadows</object>
<object name="shadowsCheckbox" size="100%-56 15 100%-30 40" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.shadows) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.shadows = this.checked;</action>
<object name="settingsDialogPanel"
style="StoneDialog"
type="image"
size="50%-180 50%-200 50%+180 50%+125"
hidden="true"
>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">Settings</object>
<object style="TranslucentPanelThinBorder"
type="image"
size="32 32 100%-32 100%-70"
>
<!-- Settings / shadows -->
<object size="0 10 100%-80 35" type="text" style="RightLabelText" ghost="true">Enable Shadows</object>
<object name="shadowsCheckbox" size="100%-56 15 100%-30 40" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.shadows) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.shadows = this.checked;</action>
</object>
<!-- Settings / Shadow PCF -->
<object size="0 35 100%-80 60" type="text" style="RightLabelText" ghost="true">Enable Shadow Filtering</object>
<object name="shadowPCFCheckbox" size="100%-56 40 100%-30 65" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.shadowPCF) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.shadowPCF = this.checked;</action>
</object>
<!-- Settings / Water -->
<object size="0 60 100%-80 85" type="text" style="RightLabelText" ghost="true">Enable Water Reflections</object>
<object name="fancyWaterCheckbox" size="100%-56 65 100%-30 90" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.fancyWater) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.fancyWater = this.checked; </action>
</object>
<!-- Settings / Advanced Water -->
<object size="0 85 100%-80 110" type="text" style="RightLabelText" ghost="true">Enable Advanced Water</object>
<object name="superFancyWaterCheckbox" size="100%-56 90 100%-30 115" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.superFancyWater) { if (renderer.fancyWater) this.checked = true; else this.checked = false; }</action>
<action on="Press">renderer.superFancyWater = this.checked; </action>
</object>
<!-- Settings / Particles -->
<object size="0 110 100%-80 135" type="text" style="RightLabelText" ghost="true">Enable Particles</object>
<object name="particlesCheckbox" size="100%-56 115 100%-30 140" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.particles) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.particles = this.checked;</action>
</object>
<!-- Settings / Unit Silhouettes -->
<object size="0 135 100%-80 160" type="text" style="RightLabelText" ghost="true">Enable Unit Silhouettes</object>
<object name="silhouettesCheckbox" size="100%-56 140 100%-30 165" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.silhouettes) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.silhouettes = this.checked;</action>
</object>
<!-- Settings / Music-->
<object size="0 160 100%-80 185" type="text" style="RightLabelText" ghost="true">Enable Music</object>
<object size="100%-56 165 100%-30 190" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Press">if (this.checked) global.music.start(); else global.music.stop();</action>
</object>
<!-- Settings / Dev Overlay -->
<object size="0 185 100%-80 210" type="text" style="RightLabelText" ghost="true">Developer Overlay</object>
<object size="100%-56 190 100%-30 215" type="checkbox" style="StoneCrossBox" checked="false">
<action on="Press">toggleDeveloperOverlay();</action>
</object>
</object>
<!-- Close button -->
<object type="button"
style="StoneButton"
size="50%-64 100%-52 50%+64 100%-24"
tooltip_style="sessionToolTip"
>
Close
<action on="Press">closeSettings(true);</action>
</object>
</object>
<!-- Settings / Shadow PCF -->
<object size="0 35 100%-80 60" type="text" style="RightLabelText" ghost="true">Enable Shadow Filtering</object>
<object name="shadowPCFCheckbox" size="100%-56 40 100%-30 65" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.shadowPCF) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.shadowPCF = this.checked;</action>
</object>
<!-- Settings / Water -->
<object size="0 60 100%-80 85" type="text" style="RightLabelText" ghost="true">Enable Water Reflections</object>
<object name="fancyWaterCheckbox" size="100%-56 65 100%-30 90" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.fancyWater) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.fancyWater = this.checked;</action>
</object>
<!-- Settings / Particles -->
<object size="0 85 100%-80 110" type="text" style="RightLabelText" ghost="true">Enable Particles</object>
<object name="particlesCheckbox" size="100%-56 90 100%-30 115" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.particles) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.particles = this.checked;</action>
</object>
<!-- Settings / Unit Silhouettes -->
<object size="0 110 100%-80 135" type="text" style="RightLabelText" ghost="true">Enable Unit Silhouettes</object>
<object name="silhouettesCheckbox" size="100%-56 115 100%-30 140" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Load">if (renderer.silhouettes) this.checked = true; else this.checked = false;</action>
<action on="Press">renderer.silhouettes = this.checked;</action>
</object>
<!-- Settings / Music-->
<object size="0 135 100%-80 160" type="text" style="RightLabelText" ghost="true">Enable Music</object>
<object size="100%-56 140 100%-30 165" type="checkbox" style="StoneCrossBox" checked="true">
<action on="Press">if (this.checked) global.music.start(); else global.music.stop();</action>
</object>
<!-- Settings / Dev Overlay -->
<object size="0 160 100%-80 185" type="text" style="RightLabelText" ghost="true">Developer Overlay</object>
<object size="100%-56 165 100%-30 190" type="checkbox" style="StoneCrossBox" checked="false">
<action on="Press">toggleDeveloperOverlay();</action>
</object>
</object>
<!-- Close button -->
<object type="button"
style="StoneButton"
size="50%-64 100%-52 50%+64 100%-24"
tooltip_style="sessionToolTip"
>
Close
<action on="Press">closeSettings(true);</action>
</object>
</object>
<!-- ================================ ================================ -->
<!-- Top Panel -->
<!-- ================================ ================================ -->

View file

@ -13,75 +13,225 @@ uniform float specularStrength; // Scaling for specular reflection (specular col
uniform float waviness; // "Wildness" of the reflections and refractions; choose based on texture
uniform vec3 tint; // Tint for refraction (used to simulate particles in water)
uniform float murkiness; // Amount of tint to blend in with the refracted colour
uniform float fullDepth; // Depth at which to use full murkiness (shallower water will be clearer)
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 color; // color of the water
uniform vec3 fogColor;
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
#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;
varying vec4 v_shadow;
#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);
#endif
#else
return 1.0;
#endif
}
#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);
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)
#if USE_SUPERFANCYWATER
vec4 heightmapval = texture2D(heightmap, gl_TexCoord[3].zw);
vec2 beachOrientation = heightmapval.rb - vec2(0.5,0.5);
float distToShore = heightmapval.a;
#endif
vec3 n, l, h, v; // Normal, light vector, half-vector and view vector (vector to eye)
float ndotl, ndoth, ndotv;
float fresnel;
float t; // Temporary variable
vec2 reflCoords, refrCoords;
vec3 reflColor, refrColor, specular;
float losMod;
vec3 ww = texture2D(normalMap, (gl_TexCoord[0].st) * mix(2.0,0.8,waviness/10.0) +gl_TexCoord[0].zw).xzy;
n = normalize(texture2D(normalMap, gl_TexCoord[0].st).xzy - vec3(0.5, 0.5, 0.5));
#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)));
#else
n = normalize(ww - vec3(0.5, 0.5, 0.5));
#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);
ndotl = (dot(n, l) + 1.0) /2.0;
ndoth = dot(n, h);
ndotv = dot(n, v);
fresnel = pow(1.0 - ndotv, 0.8); // A rather random Fresnel approximation
refrCoords = (0.5*gl_TexCoord[2].xy - 0.8*waviness*n.xz) / gl_TexCoord[2].w + 0.5; // Unbias texture coords
reflCoords = (0.5*gl_TexCoord[1].xy + waviness*n.xz) / gl_TexCoord[1].w + 0.5; // Unbias texture coords
#if USE_SUPERFANCYWATER
// Don't change these two. They should match the values in the config (TODO: dec uniforms).
float zNear = 2.0;
float zFar = 4096.0;
// Okay so here it's a tad complicated. I want to distort the depth buffer along the waves for a nice effect.
// However this causes a problem around underwater objects (think fishes): on some pixels, the depth will be seen as the same as the fishes'
// and the color will be grass ( cause I don't distort the refraction coord by exactly the same stuff)
// Also, things like towers with the feet in water would cause the buffer to see the depth as actually negative in some places.
// So what I do is first check the undistorted depth, then I compare with the distorted value and fix.
float water_b = gl_FragCoord.z;
float water_n = 2.0 * water_b - 1.0;
float waterDBuffer = 2.0 * zNear * zFar / (zFar + zNear - water_n * (zFar - zNear));
float undistortedBuffer = texture2D(depthTex, (gl_FragCoord.xy) / screenSize).x;
float undisto_z_b = texture2D(depthTex, (gl_FragCoord.xy) / screenSize).x;
float undisto_z_n = 2.0 * undisto_z_b - 1.0;
float waterDepth_undistorted = (2.0 * zNear * zFar / (zFar + zNear - undisto_z_n * (zFar - zNear)) - waterDBuffer);
vec2 depthCoord = clamp((gl_FragCoord.xy) / screenSize - n.xz*clamp( waterDepth_undistorted/400.0,0.0,0.05) , 0.001, 0.999);
float z_b = texture2D(depthTex, depthCoord).x;
if (z_b < undisto_z_b)
z_b = undisto_z_b;
float z_n = 2.0 * z_b - 1.0;
float waterDepth2 = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer);
float distoFactor = clamp(waterDepth2/10.0,0.0,7.0);
#endif
fresnel = pow(1.0 - ndotv, 1.3333);// approximation
reflColor = mix(texture2D(reflectionMap, reflCoords).rgb, sunColor * reflectionTint,
reflectionTintStrength);
#if USE_SUPERFANCYWATER
// 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.
vec2 foam2RC = gl_TexCoord[0].st*1.8 + vec2(time*-0.019,time*-0.012) - 0.012*n.xz + vec2(0.4,0.2);
vec2 WaveRocking = cos(time*1.2566) * beachOrientation * clamp(1.0 - distToShore*2.0,0.1,1.0)/3.0;
vec4 foam1 = texture2D(Foam, foam1RC + vec2(-WaveRocking.t,WaveRocking.s));
vec4 foam2 = foam1.r*texture2D(Foam, foam2RC + WaveRocking);
vec3 finalFoam = min((foam2).rrr * texture2D(infoTex,gl_TexCoord[3].zw).g,1.0);
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);
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
#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
#endif
refrColor = (0.5 + 0.5*ndotl) * mix(texture2D(refractionMap, refrCoords).rgb, sunColor * tint,
murkiness * clamp(waterDepth / fullDepth, 0.0, 1.0)); // Murkiness and tint at this pixel (tweaked based on lighting and depth)
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_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);
#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);
#endif
specular = pow(ndoth, shininess) * sunColor * specularStrength;
specular = pow(max(0.0, ndoth), shininess) * sunColor * specularStrength;
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);
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)
t = 18.0 * max(0.0, 0.7 - v.y);
gl_FragColor.a = 0.15 * waterDepth * (1.2 + t + fresnel);
#if USE_SUPERFANCYWATER
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));
#else
vec3 colour = mix(refrColor + fresnel*specular, reflColor + fresnel*specular, fresnel);
#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;
#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);
#endif
}

View file

@ -3,23 +3,43 @@
uniform mat4 reflectionMatrix;
uniform mat4 refractionMatrix;
uniform mat4 losMatrix;
uniform mat4 shadowTransform;
uniform float repeatScale;
uniform vec2 translation;
uniform float waviness; // "Wildness" of the reflections and refractions; choose based on texture
#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
uniform vec4 shadowScale;
#endif
uniform float time;
uniform float mapSize;
varying vec3 worldPos;
varying float waterDepth;
varying vec4 v_shadow;
attribute vec3 a_vertex;
attribute vec4 a_encodedDepth;
void main()
{
worldPos = a_vertex.xyz;
worldPos = a_vertex;
waterDepth = dot(a_encodedDepth.xyz, vec3(255.0, -255.0, 1.0));
gl_TexCoord[0].st = a_vertex.xz*repeatScale + translation;
gl_TexCoord[0] = vec4(a_vertex.xz*repeatScale,translation);
gl_TexCoord[1] = reflectionMatrix * vec4(a_vertex, 1.0); // projective texturing
gl_TexCoord[2] = refractionMatrix * vec4(a_vertex, 1.0);
gl_TexCoord[3] = losMatrix * vec4(a_vertex, 1.0);
gl_TexCoord[3].zw = vec2(a_vertex.xz)/mapSize;
#if USE_SHADOW && USE_SUPERFANCYWATER
v_shadow = shadowTransform * vec4(a_vertex, 1.0);
#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
v_shadow.xy *= shadowScale.xy;
#endif
#endif
gl_Position = gl_ModelViewProjectionMatrix * vec4(a_vertex, 1.0);
}

View file

@ -39,6 +39,7 @@ CStr g_PlayerName = "";
bool g_Shadows = false;
bool g_ShadowPCF = false;
bool g_FancyWater = false;
bool g_SuperFancyWater = false;
bool g_Particles = false;
bool g_Silhouettes = false;
bool g_ShowSky = false;
@ -81,6 +82,10 @@ static void LoadGlobals()
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("renderpath", String, g_RenderPath);
CFG_GET_USER_VAL("particles", Bool, g_Particles);
CFG_GET_USER_VAL("silhouettes", Bool, g_Silhouettes);

View file

@ -51,6 +51,8 @@ extern CStr g_PlayerName;
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;
// flag to switch on shadow PCF
extern bool g_ShadowPCF;
// flag to switch on particles rendering

View file

@ -575,6 +575,7 @@ static void InitRenderer()
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.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCF, g_ShadowPCF);
g_Renderer.SetOptionBool(CRenderer::OPT_PARTICLES, g_Particles);

View file

@ -123,8 +123,10 @@ void SetDisableShadowPCF(void* UNUSED(cbdata), bool disabled)
void SetDisableFancyWater(void* UNUSED(cbdata), bool disabled)
{
if (!IsOverridden("fancywater"))
if (!IsOverridden("fancywater")) {
g_FancyWater = !disabled;
g_SuperFancyWater = !disabled;
}
}
void SetRenderPath(void* UNUSED(cbdata), std::string renderpath)

View file

@ -69,6 +69,7 @@
#include "renderer/VertexBufferManager.h"
#include "renderer/WaterManager.h"
extern bool g_GameRestarted;
///////////////////////////////////////////////////////////////////////////////////
// CRendererStatsTable - Profile display of rendering stats
@ -429,6 +430,7 @@ 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;
@ -468,6 +470,7 @@ 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"horizonHeight", &m->skyManager.m_HorizonHeight, false);
AddLocalProperty(L"waterMurkiness", &m->waterManager.m_Murkiness, false);
AddLocalProperty(L"waterReflTintStrength", &m->waterManager.m_ReflectionTintStrength, false);
@ -682,6 +685,9 @@ void CRenderer::SetOptionBool(enum Option opt,bool value)
case OPT_FANCYWATER:
m_Options.m_FancyWater = value;
break;
case OPT_SUPERFANCYWATER:
m_Options.m_SuperFancyWater = value;
break;
case OPT_SHADOWPCF:
m_Options.m_ShadowPCF = value;
MakeShadersDirty();
@ -712,6 +718,8 @@ bool CRenderer::GetOptionBool(enum Option opt) const
return m_Options.m_Shadows;
case OPT_FANCYWATER:
return m_Options.m_FancyWater;
case OPT_SUPERFANCYWATER:
return m_Options.m_SuperFancyWater;
case OPT_SHADOWPCF:
return m_Options.m_ShadowPCF;
case OPT_PARTICLES:
@ -806,6 +814,10 @@ 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);
@ -1481,7 +1493,7 @@ void CRenderer::RenderSubmissions()
RenderTransparentModels(context, TRANSPARENT_OPAQUE);
ogl_WarnIfError();
m->terrainRenderer.RenderWater();
m->terrainRenderer.RenderWater(context, &m->shadow);
ogl_WarnIfError();
// render transparent stuff again, but only the blended parts that overlap water

View file

@ -81,6 +81,7 @@ public:
OPT_NOVBO,
OPT_SHADOWS,
OPT_FANCYWATER,
OPT_SUPERFANCYWATER,
OPT_SHADOWPCF,
OPT_PARTICLES,
OPT_SILHOUETTES,
@ -124,6 +125,7 @@ public:
bool m_NoVBO;
bool m_Shadows;
bool m_FancyWater;
bool m_SuperFancyWater;
RenderPath m_RenderPath;
bool m_ShadowAlphaFix;
bool m_ARBProgramShadow;

View file

@ -27,10 +27,10 @@
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/ShaderManager.h"
#include "renderer/ShadowMap.h"
#include "graphics/TerritoryTexture.h"
#include "graphics/TextRenderer.h"
@ -51,6 +51,9 @@
#include "renderer/VertexArray.h"
#include "renderer/WaterManager.h"
#include "tools/atlas/GameInterface/GameLoop.h"
extern GameLoopState* g_AtlasGameLoop;
///////////////////////////////////////////////////////////////////////////////////////////////
// TerrainRenderer implementation
@ -84,6 +87,7 @@ struct TerrainRendererInternals
/// Fancy water shader
CShaderProgramPtr fancyWaterShader;
CShaderProgramPtr wavesShader;
CSimulation2* simulation;
};
@ -641,41 +645,181 @@ CBoundingBoxAligned TerrainRenderer::ScissorWater(const CMatrix3D &viewproj)
}
// Render fancy water
bool TerrainRenderer::RenderFancyWater()
bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, ShadowMap* shadow)
{
PROFILE3_GPU("fancy water");
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterShader)
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)
{
m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_high", CShaderDefines());
reload = true;
WaterMgr->m_RunningSuperFancy = true;
}
else if (!superFancy && WaterMgr->m_RunningSuperFancy)
{
reload = true;
WaterMgr->m_RunningSuperFancy = false;
}
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading || reload)
{
if (superFancy)
defines.Add("USE_SUPERFANCYWATER", "1");
else
defines.Add("USE_SUPERFANCYWATER", "0");
// haven't updated the ARB shader yet so I'll always load the GLSL
/*if (!g_Renderer.m_Options.m_PreferGLSL && !superFancy)
m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("arb/water_high", defines);
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;
return false;
}
if (WaterMgr->m_NeedsReloading)
{
delete[] WaterMgr->m_Heightmap;
WaterMgr->m_Heightmap = NULL;
glDeleteTextures(1, &WaterMgr->m_HeightmapTexture);
WaterMgr->m_NeedsReloading = false;
WaterMgr->m_waveTT = 0;
WaterMgr->m_depthTT = 0;
}
}
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
GLuint depthTex;
// TODO: ultimately, this should go in the water manager for readibility.
if (superFancy)
{
if (WaterMgr->m_depthTT == 0)
{
glGenTextures(1, (GLuint*)&depthTex);
WaterMgr->m_depthTT = depthTex;
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, g_Renderer.GetWidth(), g_Renderer.GetHeight(),
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL);
}
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
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();
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6;
double period = 8;
int curTex = (int)(time*60/period) % 60;
int nexTex = (curTex + 1) % 60;
GLuint FramebufferName = 0;
// rendering waves to a framebuffer if super fancy water is activated
if (superFancy)
{
// Save the post-processing framebuffer.
GLint fbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo);
pglGenFramebuffersEXT(1, &FramebufferName);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FramebufferName);
GLuint renderedTexture;
if (WaterMgr->m_waveTT == 0)
{
glGenTextures(1, &renderedTexture);
WaterMgr->m_waveTT = renderedTexture;
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_waveTT);
int size = (int)round_up_to_pow2((unsigned)g_Renderer.GetHeight());
if(size > g_Renderer.GetHeight()) size /= 2;
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0,GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_waveTT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, WaterMgr->m_waveTT, 0);
glClearColor(0.5f,0.5f,1.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// rendering
m->wavesShader->Bind();
m->wavesShader->BindTexture("waveTex", WaterMgr->m_Wave);
m->wavesShader->BindTexture("infoTex", WaterMgr->m_OtherInfoTex);
m->wavesShader->Uniform("time", (float)time);
m->wavesShader->Uniform("waviness", WaterMgr->m_Waviness);
m->wavesShader->Uniform("mapSize", (float)(WaterMgr->m_TexSize));
SWavesVertex *base=(SWavesVertex *)WaterMgr->m_VBWaves->m_Owner->Bind();
GLsizei stride = sizeof(SWavesVertex);
m->wavesShader->VertexPointer(3, GL_FLOAT, stride, &base[WaterMgr->m_VBWaves->m_Index].m_Position);
m->wavesShader->TexCoordPointer(GL_TEXTURE0,2,GL_BYTE, stride,&base[WaterMgr->m_VBWaves->m_Index].m_UV);
m->wavesShader->AssertPointersBound();
u8* indexBase = WaterMgr->m_VBWavesIndices->m_Owner->Bind();
glDrawElements(GL_QUADS, (GLsizei) WaterMgr->m_VBWavesIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(WaterMgr->m_VBWavesIndices->m_Index));
g_Renderer.m_Stats.m_DrawCalls++;
CVertexBuffer::Unbind();
m->wavesShader->Unbind();
// rebind post-processing frambuffer.
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glBindTexture(GL_TEXTURE_2D, 0);
}
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)/81.0;
float ty = -fmod(time, 34.0)/34.0;
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();
@ -688,14 +832,14 @@ bool TerrainRenderer::RenderFancyWater()
m->fancyWaterShader->BindTexture("losMap", losTexture.GetTextureSmooth());
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
m->fancyWaterShader->Uniform("ambient", lightEnv.m_TerrainAmbientColor);
m->fancyWaterShader->Uniform("sunDir", lightEnv.GetSunDir());
m->fancyWaterShader->Uniform("sunColor", lightEnv.m_SunColor.X);
m->fancyWaterShader->Uniform("color", WaterMgr->m_WaterColor);
m->fancyWaterShader->Uniform("shininess", WaterMgr->m_Shininess);
m->fancyWaterShader->Uniform("specularStrength", WaterMgr->m_SpecularStrength);
m->fancyWaterShader->Uniform("waviness", WaterMgr->m_Waviness);
m->fancyWaterShader->Uniform("murkiness", WaterMgr->m_Murkiness);
m->fancyWaterShader->Uniform("fullDepth", WaterMgr->m_WaterFullDepth);
m->fancyWaterShader->Uniform("tint", WaterMgr->m_WaterTint);
m->fancyWaterShader->Uniform("reflectionTintStrength", WaterMgr->m_ReflectionTintStrength);
m->fancyWaterShader->Uniform("reflectionTint", WaterMgr->m_ReflectionTint);
@ -707,6 +851,17 @@ bool TerrainRenderer::RenderFancyWater()
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);
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)
{
m->fancyWaterShader->BindTexture("shadowTex", shadow->GetTexture());
m->fancyWaterShader->Uniform("shadowTransform", shadow->GetTextureMatrix());
int width = shadow->GetWidth();
int height = shadow->GetHeight();
m->fancyWaterShader->Uniform("shadowScale", width, height, 1.0f / width, 1.0f / height);
}
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
@ -717,6 +872,7 @@ bool TerrainRenderer::RenderFancyWater()
m->fancyWaterShader->Unbind();
pglActiveTextureARB(GL_TEXTURE0);
pglDeleteFramebuffersEXT(1, &FramebufferName);
glDisable(GL_BLEND);
@ -829,11 +985,11 @@ void TerrainRenderer::RenderSimpleWater()
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
void TerrainRenderer::RenderWater(const CShaderDefines& context, ShadowMap* shadow)
{
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
if (!WaterMgr->WillRenderFancyWater() || !RenderFancyWater())
if (!WaterMgr->WillRenderFancyWater() || !RenderFancyWater(context, shadow))
RenderSimpleWater();
}

View file

@ -135,7 +135,7 @@ public:
* preconditions : PrepareForRendering must have been called this
* frame before calling RenderWater.
*/
void RenderWater();
void RenderWater(const CShaderDefines& context, ShadowMap* shadow);
/**
* Calculate a scissor rectangle for the visible water patches.
@ -161,7 +161,7 @@ private:
* RenderFancyWater: internal rendering method for fancy water.
* Returns false if unable to render with fancy water.
*/
bool RenderFancyWater();
bool RenderFancyWater(const CShaderDefines& context, ShadowMap* shadow);
/**
* RenderSimpleWater: internal rendering method for water

View file

@ -22,6 +22,7 @@
#include "precompiled.h"
#include "graphics/TextureManager.h"
#include "graphics/Terrain.h"
#include "lib/bits.h"
#include "lib/timer.h"
@ -29,6 +30,10 @@
#include "lib/res/graphics/ogl_tex.h"
#include "maths/MathUtil.h"
#include "maths/Vector2D.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "renderer/WaterManager.h"
#include "renderer/Renderer.h"
@ -46,8 +51,8 @@ WaterManager::WaterManager()
m_RenderWater = false; // disabled until textures are successfully loaded
m_WaterHeight = 5.0f;
m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
m_WaterFullDepth = 4.0f;
m_WaterMaxAlpha = 0.85f;
m_WaterFullDepth = 5.0f;
m_WaterMaxAlpha = 1.0f;
m_WaterAlphaOffset = -0.05f;
m_SWaterTrans = 0;
m_TWaterTrans = 0;
@ -62,19 +67,41 @@ WaterManager::WaterManager()
m_RefractionTextureSize = 0;
m_WaterTexTimer = 0.0;
m_Shininess = 150.0f;
m_SpecularStrength = 0.4f;
m_SpecularStrength = 0.6f;
m_Waviness = 8.0f;
m_ReflectionTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
m_ReflectionTintStrength = 0.0f;
m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
m_Murkiness = 0.45f;
m_RepeatPeriod = 16.0f;
m_Heightmap = NULL;
m_HeightmapTexture = 0;
m_OtherInfoTex = NULL;
m_RunningSuperFancy = WillRenderSuperFancyWater();
m_NeedsReloading = false;
m_VBWaves = NULL;
m_VBWavesIndices = NULL;
m_TexSize = -1;
m_depthTT = 0;
m_waveTT = 0;
}
WaterManager::~WaterManager()
{
// Cleanup if the caller messed up
UnloadWaterTextures();
delete[] m_Heightmap;
glDeleteTextures(1, &m_HeightmapTexture);
glDeleteTextures(1, &m_OtherInfoTex);
glDeleteTextures(1, &m_depthTT);
glDeleteTextures(1, &m_waveTT);
if (m_VBWaves) g_VBMan.Release(m_VBWaves);
if (m_VBWavesIndices) g_VBMan.Release(m_VBWavesIndices);
}
@ -114,7 +141,24 @@ int WaterManager::LoadWaterTextures()
texture->Prefetch();
m_NormalMap[i] = texture;
}
// Load foam (for fancy water)
{
CTextureProperties textureProps("art/textures/terrain/types/water/foam.png");
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_Foam = texture;
}
// Load waves (for fancy water)
{
CTextureProperties textureProps("art/textures/terrain/types/water/shore_wave.png");
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_Wave = texture;
}
// Set the size to the largest power of 2 that is <= to the window height, so
// the reflection/refraction images will fit within the window
// (alternative: use FBO's, which can have arbitrary size - but do we need
@ -143,8 +187,8 @@ int WaterManager::LoadWaterTextures()
0, GL_RGB, 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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
// Enable rendering, now that we've succeeded this far
m_RenderWater = true;
@ -169,8 +213,328 @@ void WaterManager::UnloadWaterTextures()
}
}
///////////////////////////////////////////////////////////////////
// Create information about the terrain and wave vertices.
void WaterManager::CreateSuperfancyInfo()
{
ssize_t mapSize = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
ssize_t texSize = (GLsizei)round_up_to_pow2((size_t)mapSize);
CTerrain *terrain = g_Game->GetWorld()->GetTerrain();
u32* newHeightmap = NULL;
newHeightmap = new u32[texSize*texSize];
u32* otherInfo = NULL;
otherInfo = new u32[texSize*texSize];
u16* heightmap = terrain->GetHeightMap();
// Recovering wave direction at any given point (5x5 blur)
// the precision is one vertice, which iirc is 4 per in-game "tile"
CVector3D* groundDirection = new CVector3D[(mapSize+1)*(mapSize+1)];
for (ssize_t i = 0; i < mapSize; ++i)
{
for (ssize_t j = 0; j < mapSize; ++j)
{
CVector3D normal;
for (int xx = -5; xx <= 5; ++xx)
{
for (int yy = -5; yy <= 5; ++yy)
{
normal += terrain->CalcExactNormal((i+xx)*4,(j+yy)*4);
}
}
normal = normal.Normalized();
groundDirection[j*mapSize+i] = normal;
}
}
// Recovering wave intensity
u8* waveForceHQ = new u8[(mapSize+1)*(mapSize+1)]; // high qual map.
u16 waterHeightInu16 = this->m_WaterHeight/HEIGHT_SCALE;
for (ssize_t i = 0; i < mapSize; ++i)
{
for (ssize_t j = 0; j < mapSize; ++j)
{
u8 color = 0;
for (int v = 0; v < 20; v++){
if (j-v >= 0 && i-v >= 0 && heightmap[(j-v)*mapSize + i-v] > waterHeightInu16)
{
if (color == 0)
color = 5;
else
color++;
}
}
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<CVector2D> 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.0,0.0,0.0);
CVector2D landPosition(0.0,0.0);
CVector2D waterPosition(0.0,0.0);
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.0,(j*size+yy)*4.0);;
}
}
landPosition /= landTexel;
waterPosition /= waterTexel;
avnormal[1] = 1.0;
avnormal.Normalize();
avnormal[1] = 0.0;
if (landTexel < size/2)
continue;
// this should help ensure that the shore is pretty flat.
if (avnormal.Length() <= 0.2)
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,waterPosition.Y*4) > this->m_WaterHeight)
continue;
CVector2D squarePos(-1,-1);
for (u8 i = 0; i < 40; i++)
{
squarePos = landPosition * (i/40.0) + waterPosition * (1.0-(i/40.0));
if (terrain->GetExactGroundLevel(squarePos.X*4,squarePos.Y*4) > 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.
for (ssize_t i = 0; i < mapSize; ++i)
{
for (ssize_t j = 0; j < mapSize; ++j)
{
float depth = this->m_WaterHeight - heightmap[j*mapSize + i]*HEIGHT_SCALE;
int distanceToShore = 10000;
// calculation of the distance to the shore.
// this is pretty exact.
if (depth >= 0)
{
// check in the square around.
for (int xx = -5; xx <= 5; ++xx)
{
for (int yy = -5; yy <= 5; ++yy)
{
if (i+xx >= 0 && i + xx < mapSize)
if (j + yy >= 0 && j + yy < mapSize)
{
float hereDepth = this->m_WaterHeight - heightmap[(j+yy)*mapSize + (i+xx)]*HEIGHT_SCALE;
if (hereDepth < 0 && xx*xx + yy*yy < distanceToShore)
distanceToShore = xx*xx + yy*yy;
}
}
}
// finer check
for (float xx = -2.5; xx <= 2.5; ++xx)
{
for (float yy = -2.5; yy <= 2.5; ++yy)
{
float hereDepth = this->m_WaterHeight - terrain->GetExactGroundLevel( (i+xx)*4, (j+yy)*4 );
if (hereDepth < 0 && xx*xx + yy*yy < distanceToShore)
distanceToShore = xx*xx + yy*yy;
}
}
}
else
{
for (float xx = -2.0; xx <= 2.0; xx+=0.5)
{
for (float yy = -2.0; yy <= 2.0; yy+=0.5)
{
float hereDepth = this->m_WaterHeight - terrain->GetExactGroundLevel( (i+xx)*4, (j+yy)*4 );
if (hereDepth > 0)
distanceToShore = 0;
}
}
}
distanceToShore = sqrt(distanceToShore);
// Compute the normals
// Also create the waves quad.
CVector3D normal;
int waterRaise = 0;
for (int xx = -4; xx <= 4; ++xx)
{
for (int yy = -4; yy <= 4; ++yy)
{
normal += terrain->CalcExactNormal((i+xx)*4,(j+yy)*4);
if (terrain->GetVertexGroundLevel(i+xx,j+yy) < heightmap[j*mapSize + i]*HEIGHT_SCALE)
waterRaise += heightmap[j*mapSize + i]*HEIGHT_SCALE - terrain->GetVertexGroundLevel(i+xx,j+yy);
}
}
waterRaise = waterRaise > 255 ? 255 : waterRaise; // gives a very good result, actually.
normal *= 1.0/81.0;
normal[1] = 0.1; // acts as an anti distorter
normal = normal.Normalized();
u8 r = static_cast<u8>(normal[0]*128 + 127);
u8 b = static_cast<u8>(normal[2]*128 + 127);
distanceToShore = distanceToShore > 10 ? 10 : distanceToShore;
newHeightmap[j*texSize + i] = (r << 24) + (waterRaise << 16) + (b << 8) + (distanceToShore*25 << 0);
depth = clamp(depth,0.0f,10.0f);
float wvness = this->m_Waviness;
// computing the amount of foam I want
float foamAmount = (waterRaise/255.0) * (1.0 - depth/10.0) * (waveForceHQ[j*mapSize+i]/255.0) * (wvness/8.0);
foamAmount += clamp(wvness/2.0f - distanceToShore,0.0f,wvness/2.0f)/(wvness/2.0) * clamp(wvness/9.0f,0.3f,1.0f);
foamAmount = foamAmount > 1.0 ? 1.0: foamAmount;
otherInfo[j*texSize + i] = (waveForceHQ[j*mapSize+i] << 24) + ((u8)(foamAmount*255) << 16) + (0x00 << 8) + (0x00 << 0);
}
}
this->m_TexSize = texSize*4;
this->m_Heightmap = newHeightmap;
GLuint heightName;
glGenTextures(1, &heightName);
glBindTexture(GL_TEXTURE_2D, heightName);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texSize,texSize, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,newHeightmap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
this->m_HeightmapTexture = heightName;
glBindTexture(GL_TEXTURE_2D, 0);
GLuint otherInfoId;
glGenTextures(1, &otherInfoId);
glBindTexture(GL_TEXTURE_2D, otherInfoId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texSize,texSize, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,otherInfo);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
this->m_OtherInfoTex = otherInfoId;
glBindTexture(GL_TEXTURE_2D, 0);
// Actually create the waves' meshes.
std::vector<SWavesVertex> waves_vertex_data;
std::vector<GLushort> 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++)
{
CVector2D pos(waveSquares[i]);
CVector3D avgnorm(0.0,0.0,0.0);
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,(pos.Y+yy)*4);
}
}
avgnorm[1] = 0.1;
// 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.2) - avgnorm[0]*1, 0.0,pos.Y + perp.Y*(size/2.2) - avgnorm[2]*1);
vertex[0].m_Position *= 4.0;
vertex[0].m_Position.Y = this->m_WaterHeight + 1.0;
vertex[0].m_UV[1] = 1;
vertex[0].m_UV[0] = 0;
vertex[1].m_Position = CVector3D(pos.X - perp.X*(size/2.2) - avgnorm[0]*1, 0.0,pos.Y - perp.Y*(size/2.2) - avgnorm[2]*1);
vertex[1].m_Position *= 4.0;
vertex[1].m_Position.Y = this->m_WaterHeight + 1.0;
vertex[1].m_UV[1] = 1;
vertex[1].m_UV[0] = 1;
vertex[3].m_Position = CVector3D(pos.X + perp.X*(size/2.2) + avgnorm[0]*(size/1.5), 0.0,pos.Y + perp.Y*(size/2.2) + avgnorm[2]*(size/1.5));
vertex[3].m_Position *= 4.0;
vertex[3].m_Position.Y = this->m_WaterHeight + 1.0;
vertex[3].m_UV[1] = 0;
vertex[3].m_UV[0] = 0;
vertex[2].m_Position = CVector3D(pos.X - perp.X*(size/2.2) + avgnorm[0]*(size/1.5), 0.0,pos.Y - perp.Y*(size/2.2) + avgnorm[2]*(size/1.5));
vertex[2].m_Position *= 4.0;
vertex[2].m_Position.Y = this->m_WaterHeight + 1.0;
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]);
}
// 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));
}
bool WaterManager::WillRenderSuperFancyWater()
{
return (WillRenderFancyWater() && g_Renderer.GetOptionBool(CRenderer::OPT_SUPERFANCYWATER));
}

View file

@ -26,6 +26,17 @@
#include "ps/Overlay.h"
#include "maths/Matrix3D.h"
#include "lib/ogl.h"
#include "VertexBufferManager.h"
struct SWavesVertex {
// vertex position
CVector3D m_Position;
u8 m_UV[2];
};
cassert(sizeof(SWavesVertex) == 16);
/**
* Class WaterManager: Maintain water settings and textures.
@ -33,15 +44,29 @@
* This could be extended to provide more advanced water rendering effects
* (refractive/reflective water) in the future.
*/
class WaterManager
{
public:
CTexturePtr m_WaterTexture[60];
CTexturePtr m_NormalMap[60];
CTexturePtr m_Foam;
CTexturePtr m_Wave;
u32* m_Heightmap;
GLuint m_HeightmapTexture;
GLuint m_OtherInfoTex;
ssize_t m_TexSize;
GLuint m_depthTT;
GLuint m_waveTT;
int m_WaterCurrentTex;
CColor m_WaterColor;
bool m_RenderWater;
bool m_RunningSuperFancy;
bool m_NeedsReloading;
bool m_WaterScroll;
float m_WaterHeight;
float m_WaterMaxAlpha;
@ -76,6 +101,11 @@ public:
float m_Murkiness;
CColor m_ReflectionTint;
float m_ReflectionTintStrength;
// Waves
// see the struct above.
CVertexBuffer::VBChunk* m_VBWaves;
CVertexBuffer::VBChunk* m_VBWavesIndices;
public:
WaterManager();
@ -89,18 +119,29 @@ public:
* if more textures need to be loaded and a negative error value on failure.
*/
int LoadWaterTextures();
/**
* UnloadWaterTextures: Free any loaded water textures and reset the internal state
* so that another call to LoadWaterTextures will begin progressive loading.
*/
void UnloadWaterTextures();
/**
* CreateSuperfancyInfo: creates textures and wave vertices for superfancy water
*/
void CreateSuperfancyInfo();
/**
* 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();
};