1
0
forked from 0ad/0ad

Change the water texture to a new, bigger one, created in Blender (thanks to Enrique). Change settings so that they are more logical, yet allow reverting back to an even lower quality setting. Add a WIP high setting (with foam) which isn't finished yet.

Change the shader itself so that the effects look nicer and are more
consistent across settings.
Rework the water mesh generation (simpler system). Fix a few issues.

May work oddly with Atlas since I haven't been able to compile yet.

Refs #1875 (maybe fix), Fixes #2077 (I'll assume it does), Fixes #2114
(assumption again), refs #48.

This was SVN commit r15473.
This commit is contained in:
wraitii 2014-07-01 16:05:05 +00:00
parent 0bde61fa5b
commit ab30e0d4fb
90 changed files with 924 additions and 412 deletions

View File

@ -52,13 +52,12 @@ bpp = 0
; if false, actors won't be rendered but anything entity will be.
renderactors = true
waternormals = true
waterugly=false; Force usage of the fixed pipeline water. This is faster, but really, really ugly.
waterfancyeffects = false
waterrealdepth = true
waterfoam = false
watercoastalwaves = false
waterrefraction = true
waterreflection = true
watershadows = false
shadowsonwater = false
shadows = true
shadowpcf = true

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Textures>
<File pattern="normal*" normal="true" format="rgba" mipmap="true"/>
<File pattern="diffuse*" normal="false" format="rgba" mipmap="true"/>
</Textures>

View File

@ -21,13 +21,12 @@ var options = {
[translate("Smooth LOS"), translate("Lift darkness and fog-of-war smoothly (Requires Prefer GLSL)"), {"renderer":"SmoothLOS", "config":"smoothlos"}, "boolean"],
[translate("Unit Silhouettes"), translate("Show outlines of units behind buildings"), {"renderer":"Silhouettes", "config":"silhouettes"}, "boolean"],
[translate("Shadow Filtering"), translate("Smooth shadows"), {"renderer":"ShadowPCF", "config":"shadowpcf"}, "boolean"],
[translate("HQ Waviness"), translate("Use real normals for ocean-wave rendering, instead of applying them as a flat texture"), {"renderer":"WaterNormal", "config":"waternormals"}, "boolean"],
[translate("Fast & Ugly Water"), translate("Use the lowest settings possible to render water. This makes other settings irrelevant."), {"renderer":"WaterUgly", "config":"waterugly"}, "boolean"],
[translate("HQ Water Effects"), translate("Use higher-quality effects for water, rendering coastal waves, shore foam, and ships trails."), {"renderer":"WaterFancyEffects", "config":"waterfancyeffects"}, "boolean"],
[translate("Real Water Depth"), translate("Use actual water depth in rendering calculations"), {"renderer":"WaterRealDepth", "config":"waterrealdepth"}, "boolean"],
[translate("Water Reflections"), translate("Allow water to reflect a mirror image"), {"renderer":"WaterReflection", "config":"waterreflection"}, "boolean"],
[translate("Water Refraction"), translate("Use a real water refraction map and not transparency"), {"renderer":"WaterRefraction", "config":"waterrefraction"}, "boolean"],
[translate("Shore Foam"), translate("Show foam on water near shore depending on water waviness"), {"renderer":"WaterFoam", "config":"waterfoam"}, "boolean"],
[translate("Shore Waves"), translate("Show breaking waves on water near shore (Requires HQ Waviness)"), {"renderer":"WaterCoastalWaves", "config":"watercoastalwaves"}, "boolean"],
[translate("Water Shadows"), translate("Cast shadows on water"), {"renderer":"WaterShadow", "config":"watershadows"}, "boolean"],
[translate("Shadows on Water"), translate("Cast shadows on water"), {"renderer":"WaterShadows", "config":"watershadows"}, "boolean"],
[translate("VSync"), translate("Run vertical sync to fix screen tearing. REQUIRES GAME RESTART"), {"config":"vsync"}, "boolean"],
],
"soundSetting":

View File

@ -180,7 +180,6 @@ function RunDetection(settings)
var disable_shadowpcf = undefined;
var disable_allwater = undefined;
var disable_fancywater = undefined;
var disable_fbowater = undefined;
var override_renderpath = undefined;
// TODO: add some mechanism for setting config values
@ -257,7 +256,6 @@ function RunDetection(settings)
{
disable_allwater = false;
disable_fancywater = true;
//disable_fbowater = true;
disable_shadowpcf = true;
}
@ -295,7 +293,6 @@ function RunDetection(settings)
"disable_shadowpcf": disable_shadowpcf,
"disable_allwater": disable_allwater,
"disable_fancywater": disable_fancywater,
"disable_fbowater": disable_fbowater,
"override_renderpath": override_renderpath,
};
}
@ -334,9 +331,6 @@ global.RunHardwareDetection = function(settings)
if (output.disable_fancywater !== undefined)
Engine.SetDisableFancyWater(output.disable_fancywater);
if (output.disable_fbowater !== undefined)
Engine.SetDisableFancyWater(output.disable_fbowater);
if (output.override_renderpath !== undefined)
Engine.SetRenderPath(output.override_renderpath);

View File

@ -29,7 +29,7 @@ for each (var settings in hwdetectTestData)
var os = (settings.os_linux ? "linux" : settings.os_macosx ? "macosx" : settings.os_win ? "win" : "???");
var disabled = [];
for each (var d in ["disable_audio", "disable_s3tc", "disable_shadows", "disable_shadowpcf", "disable_allwater", "disable_fbowater", "disable_fancywater", "override_renderpath"])
for each (var d in ["disable_audio", "disable_s3tc", "disable_shadows", "disable_shadowpcf", "disable_allwater", "disable_fancywater", "override_renderpath"])
if (output[d] !== undefined)
disabled.push(d+"="+output[d])

View File

@ -35,6 +35,7 @@ uniform vec3 fogColor;
uniform vec2 fogParams;
varying vec4 v_lighting;
varying vec4 v_lighting2;
varying vec2 v_tex;
varying vec2 v_los;
@ -249,9 +250,12 @@ void main()
specular.rgb = sunColor * specCol * pow(max(0.0, dot(normalize(normal), v_half)), specPow);
#endif
vec3 color = (texdiffuse * sundiffuse + specular.rgb) * get_shadow();
vec3 ambColor = texdiffuse * ambient;
float shadow = get_shadow();
vec3 color = (texdiffuse * sundiffuse + specular.rgb) * shadow;
vec3 ambColor = texdiffuse * (ambient + v_lighting2.rgb*(shadow+0.8)*0.5);
//ambColor = texdiffuse * ambient;
#if (USE_INSTANCING || USE_GPU_SKINNING) && USE_AO
vec3 ao = texture2D(aoTex, v_tex2).rrr;
ao = mix(vec3(1.0), ao * 2.0, effectSettings.w);
@ -273,6 +277,8 @@ void main()
#endif
color *= shadingColor;
//color = v_lighting2.rgb;
gl_FragColor.rgb = color;
}

View File

@ -23,6 +23,7 @@ uniform mat4 instancingTransform;
#endif
varying vec4 v_lighting;
varying vec4 v_lighting2;
varying vec2 v_tex;
varying vec2 v_los;
@ -74,6 +75,8 @@ vec4 fakeCos(vec4 x)
void main()
{
// Compute position, normal and tangent vertices.
// GPU skinning does the animation live on the GPU.
#if USE_GPU_SKINNING
vec3 p = vec3(0.0);
vec3 n = vec3(0.0);
@ -105,7 +108,7 @@ void main()
#endif
#endif
// Calculate swaying of trees dynamically
#if USE_WIND
vec2 wind = windData.xy;
@ -139,7 +142,6 @@ void main()
position.xz += diff + diff2 * wind;
#endif
gl_Position = transform * position;
#if USE_SPECULAR || USE_NORMAL_MAP || USE_SPECULAR_MAP || USE_PARALLAX_MAP
@ -165,7 +167,12 @@ void main()
#endif
#endif
v_lighting.xyz = max(0.0, dot(normal, -sunDir)) * sunColor;
float firstDot = dot(normal, -sunDir);
v_lighting2.xyz = max(0.0, (dot(normal, vec3(-sunDir.r,sunDir.g,-sunDir.b)) + 1.0) - abs(firstDot)*0.8 - max(0.0,firstDot)*1.9) * 0.4 * sunColor;
v_lighting2 -= a_vertex.y*0.1;
v_lighting2 = clamp(vec4(0.3), vec4(0.0), v_lighting2);
v_lighting.xyz = max(0.0, firstDot) * sunColor;
v_tex = a_uv0;
@ -177,7 +184,7 @@ void main()
v_shadow = shadowTransform * position;
#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
v_shadow.xy *= shadowScale.xy;
#endif
#endif
#endif
v_los = position.xz * losTransform.x + losTransform.y;

View File

@ -0,0 +1,52 @@
#version 110
// This is a lightened version of water_high.fs that generates only the "n" vector (ie the normal) and the foam coverage.
// It's thus uncommented.
uniform float waviness;
uniform vec2 screenSize;
uniform float time;
varying vec3 worldPos;
uniform float mapSize;
uniform sampler2D normalMap;
uniform sampler2D normalMap2;
/*#if USE_FOAM || USE_WAVES
uniform sampler2D Foam;
uniform sampler2D waveTex;
#endif*/
void main()
{
float wavyFactor = waviness * 0.125;
vec3 ww1 = texture2D(normalMap, (gl_TexCoord[0].st) * (0.7 - waviness/20.0)).xzy;
vec3 ww2 = texture2D(normalMap2, (gl_TexCoord[0].st) * (0.7 - waviness/20.0)).xzy;
//vec3 smallWW = texture2D(normalMap, (gl_TexCoord[0].st - gl_TexCoord[0].wz*6.0) * 1.2).xzy;
//vec3 smallWW2 = texture2D(normalMap2, (gl_TexCoord[0].st - gl_TexCoord[0].wz*6.0) * 1.2).xzy;
ww1 = mix(ww1, ww2, mod(time * 60.0, 8.0) / 8.0);
//smallWW = mix(smallWW, smallWW2, mod(time * 60.0, 8.0) / 8.0) - vec3(0.5);
//ww += vec3(smallWW.x,0.0,smallWW.z)*0.5;
/*#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*/
vec3 n = normalize(ww1 - vec3(0.5, 0.5, 0.5));
n = mix(vec3(0.0,1.0,0.0),n,waviness/10.0);
float foamFact1 = texture2D(normalMap, (gl_TexCoord[0].st) * 0.3).a;
float foamFact2 = texture2D(normalMap2, (gl_TexCoord[0].st) * 0.3).a;
float foamFact = mix(foamFact1, foamFact2, mod(time * 60.0, 8.0) / 8.0);
float foamUniversal1 = texture2D(normalMap, (gl_TexCoord[0].st) * 0.05).a;
float foamUniversal2 = texture2D(normalMap2, (gl_TexCoord[0].st) * 0.05).a;
float foamUniversal = mix(foamUniversal1, foamUniversal2, mod(time * 60.0, 8.0) / 8.0);
gl_FragColor.rgba = vec4(n,foamFact*foamUniversal*30.0);
}

View File

@ -0,0 +1,22 @@
#version 110
// This is a lightened version of water_high.vs
uniform float repeatScale;
uniform vec2 translation;
uniform float time;
uniform float mapSize;
varying vec3 worldPos;
attribute vec3 a_vertex;
void main()
{
worldPos = a_vertex;
gl_TexCoord[0] = vec4(a_vertex.xz*repeatScale,translation);
gl_TexCoord[3].zw = vec2(a_vertex.xz)/mapSize;
gl_Position = gl_ModelViewProjectionMatrix * vec4(a_vertex, 1.0);
}

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="glsl">
<vertex file="glsl/waves.vs">
<vertex file="glsl/water_effects.vs">
<stream name="pos"/>
<stream name="uv0"/>
<stream name="color"/>
<attrib name="a_vertex" semantics="gl_Vertex"/>
<attrib name="a_uv0" semantics="gl_MultiTexCoord0"/>
</vertex>
<fragment file="glsl/waves.fs"/>
<fragment file="glsl/water_effects.fs"/>
</program>

View File

@ -23,11 +23,17 @@ varying vec3 worldPos;
varying float waterDepth;
varying vec4 waterInfo;
uniform float mapSize;
uniform samplerCube skyCube;
uniform sampler2D normalMap;
uniform sampler2D normalMap2;
#if USE_FANCY_EFFECTS
uniform sampler2D waterEffectsTex;
#endif
#if USE_REFLECTION
uniform sampler2D reflectionMap;
#endif
@ -37,11 +43,13 @@ uniform sampler2D normalMap2;
#if USE_REAL_DEPTH
uniform sampler2D depthTex;
#endif
#if USE_FOAM || USE_WAVES
/*#if USE_FOAM || USE_WAVES
uniform sampler2D Foam;
uniform sampler2D waveTex;
#endif
#if USE_SHADOWS && USE_SHADOW
#endif*/
#if USE_SHADOWS_ON_WATER && USE_SHADOW
varying vec4 v_shadow;
#if USE_SHADOW_SAMPLER
uniform sampler2DShadow shadowTex;
@ -53,7 +61,7 @@ uniform sampler2D normalMap2;
#endif
float get_shadow(vec4 coords)
{
#if USE_SHADOWS && !DISABLE_RECEIVE_SHADOWS
#if USE_SHADOWS_ON_WATER && !DISABLE_RECEIVE_SHADOWS
#if USE_SHADOW_SAMPLER
#if USE_SHADOW_PCF
vec2 offset = fract(coords.xy - 0.5);
@ -78,6 +86,31 @@ uniform sampler2D normalMap2;
}
#endif
// TODO: convert this to something not only for AABBs
struct Ray {
vec3 Origin;
vec3 Direction;
};
float IntersectBox (in Ray ray, in vec3 minimum, in vec3 maximum)
{
vec3 OMIN = ( minimum - ray.Origin ) / ray.Direction;
vec3 OMAX = ( maximum - ray.Origin ) / ray.Direction;
vec3 MAX = max ( OMAX, OMIN );
return min ( MAX.x, min ( MAX.y, MAX.z ) );
}
// This is sorta useless since it's hardcoded for 45°. Change it to sun orientation too.
mat3 rotationMatrix()
{
vec3 axis = vec3(0.0,1.0,0.0);
float s = sin(1.12);
float c = cos(1.12);
return mat3(c, 0.0, s,
0.0, 1.0, 0.0,
-s, 0.0, c);
}
vec3 get_fog(vec3 color)
{
float density = fogParams.x;
@ -96,50 +129,63 @@ vec3 get_fog(vec3 color)
void main()
{
#if USE_FOAM || USE_WAVES
vec4 heightmapval = waterInfo;
vec2 beachOrientation = heightmapval.rg;
float distToShore = heightmapval.b;
#endif
vec3 n, l, h, v; // Normal, light vector, half-vector and view vector (vector to eye)
float ndotl, ndoth, ndotv;
//float fwaviness = 1;
float fresnel;
float t; // Temporary variable
vec2 reflCoords, refrCoords;
vec3 reflColor, refrColor, specular;
float losMod;
// Correct the waviness (range [0,10]) to something slightly more convenient (range [0,1.2] so you can treat it like [0.1] with overdraft).
float wavyFactor = waviness * 0.125;
l = -sunDir;
v = normalize(cameraPos - worldPos);
h = normalize(l + v);
// 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_NORMALS
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 l = -sunDir;
vec3 v = normalize(cameraPos - worldPos);
vec3 h = normalize(l + v);
#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
ndoth = dot( mix(vec3(0.0,1.0,0.0),n,clamp(wavyFactor * v.y * 8.0,0.05,1.0)) ,h);
n = mix(vec3(0.0,1.0,0.0),n,wavyFactor);
#else
ndoth = dot(vec3(0.0,1.0,0.0), h);
n = vec3(0.0,1.0,0.0);
#endif
// Calculate water normals.
ndotl = (dot(n, l) + 1.0)/2.0;
ndotv = clamp(dot(n, v),0.0,1.0);
#if USE_FANCY_EFFECTS
vec4 fancyeffects = texture2D(waterEffectsTex, gl_FragCoord.xy/screenSize);
vec3 n = fancyeffects.rgb;
#else
// This method uses 60 animated water frames. We're blending between each two frames
// TODO: could probably have fewer frames thanks to this blending.
// Scale the normal textures by waviness so that big waviness means bigger waves.
vec3 ww1 = texture2D(normalMap, (gl_TexCoord[0].st) * (0.7 - waviness/20.0)).xzy;
vec3 ww2 = texture2D(normalMap2, (gl_TexCoord[0].st) * (0.7 - waviness/20.0)).xzy;
//vec3 smallWW = texture2D(normalMap, (gl_TexCoord[0].st - gl_TexCoord[0].wz*6.0) * 1.2).xzy;
//vec3 smallWW2 = texture2D(normalMap2, (gl_TexCoord[0].st - gl_TexCoord[0].wz*6.0) * 1.2).xzy;
ww1 = mix(ww1, ww2, mod(time * 60.0, 8.0) / 8.0);
//smallWW = mix(smallWW, smallWW2, mod(time * 60.0, 8.0) / 8.0) - vec3(0.5);
//ww += vec3(smallWW.x,0.0,smallWW.z)*0.5;
/*#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*/
// Fix our normals.
vec3 n = normalize(ww1 - vec3(0.5, 0.5, 0.5));
// Flatten them based on waviness.
n = mix(vec3(0.0,1.0,0.0),n,waviness/10.0);
#endif
// simulates how parallel the "point->sun", "view->point" vectors are.
// To always have a bit of that, we don't use the "n" we calculated above.
float ndoth = dot( mix(vec3(0.0,1.0,0.0),n,0.1 + min(0.8,waviness*waviness/70.0)) , h);
// how perpendicular to the normal our view is. Used for fresnel.
float ndotv = clamp(dot(n, v),0.0,1.0);
// diffuse lighting-like. used for shadows?
float ndotl = (dot(n, l) + 1.0)/2.0;
float depth;
#if USE_REAL_DEPTH
// Don't change these two. They should match the values in the config (TODO: dec uniforms).
float zNear = 2.0;
@ -167,17 +213,25 @@ void main()
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/3.0,0.0,7.0);
depth = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer);
#else
float perceivedDepth = waterDepth / (v.y*v.y);
float distoFactor = clamp(perceivedDepth/4.0,0.0,7.0);
depth = waterDepth / (min(0.5,v.y)*1.5*min(0.5,v.y)*2.0);
#endif
fresnel = pow(1.05 - ndotv, 1.3333); // approximation. I'm using 1.05 and not 1.0 because it causes artifacts, see #1714
// Fresnel for "how much reflection vs how much refraction".
// Since we're not trying to simulate a realistic ocean 100%, aim for something that gives a little too much reflection
// because we're not used to seeing the see from above.
fresnel = clamp(pow(1.05 - ndotv, 1.3),0.0,0.8); // approximation. I'm using 1.05 and not 1.0 because it causes artifacts, see #1714
// multiply by v.y so that in the distance refraction wins.
// TODO: this is a hack because reflections don't work in the distance.
fresnel *= min(1.0,log(1.0 + v.y*5.0));
fresnel = 0.2 + fresnel * 0.8;
#if USE_FOAM
//gl_FragColor = vec4(fresnel,fresnel,fresnel,1.0);
//return;
/*#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.
@ -196,120 +250,115 @@ void main()
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
#endif*/
#if USE_SHADOWS && USE_SHADOW
#if USE_SHADOWS_ON_WATER && USE_SHADOW
float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw));
#endif
// for refraction, we want to adjust the value by v.y slightly otherwise it gets too different between "from above" and "from the sides".
// And it looks weird (again, we are not used to seeing water from above).
float fixedVy = clamp(v.y,0.1,1.0);
float distoFactor = clamp(depth/2.0,0.0,7.0);
float murky = mix(200.0,0.1,pow(murkiness,0.25));
#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);
#if USE_SHADOWS && USE_SHADOW
refrColor = (0.5 + 0.5*ndotl) * mix(color * (0.5 + shadow/2.0),mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#else
refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#endif
refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * distoFactor*10.0) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords
vec3 refColor = texture2D(refractionMap, refrCoords).rgb;
// TODO: make murkiness (both types rematter on that.
// linearly extinct the water. This is how quickly we see nothing but the pure water color
float extFact = max(0.0,1.0 - (depth*fixedVy/murky));
// This is how tinted the water is, ie how quickly the refracted floor takes the tint of the water
float ColextFact = max(0.0,1.0 - (depth*fixedVy/murky));
vec3 colll = mix(refColor*tint,refColor,ColextFact);
#if USE_SHADOWS_ON_WATER && USE_SHADOW
// TODO:
refrColor = mix(color, colll, extFact);
#else
refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * distoFactor) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords
// cleverly get the perceived depth based on camera tilting (if horizontal, it's likely we will have more water to look at).
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);
#if USE_SHADOWS && USE_SHADOW
refrColor = (0.5 + 0.5*ndotl) * mix(color * (0.5 + shadow/2.0),mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#else
refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#endif
refrColor = mix(color, colll, extFact);
#endif
#else
float alphaCoeff = 0.0;
#if USE_REAL_DEPTH
float luminance = clamp((waterDepth2/mix(150.0,2.0, pow(murkiness,0.2) )), 0.0, 1.0);
alphaCoeff = mix(mix(0.0,3.0 - (tint.r + tint.g + tint.b),clamp(waterDepth2*murkiness/5.0,0.0,1.0)),1.0,luminance*luminance);
#else
float luminance = clamp(((waterDepth / v.y)/mix(150.0,2.0, pow(murkiness,0.2) )), 0.0, 1.0);
alphaCoeff = mix(mix(0.0,3.0 - (tint.r + tint.g + tint.b),clamp(perceivedDepth*murkiness/5.0,0.0,1.0)),1.0,luminance*luminance);
#endif
// linearly extinct the water. This is how quickly we see nothing but the pure water color
float extFact = max(0.0,1.0 - (depth*fixedVy/20.0));
// using both those factors, get our transparency.
// This will be our base transparency on top.
float base = 0.4 + depth*fixedVy/15.0; // TODO: murkiness.
float alphaCoeff = mix(1.0, base, extFact);
refrColor = color;
#endif
#if !USE_NORMALS
// we're not using normals. Simulate by applying a B&W effect.
refrColor *= (ww*2.0).x;
#endif
#if USE_REFLECTION
reflCoords = clamp( (0.5*gl_TexCoord[1].xy + distoFactor*1.5*n.xz) / gl_TexCoord[1].w + 0.5,0.0,1.0); // Unbias texture coords
reflCoords = clamp( (0.5*gl_TexCoord[1].xy - waviness * 2.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);
// TODO: At very low angles the reflection stuff doesn't really work any more:
// IRL you would get a blur of the sky, but we don't have that precision (would require mad oversampling)
// So tend towards a predefined color (per-map) which looks like what the skybox would look like if you really blurred it.
// The TODO here would be to precompute a band (1x32?) that represents the average color around the map.
// TODO: another issue is that at high distances (half map) the texture blurs into flatness. Using better mipmaps won't really solve it
// So we'll need to stop showing reflections and default to sky color there too.
// Unless maybe waviness is so low that you would see like in a mirror anyways.
//float disttt = distance(worldPos,cameraPos);
//reflColor = mix(vec3(0.5,0.5,0.55), reflColor, clamp(1.0-disttt/600.0*disttt/600.0,0.0,1.0));//clamp(-0.05 + v.y*20.0,0.0,1.0));
#else
vec3 eye = reflect(v, mix(vec3(0.0,1.0,0.0),n,0.2));
vec3 tex = textureCube(skyCube, eye).rgb;
vec3 eye = reflect(v,n);
eye.y = min(-0.1,eye.y);
// let's calculate where we intersect with the skycube.
Ray myRay = Ray(vec3(worldPos.x/4.0,worldPos.y,worldPos.z/4.0),eye);
vec3 start = vec3(-1500.0 + mapSize/2.0,-100.0,-1500.0 + mapSize/2.0);
vec3 end = vec3(1500.0 + mapSize/2.0,500.0,1500.0 + mapSize/2.0);
float tmin = IntersectBox(myRay,start,end);
vec3 newpos = vec3(-worldPos.x/4.0,worldPos.y,-worldPos.z/4.0) + eye * tmin - vec3(-mapSize/2.0,worldPos.y,-mapSize/2.0);
//newpos = normalize(newpos);
newpos.y *= 6.0;
newpos *= rotationMatrix();
vec3 tex = textureCube(skyCube, newpos).rgb;
//float disttt = distance(worldPos,cameraPos);
//tex = mix(tex,vec3(0.7,0.7,0.9),clamp(disttt/300.0*disttt/300.0*disttt/300.0,0.0,0.9));
//gl_FragColor = vec4(clamp(disttt/300.0*disttt/300.0,0.0,1.0),clamp(disttt/300.0*disttt/300.0,0.0,1.0),clamp(disttt/300.0*disttt/300.0,0.0,1.0),1.0);
//return;
reflColor = mix(tex, sunColor * reflectionTint, reflectionTintStrength);
#endif
#if USE_NORMALS
specular = pow(ndoth, mix(100.0,450.0, v.y*2.0)) * sunColor * 1.5;
#else
specular = pow(ndoth, mix(100.0,450.0, v.y*2.0)) * sunColor * 1.5 * ww.r;
#endif
// Specular.
specular = pow(ndoth, mix(100.0,450.0, v.y*2.0))*sunColor * 1.5;// * sunColor * 1.5 * ww.r;
losMod = texture2D(losMap, gl_TexCoord[3].st).a;
losMod = losMod < 0.03 ? 0.0 : losMod;
vec3 colour;
#if USE_SHADOWS && USE_SHADOW
#if USE_SHADOWS_ON_WATER && USE_SHADOW
float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + murkiness*0.2);
#if USE_FOAM
/* #if USE_FOAM
colour = mix(refrColor, reflColor, fresShadow) + max(ndotl,0.4)*(finalFoam)*(shadow/2.0 + 0.5);
#else
colour = mix(refrColor, reflColor, fresShadow);
#endif
#else*/
colour = mix(refrColor, reflColor, fresShadow);
#else
#if USE_FOAM
/*#if USE_FOAM
colour = mix(refrColor, reflColor, fresnel) + max(ndotl,0.4)*(finalFoam);
#else
colour = mix(refrColor, reflColor, fresnel);
#endif
#else*/
colour = mix(refrColor, reflColor, fresnel);
#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
#endif
#if USE_SHADOWS && USE_SHADOW
#if USE_SHADOWS_ON_WATER && USE_SHADOW
colour += shadow*specular;
#else
colour += specular;
#endif
gl_FragColor.rgb = get_fog(colour) * 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;
// TODO: work the foam in somewhere else.
#if USE_FANCY_EFFECTS
gl_FragColor.rgb = get_fog(colour) * losMod + fancyeffects.a * losMod;
#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 = 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
gl_FragColor.rgb = get_fog(colour) * losMod;
#endif
#if !USE_REFRACTION
gl_FragColor.a = clamp(depth*2.0,0.0,1.0) * alphaCoeff;
#else
gl_FragColor.a = clamp(depth*2.0,0.0,1.0);
#endif
}

View File

@ -18,7 +18,8 @@ uniform float mapSize;
varying vec3 worldPos;
varying float waterDepth;
varying vec4 waterInfo;
#if USE_SHADOW && USE_SHADOWS
#if USE_SHADOW && USE_SHADOWS_ON_WATER
varying vec4 v_shadow;
#endif
attribute vec3 a_vertex;
@ -37,7 +38,7 @@ void main()
gl_TexCoord[3].zw = vec2(a_vertex.xz)/mapSize;
#if USE_SHADOW && USE_SHADOWS
#if USE_SHADOW && USE_SHADOWS_ON_WATER
v_shadow = shadowTransform * vec4(a_vertex, 1.0);
#if USE_SHADOW_SAMPLER && USE_SHADOW_PCF
v_shadow.xy *= shadowScale.xy;

View File

@ -1,24 +0,0 @@
#version 110
uniform sampler2D waveTex;
uniform sampler2D infoTex;
uniform float time;
uniform float waviness;
void main()
{
vec3 color = texture2D(waveTex, gl_TexCoord[0].st * vec2(2.0,4.0) - vec2(0.0,0.5 + time/5.0)).rgb;
float split = abs(gl_TexCoord[0].x - 0.5);
split = 0.48 - split;
split *= 3.0;
split = min(1.0,split);
float opac = split*min(1.0, gl_TexCoord[0].y);
opac *= 1.0 - max(0.0,gl_TexCoord[0].y-0.9)*10.0;
color = mix(vec3(0.5,0.5,1.0),color, opac);
gl_FragColor.rgb = mix(vec3(0.5,0.5,1.0), color, clamp(texture2D(infoTex,gl_TexCoord[0].zw).r,0.4,1.0));
gl_FragColor.a = 1.0;
}

View File

@ -1,13 +0,0 @@
#version 110
attribute vec3 a_vertex;
attribute vec2 a_uv0;
uniform float mapSize;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * vec4(a_vertex, 1.0);
gl_TexCoord[0].st = a_uv0;
gl_TexCoord[0].zw = vec2(a_vertex.xz)/mapSize;
}

View File

@ -1 +1 @@
L"custom build"
L"-release"

View File

@ -50,7 +50,7 @@ X(MODE_WIREFRAME)
X(SYS_HAS_ARB)
X(SYS_HAS_GLSL)
X(SYS_PREFER_GLSL)
X(USE_FOAM)
X(USE_FANCY_EFFECTS)
X(USE_FP_SHADOW)
X(USE_GPU_SKINNING)
X(USE_INSTANCING)
@ -60,10 +60,9 @@ X(USE_REAL_DEPTH)
X(USE_REFLECTION)
X(USE_REFRACTION)
X(USE_SHADOW)
X(USE_SHADOWS)
X(USE_SHADOWS_ON_WATER)
X(USE_SHADOW_PCF)
X(USE_SHADOW_SAMPLER)
X(USE_WAVES)
X2(_emptystring, "")
X(a_skinJoints)
X(a_skinWeights)
@ -142,7 +141,7 @@ X(tint)
X(transform)
X(translation)
X(waterTex)
X(waveTex)
X(waterEffectsTex)
X(waviness)
X(width)
X(zFar)

View File

@ -41,10 +41,9 @@ bool g_RenderActors = true;
bool g_Shadows = false;
bool g_ShadowPCF = false;
bool g_WaterNormal = false;
bool g_WaterUgly = false;
bool g_WaterFancyEffects = 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;
@ -94,15 +93,12 @@ static void LoadGlobals()
CFG_GET_VAL("shadows", Bool, g_Shadows);
CFG_GET_VAL("shadowpcf", Bool, g_ShadowPCF);
CFG_GET_VAL("waternormals",Bool, g_WaterNormal);
CFG_GET_VAL("waterugly",Bool, g_WaterUgly);
CFG_GET_VAL("waterfancyeffects",Bool, g_WaterFancyEffects);
CFG_GET_VAL("waterrealdepth",Bool, g_WaterRealDepth);
CFG_GET_VAL("waterfoam",Bool, g_WaterFoam);
CFG_GET_VAL("watercoastalwaves",Bool, g_WaterCoastalWaves);
if (g_WaterCoastalWaves && !g_WaterNormal)
g_WaterCoastalWaves = false;
CFG_GET_VAL("waterrefraction",Bool, g_WaterRefraction);
CFG_GET_VAL("waterreflection",Bool, g_WaterReflection);
CFG_GET_VAL("watershadows",Bool, g_WaterShadows);
CFG_GET_VAL("shadowsonwater",Bool, g_WaterShadows);
CFG_GET_VAL("renderpath", String, g_RenderPath);
CFG_GET_VAL("particles", Bool, g_Particles);

View File

@ -50,14 +50,12 @@ extern bool g_RenderActors;
// flag to switch on shadows
extern bool g_Shadows;
// Use real normals for ocean-wave rendering, instead of applying them as a flat texture.
extern bool g_WaterNormal;
// Force the use of the fixed function for rendering water.
extern bool g_WaterUgly;
// Add foam and waves near the shores, trails following ships, and other HQ things.
extern bool g_WaterFancyEffects;
// 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.

View File

@ -591,13 +591,12 @@ static void InitRenderer()
g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO, g_NoGLVBO);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, g_Shadows);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERNORMAL, g_WaterNormal);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERUGLY, g_WaterUgly);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS, g_WaterFancyEffects);
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_WaterReflection);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERREFRACTION, g_WaterRefraction);
g_Renderer.SetOptionBool(CRenderer::OPT_WATERSHADOW, g_WaterShadows);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWSONWATER, g_WaterShadows);
g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWPCF, g_ShadowPCF);

View File

@ -147,39 +147,28 @@ void SetDisableShadowPCF(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), bool di
void SetDisableAllWater(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), bool disabled)
{
if (!IsOverridden("waternormals"))
g_WaterNormal = !disabled;
g_WaterUgly = true;
if (!IsOverridden("waterfancyeffects"))
g_WaterFancyEffects = !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"))
if (!IsOverridden("shadowsonwater"))
g_WaterShadows = !disabled;
}
void SetDisableFancyWater(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), bool disabled)
{
if (!IsOverridden("waterfancyeffects"))
g_WaterFancyEffects = !disabled;
if (!IsOverridden("waterrealdepth"))
g_WaterRealDepth = !disabled;
if (!IsOverridden("waterfoam"))
g_WaterFoam = !disabled;
if (!IsOverridden("watercoastalwaves"))
g_WaterCoastalWaves = !disabled;
if (!IsOverridden("watershadows"))
if (!IsOverridden("shadowsonwater"))
g_WaterShadows = !disabled;
}
void SetDisableFBOWater(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), bool disabled)
{
if (!IsOverridden("waterfoam"))
g_WaterFoam = !disabled;
if (!IsOverridden("watercoastalwaves"))
g_WaterCoastalWaves = !disabled;
}
void SetRenderPath(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::string renderpath)
{
@ -198,7 +187,6 @@ void RunHardwareDetection()
scriptInterface.RegisterFunction<void, bool, &SetDisableShadowPCF>("SetDisableShadowPCF");
scriptInterface.RegisterFunction<void, bool, &SetDisableAllWater>("SetDisableAllWater");
scriptInterface.RegisterFunction<void, bool, &SetDisableFancyWater>("SetDisableFancyWater");
scriptInterface.RegisterFunction<void, bool, &SetDisableFBOWater>("SetDisableFBOWater");
scriptInterface.RegisterFunction<void, std::string, &SetRenderPath>("SetRenderPath");
// Load the detection script:

View File

@ -1305,31 +1305,168 @@ void CPatchRData::BuildWater()
// TODO: This is not (yet) exported via the ICmp interface so... we stick to these values which can be compiled in defaults
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
if (WaterMgr->m_NeedInfoUpdate)
/*if (WaterMgr->m_NeedInfoUpdate)
{
WaterMgr->m_NeedInfoUpdate = false;
WaterMgr->CreateSuperfancyInfo(m_Simulation);
}
}*/
CPatch* patch = m_Patch;
CTerrain* terrain = patch->m_Parent;
ssize_t mapSize = (size_t)terrain->GetVerticesPerSide();
//Top-left coordinates of our patch.
ssize_t x1 = m_Patch->m_X*PATCH_SIZE;
ssize_t z1 = m_Patch->m_Z*PATCH_SIZE;
// to whoever implements different water heights, this is a TODO: water height)
float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
int moves[4][2] = { {0,0}, {water_cell_size,0}, {0,water_cell_size}, {water_cell_size,water_cell_size} };
// build vertices, uv, and shader varying
for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
{
for (ssize_t x = 0; x <= PATCH_SIZE; x += water_cell_size)
for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
{
// Check that the edge at x is partially underwater
float startTerrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
float startWaterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
if (startTerrainHeight[0] >= startWaterHeight[0] && startTerrainHeight[1] >= startWaterHeight[1])
// Check that this tile has at least one vertice underwater.
if (terrain->GetVertexGroundLevel(x+x1, z+z1) >= waterHeight
&& terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) >= waterHeight
&& terrain->GetVertexGroundLevel(x+x1 + water_cell_size, z+z1) >= waterHeight
&& terrain->GetVertexGroundLevel(x+x1 + water_cell_size, z+z1 + water_cell_size) >= waterHeight)
continue;
// This is actually lying and I should call CcmpTerrain
/*if (!terrain->IsOnMap(x+x1, z+z1)
&& !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
&& !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
&& !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
continue;*/
// Figure out our points. We might want to draw only one of two triangles per tile.
// In order: the one on the diagonal on top, the one of the diagonal on bottom, other on top, other on bottom.
// The numbers refer to "moves" above. -1 means "not rendering".
// by default the diagonal is the "x,z->x+1,z+1" one.
int points[4] = { 0, 3, 1, 2 };
// If the other diagonal is over water completely, that means we have the wrong diagonal.
if (terrain->GetVertexGroundLevel(x+x1 + water_cell_size, z+z1) >= waterHeight
&& terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) >= waterHeight)
{
points[0] = 1; points[1] = 2; points[2] = 0; points[3] = 3;
}
// check if the diagonal is completely out of the water, and if so, check which triangles we want to render (1 or 2)
if (terrain->GetVertexGroundLevel(x+x1 + moves[points[0]][0], z+z1 + moves[points[0]][1]) >= waterHeight
&& terrain->GetVertexGroundLevel(x+x1 + moves[points[1]][0], z+z1 + moves[points[1]][1]) >= waterHeight)
{
if (terrain->GetVertexGroundLevel(x+x1 + moves[points[2]][0], z+z1 + moves[points[2]][1]) >= waterHeight)
points[2] = -1;
else if (terrain->GetVertexGroundLevel(x+x1 + moves[points[3]][0], z+z1 + moves[points[3]][1]) >= waterHeight)
points[3] = -1;
}
// Compute data.
for (int i = 0; i < 4; ++i)
{
if (points[i] == -1)
continue;
// Check if we already computed this vertex from an earlier strip
if (water_index_map[z+moves[points[i]][1]][x+moves[points[i]][0]] != 0xFFFF)
continue;
ssize_t zz = z+z1+moves[points[i]][1];
ssize_t xx = x+x1+moves[points[i]][0];
SWaterVertex vertex;
terrain->CalcPosition(xx,zz, vertex.m_Position);
float depth = waterHeight - vertex.m_Position.Y;
// Try and get the point on the shore if it's over water.
// In some cases this won't be possible.
if (depth < 0.0f)
{
float temp = 0.0f;
float left_t = 0.0f,right_t = 0.0f,top_t = 0.0f,bottom_t = 0.0f;
if (xx > 0)
{
temp = terrain->GetVertexGroundLevel(xx-1,zz);
if (temp < waterHeight)
left_t = 1.0f-(temp-waterHeight)/(temp-vertex.m_Position.Y);
}
if (xx < mapSize - 1)
{
temp = terrain->GetVertexGroundLevel(xx+1,zz);
if (temp < waterHeight)
right_t = 1.0f-(temp-waterHeight)/(temp-vertex.m_Position.Y);
}
if (zz > 0)
{
temp = terrain->GetVertexGroundLevel(xx,zz-1);
if (temp < waterHeight)
top_t = 1.0f-(temp-waterHeight)/(temp-vertex.m_Position.Y);
}
if (zz < mapSize - 1)
{
temp = terrain->GetVertexGroundLevel(xx,zz-1);
if (temp < waterHeight)
bottom_t = 1.0f-(temp-waterHeight)/(temp-vertex.m_Position.Y);
}
vertex.m_Position.X = vertex.m_Position.X + 4.0 * (right_t - left_t);
vertex.m_Position.Z = vertex.m_Position.Z + 4.0 * (bottom_t - top_t);
}
vertex.m_Position.Y = waterHeight;
m_WaterBounds += vertex.m_Position;
// faking fresnel for simplest water.
float alpha = clamp(depth / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterMaxAlpha);
// Split the depth data across 24 bits, so the fancy-water shader can reconstruct
// the depth value while the simple-water can just use the precomputed alpha
float depthInt = floor(depth);
float depthFrac = depth - depthInt;
vertex.m_DepthData = SColor4ub(u8(clamp(depthInt, 0.0f, 255.0f)),
u8(clamp(-depthInt, 0.0f, 255.0f)),
u8(clamp(depthFrac*255.0f, 0.0f, 255.0f)),
u8(clamp(alpha*255.0f, 0.0f, 255.0f)));
// Move x back one cell (unless at start of patch), then scan rightwards
vertex.m_WaterData = CVector4D(WaterMgr->m_BlurredNormalMap[xx + zz*mapSize].X,
WaterMgr->m_BlurredNormalMap[xx + zz*mapSize].Z,
WaterMgr->m_DistanceHeightmap[xx + zz*mapSize],
0.0f);
water_index_map[z+moves[points[i]][1]][x+moves[points[i]][0]] = water_vertex_data.size();
water_vertex_data.push_back(vertex);
}
// Render them.
if (points[2] == 0) // Top point is wanted, render corresponding triangle
{
water_indices.push_back(water_index_map[z + moves[points[1]][1]][x + moves[points[1]][0]]);
water_indices.push_back(water_index_map[z + moves[points[2]][1]][x + moves[points[2]][0]]);
water_indices.push_back(water_index_map[z + moves[points[0]][1]][x + moves[points[0]][0]]);
}
else if (points[2] == 1)
{
water_indices.push_back(water_index_map[z + moves[points[1]][1]][x + moves[points[1]][0]]);
water_indices.push_back(water_index_map[z + moves[points[0]][1]][x + moves[points[0]][0]]);
water_indices.push_back(water_index_map[z + moves[points[2]][1]][x + moves[points[2]][0]]);
}
if (points[3] == 3) // Bottom point is wanted, render corresponding triangle.
{
water_indices.push_back(water_index_map[z + moves[points[1]][1]][x + moves[points[1]][0]]);
water_indices.push_back(water_index_map[z + moves[points[0]][1]][x + moves[points[0]][0]]);
water_indices.push_back(water_index_map[z + moves[points[3]][1]][x + moves[points[3]][0]]);
}
else if (points[3] == 2)
{
water_indices.push_back(water_index_map[z + moves[points[1]][1]][x + moves[points[1]][0]]);
water_indices.push_back(water_index_map[z + moves[points[3]][1]][x + moves[points[3]][0]]);
water_indices.push_back(water_index_map[z + moves[points[0]][1]][x + moves[points[0]][0]]);
}
/*// Move x back one cell (unless at start of patch), then scan rightwards.
bool belowWater = true;
ssize_t stripStart;
for (stripStart = x = std::max(x-water_cell_size, (ssize_t)0); x <= PATCH_SIZE; x += water_cell_size)
@ -1337,9 +1474,7 @@ void CPatchRData::BuildWater()
// If this edge is not underwater, and neither is the previous edge
// (i.e. belowWater == false), then stop this strip since we've reached
// a cell that's entirely above water
float terrainHeight[2] = { terrain->GetVertexGroundLevel(x+x1, z+z1), terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) };
float waterHeight[2] = { cmpWaterManager->GetExactWaterLevel(x+x1, z+z1), cmpWaterManager->GetExactWaterLevel(x+x1, z+z1 + water_cell_size) };
if (terrainHeight[0] >= waterHeight[0] && terrainHeight[1] >= waterHeight[1])
if (terrain->GetVertexGroundLevel(x+x1, z+z1) >= waterHeight && terrain->GetVertexGroundLevel(x+x1, z+z1 + water_cell_size) >= waterHeight)
{
if (!belowWater)
break;
@ -1404,7 +1539,7 @@ void CPatchRData::BuildWater()
water_indices.push_back(water_index_map[z + water_cell_size][x]);
water_indices.push_back(water_index_map[z][x - water_cell_size]);
water_indices.push_back(water_index_map[z][x]);
}
}*/
}
}
@ -1430,6 +1565,8 @@ void CPatchRData::RenderWater(CShaderProgramPtr& shader)
SWaterVertex *base=(SWaterVertex *)m_VBWater->m_Owner->Bind();
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// setup data pointers
GLsizei stride = sizeof(SWaterVertex);
shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &base[m_VBWater->m_Index].m_DepthData);
@ -1445,6 +1582,8 @@ void CPatchRData::RenderWater(CShaderProgramPtr& shader)
glDrawElements(GL_TRIANGLES, (GLsizei) m_VBWaterIndices->m_Count,
GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_VBWaterIndices->m_Index));
}
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;

View File

@ -667,26 +667,23 @@ void CRenderer::SetOptionBool(enum Option opt,bool value)
m_Options.m_Shadows = value;
MakeShadersDirty();
break;
case OPT_WATERNORMAL:
m_Options.m_WaterNormal = value;
case OPT_WATERUGLY:
m_Options.m_WaterUgly = value;
break;
case OPT_WATERFANCYEFFECTS:
m_Options.m_WaterFancyEffects = 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;
case OPT_SHADOWSONWATER:
m_Options.m_WaterShadows = value;
break;
case OPT_SHADOWPCF:
m_Options.m_ShadowPCF = value;
@ -733,20 +730,18 @@ bool CRenderer::GetOptionBool(enum Option opt) const
return m_Options.m_NoVBO;
case OPT_SHADOWS:
return m_Options.m_Shadows;
case OPT_WATERNORMAL:
return m_Options.m_WaterNormal;
case OPT_WATERUGLY:
return m_Options.m_WaterUgly;
case OPT_WATERFANCYEFFECTS:
return m_Options.m_WaterFancyEffects;
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_SHADOWSONWATER:
return m_Options.m_WaterShadows;
case OPT_SHADOWPCF:
return m_Options.m_ShadowPCF;
case OPT_PARTICLES:

View File

@ -80,13 +80,12 @@ public:
enum Option {
OPT_NOVBO,
OPT_SHADOWS,
OPT_WATERNORMAL,
OPT_WATERUGLY,
OPT_WATERFANCYEFFECTS,
OPT_WATERREALDEPTH,
OPT_WATERFOAM,
OPT_WATERCOASTALWAVES,
OPT_WATERREFLECTION,
OPT_WATERREFRACTION,
OPT_WATERSHADOW,
OPT_SHADOWSONWATER,
OPT_SHADOWPCF,
OPT_PARTICLES,
OPT_GENTANGENTS,
@ -143,13 +142,12 @@ public:
bool m_NoVBO;
bool m_Shadows;
bool m_WaterNormal;
bool m_WaterUgly;
bool m_WaterFancyEffects;
bool m_WaterRealDepth;
bool m_WaterFoam;
bool m_WaterCoastalWaves;
bool m_WaterRefraction;
bool m_WaterReflection;
bool m_WaterShadow;
bool m_WaterShadows;
RenderPath m_RenderPath;
bool m_ShadowAlphaFix;

View File

@ -85,7 +85,7 @@ struct TerrainRendererInternals
/// Fancy water shader
CShaderProgramPtr fancyWaterShader;
CShaderProgramPtr wavesShader;
CShaderProgramPtr fancyEffectsShader;
CSimulation2* simulation;
};
@ -632,32 +632,26 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGr
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CShaderDefines defines = context;
WaterMgr->UpdateQuality();
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading)
{
if (WaterMgr->m_WaterNormal)
defines.Add(str_USE_NORMALS, str_1);
if (WaterMgr->m_WaterRealDepth)
defines.Add(str_USE_REAL_DEPTH, str_1);
if (WaterMgr->m_WaterFoam)
defines.Add(str_USE_FOAM, str_1);
if (WaterMgr->m_WaterCoastalWaves && false)
defines.Add(str_USE_WAVES, str_1);
if (WaterMgr->m_WaterFancyEffects)
defines.Add(str_USE_FANCY_EFFECTS, str_1);
if (WaterMgr->m_WaterRefraction)
defines.Add(str_USE_REFRACTION, str_1);
if (WaterMgr->m_WaterReflection)
defines.Add(str_USE_REFLECTION, str_1);
if (shadow && WaterMgr->m_WaterShadows)
defines.Add(str_USE_SHADOWS, str_1);
defines.Add(str_USE_SHADOWS_ON_WATER, str_1);
m->wavesShader = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", defines);
if (!m->wavesShader)
m->fancyEffectsShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_effects", defines);
if (!m->fancyEffectsShader)
{
LOGERROR(L"Failed to load waves shader. Deactivating waves.\n");
g_Renderer.SetOptionBool(CRenderer::OPT_WATERCOASTALWAVES, false);
defines.Add(str_USE_WAVES, str_0);
LOGERROR(L"Failed to load Fancy effects shader. Deactivating fancy effects.\n");
g_Renderer.SetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS, false);
defines.Add(str_USE_FANCY_EFFECTS, str_0);
}
// haven't updated the ARB shader yet so I'll always load the GLSL
@ -668,7 +662,7 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGr
if (!m->fancyWaterShader)
{
LOGERROR(L"Failed to load water shader. Falling back to non-fancy water.\n");
LOGERROR(L"Failed to load water shader. Falling back to fixed pipeline water.\n");
WaterMgr->m_RenderWater = false;
return false;
}
@ -717,104 +711,94 @@ bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGr
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 = 8;
int curTex = (int)(time*60/period) % 60;
int nexTex = (curTex + 1) % 60;
GLuint FramebufferName = 0;
// rendering waves to a framebuffer
// TODO: reactivate this with something that looks good.
if (false && WaterMgr->m_WaterCoastalWaves && WaterMgr->m_VBWaves && !g_AtlasGameLoop->running)
{
// 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);
// TODO: use POT texture
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(str_waveTex, WaterMgr->m_Wave);
m->wavesShader->Uniform(str_time, (float)time);
m->wavesShader->Uniform(str_waviness, WaterMgr->m_Waviness);
m->wavesShader->Uniform(str_mapSize, (float)(WaterMgr->m_MapSize));
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_TRIANGLES, (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();
// 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;
GLuint FramebufferName = 0;
// Render normals and foam to a framebuffer if we're in fancy effects
if (WaterMgr->m_WaterFancyEffects)
{
// Save the post-processing framebuffer.
GLint fbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo);
// Generate our framebuffer
pglGenFramebuffersEXT(1, &FramebufferName);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FramebufferName);
GLuint renderedTexture;
if (WaterMgr->m_FancyTexture == 0)
{
glGenTextures(1, &renderedTexture);
WaterMgr->m_FancyTexture = renderedTexture;
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_FancyTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0,GL_RGBA, GL_FLOAT, 0);
} else
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_FancyTexture);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, WaterMgr->m_FancyTexture, 0);
// rendering
m->fancyEffectsShader->Bind();
m->fancyEffectsShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]);
m->fancyEffectsShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]);
m->fancyEffectsShader->Uniform(str_waviness, WaterMgr->m_Waviness);
m->fancyEffectsShader->Uniform(str_translation, tx, ty);
m->fancyEffectsShader->Uniform(str_repeatScale, 1.0f / repeatPeriod);
m->fancyEffectsShader->Uniform(str_time, (float)time);
m->fancyEffectsShader->Uniform(str_screenSize, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f);
m->fancyEffectsShader->Uniform(str_mapSize, (float)(WaterMgr->m_MapSize));
std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
for (size_t i = 0; i < visiblePatches.size(); ++i)
{
CPatchRData* data = visiblePatches[i];
data->RenderWater(m->fancyEffectsShader);
}
m->fancyEffectsShader->Unbind();
// rebind post-processing frambuffer.
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glBindTexture(GL_TEXTURE_2D, 0);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
m->fancyWaterShader->Bind();
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
m->fancyWaterShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]);
m->fancyWaterShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]);
if (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves)
{
m->fancyWaterShader->BindTexture(str_Foam, WaterMgr->m_Foam);
m->fancyWaterShader->Uniform(str_mapSize, (float)(WaterMgr->m_MapSize));
}
if (WaterMgr->m_WaterFancyEffects)
m->fancyWaterShader->BindTexture(str_waterEffectsTex, WaterMgr->m_FancyTexture);
if (WaterMgr->m_WaterRealDepth)
m->fancyWaterShader->BindTexture(str_depthTex, WaterMgr->m_depthTT);
if (WaterMgr->m_WaterCoastalWaves)
m->fancyWaterShader->BindTexture(str_waveTex, WaterMgr->m_waveTT);
if (WaterMgr->m_WaterReflection)
m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture);
m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture);
if (WaterMgr->m_WaterRefraction)
m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture);
m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture);
m->fancyWaterShader->BindTexture(str_losMap, losTexture.GetTextureSmooth());
@ -981,6 +965,8 @@ void TerrainRenderer::RenderWater(const CShaderDefines& context, int cullGroup,
{
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
WaterMgr->UpdateQuality();
if (!WaterMgr->WillRenderFancyWater())
RenderSimpleWater(cullGroup);
else

View File

@ -82,10 +82,12 @@ WaterManager::WaterManager()
m_DistanceToShore = NULL;
m_FoamFactor = NULL;
m_WaterNormal = false;
m_DistanceHeightmap = NULL;
m_BlurredNormalMap = NULL;
m_WaterUgly = false;
m_WaterFancyEffects = false;
m_WaterRealDepth = false;
m_WaterFoam = false;
m_WaterCoastalWaves = false;
m_WaterRefraction = false;
m_WaterReflection = false;
m_WaterShadows = false;
@ -97,7 +99,7 @@ WaterManager::WaterManager()
m_VBWavesIndices = NULL;
m_depthTT = 0;
m_waveTT = 0;
m_FancyTexture = 0;
m_MapSize = 0;
@ -117,7 +119,7 @@ WaterManager::~WaterManager()
delete[] m_FoamFactor;
glDeleteTextures(1, &m_depthTT);
glDeleteTextures(1, &m_waveTT);
glDeleteTextures(1, &m_FancyTexture);
if (m_VBWaves) g_VBMan.Release(m_VBWaves);
if (m_VBWavesIndices) g_VBMan.Release(m_VBWavesIndices);
@ -134,14 +136,14 @@ int WaterManager::LoadWaterTextures()
// TODO: add a member variable and setter for this. (can't make this
// a parameter because this function is called via delay-load code)
static const wchar_t* const water_type = L"default";
static const wchar_t* const water_type = L"ocean";
wchar_t pathname[PATH_MAX];
// Load diffuse grayscale images (for non-fancy water)
for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i)
{
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/diffuse%02d.dds", water_type, (int)i+1);
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1);
CTextureProperties textureProps(pathname);
textureProps.SetWrap(GL_REPEAT);
@ -153,7 +155,7 @@ int WaterManager::LoadWaterTextures()
// Load normalmaps (for fancy water)
for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i)
{
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal%02d.dds", water_type, (int)i+1);
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", water_type, (int)i+1);
CTextureProperties textureProps(pathname);
textureProps.SetWrap(GL_REPEAT);
textureProps.SetMaxAnisotropy(4);
@ -162,6 +164,7 @@ int WaterManager::LoadWaterTextures()
texture->Prefetch();
m_NormalMap[i] = texture;
}
// Load foam (for fancy water)
{
CTextureProperties textureProps("art/textures/terrain/types/water/foam.png");
@ -234,6 +237,128 @@ void WaterManager::UnloadWaterTextures()
}
}
///////////////////////////////////////////////////////////////////
// Calculate our binary heightmap from the terrain heightmap.
void WaterManager::RecomputeDistanceHeightmap()
{
if (m_DistanceHeightmap == NULL)
m_DistanceHeightmap = new u8[m_MapSize*m_MapSize];
// Custom copy the heightmap.
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
u16 waterLevel = m_WaterHeight/HEIGHT_SCALE;
u16* heightmap = terrain->GetHeightMap();
// We will "expand" the heightmap. That is we'll set each vertex on land as "3", and "bleed" that onto neighboring pixels.
// So 3 is "on land", 2 is "close", 1 "somewhat close" and 0 is "water".
// This gives a basic manhattan approximation of how close to the coast we are.
// I have a heathen fondness for ternary operators so there are some below.
u8 level = 0;
for (size_t z = 0; z < m_MapSize; ++z)
{
level = 0;
for (size_t x = 0; x < m_MapSize; ++x)
m_DistanceHeightmap[z*m_MapSize + x] = heightmap[z*m_MapSize + x] >= waterLevel ? level = 3
: level > 0 ? --level : 0;
level = 0;
for (size_t x = m_MapSize-1; x != (size_t)-1; --x)
{
if (heightmap[z*m_MapSize + x] >= waterLevel)
level = 3; // no need to set m_distanceHeightmap, it's already been done by the other loop.
else
{
level > 0 ? --level : 0;
if (level > m_DistanceHeightmap[z*m_MapSize + x])
m_DistanceHeightmap[z*m_MapSize + x] = level;
}
}
}
for (size_t x = 0; x < m_MapSize; ++x)
{
level = 0;
for (size_t z = 0; z < m_MapSize; ++z)
{
if (heightmap[z*m_MapSize + x] >= waterLevel)
level = 3;
else
{
level > 0 ? --level : 0;
if (level > m_DistanceHeightmap[z*m_MapSize + x])
m_DistanceHeightmap[z*m_MapSize + x] = level;
}
}
level = 0;
for (size_t z = m_MapSize-1; z != (size_t)-1; --z)
{
if (heightmap[z*m_MapSize + x] >= waterLevel)
level = 3;
else
{
level > 0 ? --level : 0;
if (level > m_DistanceHeightmap[z*m_MapSize + x])
m_DistanceHeightmap[z*m_MapSize + x] = level;
}
}
}
}
///////////////////////////////////////////////////////////////////
// Calculate The blurred normal map to get an idea of where water ought to go.
void WaterManager::RecomputeBlurredNormalMap()
{
// used to cache terrain normals since otherwise we'd recalculate them a lot (I'm blurring the "normal" map).
// this might be updated to actually cache in the terrain manager but that's not for now.
if (m_BlurredNormalMap == NULL)
m_BlurredNormalMap = new CVector3D[m_MapSize*m_MapSize];
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
// It's really slow to calculate normals so cache them first.
CVector3D* normals = new CVector3D[m_MapSize*m_MapSize];
// Not the edges, we won't care about them.
float ii = 8.0f, jj = 8.0f;
for (size_t j = 2; j < m_MapSize-2; ++j, jj += 4.0f)
for (size_t i = 2; i < m_MapSize-2; ++i, ii += 4.0f)
{
CVector3D norm;
terrain->CalcNormal(i,j,norm);
normals[j*m_MapSize + i] = norm;
}
// We could be way fancier (and faster) for our blur but we probably don't need the complexity.
// Two pass filter, nothing complicated here.
CVector3D blurValue;
ii = 8.0f; jj = 8.0f;
size_t idx = 2;
for (size_t j = 2; j < m_MapSize-2; ++j, jj += 4.0f)
for (size_t i = 2; i < m_MapSize-2; ++i, ii += 4.0f,++idx)
{
blurValue = normals[idx-2];
blurValue += normals[idx-1];
blurValue += normals[idx];
blurValue += normals[idx+1];
blurValue += normals[idx+2];
m_BlurredNormalMap[idx] = blurValue * 0.2f;
}
// y direction, probably slower because of cache misses but I don't see an easy way around that.
ii = 8.0f; jj = 8.0f;
for (size_t i = 2; i < m_MapSize-2; ++i, ii += 4.0f)
{
for (size_t j = 2; j < m_MapSize-2; ++j, jj += 4.0f)
{
blurValue = normals[(j-2)*m_MapSize + i];
blurValue += normals[(j-1)*m_MapSize + i];
blurValue += normals[j*m_MapSize + i];
blurValue += normals[(j+1)*m_MapSize + i];
blurValue += normals[(j+2)*m_MapSize + i];
m_BlurredNormalMap[j*m_MapSize + i] = blurValue * 0.2f;
}
}
}
///////////////////////////////////////////////////////////////////
// Create information about the terrain and wave vertices.
void WaterManager::CreateSuperfancyInfo(CSimulation2* simulation)
@ -248,7 +373,7 @@ void WaterManager::CreateSuperfancyInfo(CSimulation2* simulation)
g_VBMan.Release(m_VBWavesIndices);
m_VBWavesIndices = NULL;
}
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
CmpPtr<ICmpWaterManager> cmpWaterManager(*simulation, SYSTEM_ENTITY);
@ -608,7 +733,7 @@ void WaterManager::CreateSuperfancyInfo(CSimulation2* simulation)
}
////////////////////////////////////////////////////////////////////////
// This will always recalculate for now
// TODO: This will always recalculate for now
void WaterManager::SetMapSize(size_t size)
{
// TODO: Im' blindly trusting the user here.
@ -619,6 +744,8 @@ void WaterManager::SetMapSize(size_t size)
m_updatej0 = 0;
m_updatej1 = size;
SAFE_ARRAY_DELETE(m_DistanceHeightmap);
SAFE_ARRAY_DELETE(m_BlurredNormalMap);
SAFE_ARRAY_DELETE(m_WaveX);
SAFE_ARRAY_DELETE(m_WaveZ);
SAFE_ARRAY_DELETE(m_DistanceToShore);
@ -629,24 +756,18 @@ void WaterManager::SetMapSize(size_t size)
// 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);
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERUGLY) != m_WaterUgly) {
m_WaterUgly = g_Renderer.GetOptionBool(CRenderer::OPT_WATERUGLY);
m_NeedsReloading = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS) != m_WaterFancyEffects) {
m_WaterFancyEffects = g_Renderer.GetOptionBool(CRenderer::OPT_WATERFANCYEFFECTS);
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_NeedInfoUpdate = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERCOASTALWAVES) != m_WaterCoastalWaves) {
m_WaterCoastalWaves = g_Renderer.GetOptionBool(CRenderer::OPT_WATERCOASTALWAVES);
m_NeedsReloading = true;
m_NeedInfoUpdate = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION) != m_WaterRefraction) {
m_WaterRefraction = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION);
m_NeedsReloading = true;
@ -655,8 +776,8 @@ void WaterManager::UpdateQuality()
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);
if (g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER) != m_WaterShadows) {
m_WaterShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER);
m_NeedsReloading = true;
}
}
@ -665,7 +786,7 @@ bool WaterManager::WillRenderFancyWater()
{
if (!g_Renderer.GetCapabilities().m_FragmentShader)
return false;
if (!m_RenderWater)
if (!m_RenderWater || m_WaterUgly)
return false;
return true;
}

View File

@ -58,11 +58,14 @@ public:
float* m_DistanceToShore;
float* m_FoamFactor;
u8* m_DistanceHeightmap; // Returns how far from the shore a point is. 3-2-1-0 where 3 is "on land"
CVector3D* m_BlurredNormalMap; // Returns how far from the shore a point is. 3-2-1-0 where 3 is "on land"
size_t m_MapSize;
ssize_t m_TexSize;
GLuint m_depthTT;
GLuint m_waveTT;
GLuint m_FancyTexture;
// used to know what to update when updating parts of the terrain only.
u32 m_updatei0;
@ -74,13 +77,18 @@ public:
CColor m_WaterColor;
bool m_RenderWater;
// Force the use of the fixed function for rendering.
bool m_WaterUgly;
// Those variables register the current quality level. If there is a change, I have to recompile the shader.
bool m_WaterNormal;
// Use real depth or use the fake precomputed one.
bool m_WaterRealDepth;
bool m_WaterFoam;
bool m_WaterCoastalWaves;
// Use fancy shore effects and show trails behind ships
bool m_WaterFancyEffects;
// Use refractions instead of simply making the water more or less transparent.
bool m_WaterRefraction;
// Use complete reflections instead of showing merely the sky.
bool m_WaterReflection;
// Show shadows on the water.
bool m_WaterShadows;
bool m_NeedsReloading;
@ -145,6 +153,17 @@ public:
*/
void UnloadWaterTextures();
/**
* RecomputeDistanceHeightmap: recalculates (or calculates) the distance heightmap.
*/
void RecomputeDistanceHeightmap();
/**
* RecomputeBlurredNormalMap: calculates the blurred normal map of the terrain. Slow.
*/
void RecomputeBlurredNormalMap();
/**
* CreateSuperfancyInfo: creates textures and wave vertices for superfancy water
*/

View File

@ -34,15 +34,14 @@ void JSI_Renderer::Set##SCRIPTNAME##Enabled(ScriptInterface::CxPrivate* UNUSED(p
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(PARTICLES, Particles);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(GENTANGENTS, GenTangents);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(PREFERGLSL, PreferGLSL);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERNORMAL, WaterNormal);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERUGLY, WaterUgly);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERFANCYEFFECTS, WaterFancyEffects);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHADOWPCF, ShadowPCF);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHADOWS, Shadows);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERREALDEPTH, WaterRealDepth);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERREFLECTION, WaterReflection);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERREFRACTION, WaterRefraction);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERFOAM, WaterFoam);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERCOASTALWAVES, WaterCoastalWaves);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(WATERSHADOW, WaterShadow);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHADOWSONWATER, WaterShadows);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SILHOUETTES, Silhouettes);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SHOWSKY, ShowSky);
IMPLEMENT_BOOLEAN_SCRIPT_SETTING(SMOOTHLOS, SmoothLOS);
@ -75,13 +74,12 @@ void JSI_Renderer::RegisterScriptFunctions(ScriptInterface& scriptInterface)
REGISTER_BOOLEAN_SCRIPT_SETTING(Particles);
REGISTER_BOOLEAN_SCRIPT_SETTING(GenTangents);
REGISTER_BOOLEAN_SCRIPT_SETTING(PreferGLSL);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterNormal);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterUgly);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterFancyEffects);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterRealDepth);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterReflection);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterRefraction);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterFoam);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterCoastalWaves);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterShadow);
REGISTER_BOOLEAN_SCRIPT_SETTING(WaterShadows);
REGISTER_BOOLEAN_SCRIPT_SETTING(Silhouettes);
REGISTER_BOOLEAN_SCRIPT_SETTING(ShowSky);
REGISTER_BOOLEAN_SCRIPT_SETTING(SmoothLOS);

View File

@ -35,13 +35,12 @@ namespace JSI_Renderer
DECLARE_BOOLEAN_SCRIPT_SETTING(Particles);
DECLARE_BOOLEAN_SCRIPT_SETTING(GenTangents);
DECLARE_BOOLEAN_SCRIPT_SETTING(PreferGLSL);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterNormal);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterUgly);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterFancyEffects);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterRealDepth);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterReflection);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterRefraction);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterFoam);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterCoastalWaves);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterShadow);
DECLARE_BOOLEAN_SCRIPT_SETTING(WaterShadows);
DECLARE_BOOLEAN_SCRIPT_SETTING(Silhouettes);
DECLARE_BOOLEAN_SCRIPT_SETTING(ShowSky);
DECLARE_BOOLEAN_SCRIPT_SETTING(SmoothLOS);

View File

@ -136,8 +136,11 @@ public:
}
if (ReloadWater && CRenderer::IsInitialised())
{
g_Renderer.GetWaterManager()->SetMapSize(vertices);
g_Renderer.GetWaterManager()->RecomputeDistanceHeightmap();
g_Renderer.GetWaterManager()->RecomputeBlurredNormalMap();
}
MakeDirty(0, 0, tiles+1, tiles+1);
}

View File

@ -33,6 +33,7 @@ class CCmpWaterManager : public ICmpWaterManager
public:
static void ClassInit(CComponentManager& componentManager)
{
// No need to subscribe to WaterChanged since we're actually the one sending those.
componentManager.SubscribeToMessageType(MT_Interpolate);
componentManager.SubscribeToMessageType(MT_TerrainChanged);
}
@ -50,7 +51,6 @@ public:
virtual void Init(const CParamNode& UNUSED(paramNode))
{
SetWaterLevel(entity_pos_t::FromInt(5));
}
virtual void Deinit()
@ -119,6 +119,8 @@ public:
// Tell the terrain it'll need to recompute its cached render data
GetSimContext().GetTerrain().MakeDirty(RENDERDATA_UPDATE_VERTICES);
g_Renderer.GetWaterManager()->m_WaterHeight = h.ToFloat();
CMessageWaterChanged msg;
GetSimContext().GetComponentManager().BroadcastMessage(msg);
}