Index: ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 16387)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 16388)
@@ -1,396 +1,398 @@
#version 110
// Environment settings
uniform vec3 ambient;
uniform vec3 sunDir;
uniform vec3 sunColor;
uniform mat4 skyBoxRot;
uniform vec3 cameraPos;
uniform sampler2D losMap;
uniform float waviness; // "Wildness" of the reflections and refractions; choose based on texture
uniform vec3 color; // color of the water
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 windAngle;
varying vec2 WindCosSin;
uniform vec3 fogColor;
uniform vec2 fogParams;
uniform vec2 screenSize;
uniform float time;
varying vec3 worldPos;
varying float waterDepth;
varying vec2 waterInfo;
varying vec4 normalCoords;
varying vec3 reflectionCoords;
varying vec3 refractionCoords;
varying vec2 losCoords;
varying float fwaviness;
uniform float mapSize;
uniform samplerCube skyCube;
uniform sampler2D normalMap;
uniform sampler2D normalMap2;
#if USE_FANCY_EFFECTS
uniform sampler2D waterEffectsTexNorm;
uniform sampler2D waterEffectsTexOther;
#endif
uniform vec4 waveParams1; // wavyEffect, BaseScale, Flattenism, Basebump
uniform vec4 waveParams2; // Smallintensity, Smallbase, Bigmovement, Smallmovement
uniform sampler2D reflectionMap;
#if USE_REFRACTION
uniform sampler2D refractionMap;
#endif
#if USE_REAL_DEPTH
uniform sampler2D depthTex;
#endif
#if USE_SHADOWS_ON_WATER && USE_SHADOW
varying vec4 v_shadow;
#if USE_SHADOW_SAMPLER
uniform sampler2DShadow shadowTex;
#if USE_SHADOW_PCF
uniform vec4 shadowScale;
#endif
#else
uniform sampler2D shadowTex;
#endif
float get_shadow(vec4 coords)
{
#if USE_SHADOWS_ON_WATER && !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(1.0, 1.0, -0.5, -0.5) + (coords.xy - 0.5*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
// 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 ) );
}
vec3 get_fog(vec3 color)
{
float density = fogParams.x;
float maxFog = fogParams.y;
const float LOG2 = 1.442695;
float z = gl_FragCoord.z / gl_FragCoord.w;
float fogFactor = exp2(-density * density * z * z * LOG2);
fogFactor = fogFactor * (1.0 - maxFog) + maxFog;
fogFactor = clamp(fogFactor, 0.0, 1.0);
return mix(fogColor, color, fogFactor);
}
void main()
{
//gl_FragColor = texture2D(waterEffectsTex, gl_FragCoord.xy/screenSize);
//return;
float fresnel;
float t; // Temporary variable
vec2 reflCoords, refrCoords;
vec3 reflColor, refrColor, specular;
float losMod;
vec3 l = -sunDir;
vec3 v = normalize(cameraPos - worldPos);
vec3 h = normalize(l + v);
// Calculate water normals.
float wavyEffect = waveParams1.r;
float baseScale = waveParams1.g;
float flattenism = waveParams1.b;
float baseBump = waveParams1.a;
float smallIntensity = waveParams2.r;
float smallBase = waveParams2.g;
float BigMovement = waveParams2.b;
float SmallMovement = waveParams2.a;
float moddedTime = mod(time * 60.0, 8.0) / 8.0;
// 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, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).xzy;
vec3 ww2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).xzy;
vec3 wwInterp = mix(ww1, ww2, moddedTime) - vec3(0.5,0.0,0.5);
ww1.x = wwInterp.x * WindCosSin.x - wwInterp.z * WindCosSin.y;
ww1.z = wwInterp.x * WindCosSin.y + wwInterp.z * WindCosSin.x;
ww1.y = wwInterp.y;
vec3 smallWW = texture2D(normalMap, (normalCoords.st + normalCoords.zw * SmallMovement*waviness/10.0) * baseScale*3.0).xzy;
vec3 smallWW2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * SmallMovement*waviness/10.0) * baseScale*3.0).xzy;
vec3 smallWWInterp = mix(smallWW, smallWW2, moddedTime) - vec3(0.5,0.0,0.5);
smallWW.x = smallWWInterp.x * WindCosSin.x - smallWWInterp.z * WindCosSin.y;
smallWW.z = smallWWInterp.x * WindCosSin.y + smallWWInterp.z * WindCosSin.x;
smallWW.y = smallWWInterp.y;
ww1 += vec3(smallWW)*(fwaviness/10.0*smallIntensity + smallBase);
ww1 = mix(smallWW, ww1, waterInfo.r);
// Flatten them based on waviness.
vec3 n = normalize(mix(vec3(0.0,1.0,0.0),ww1, clamp(baseBump + fwaviness/flattenism,0.0,1.0)));
#if USE_FANCY_EFFECTS
vec4 fancyeffects = texture2D(waterEffectsTexNorm, gl_FragCoord.xy/screenSize);
n = mix(vec3(0.0,1.0,0.0), n,0.5 + waterInfo.r/2.0);
n.xz = mix(n.xz, fancyeffects.rb,fancyeffects.a/2.0);
#else
n = mix(vec3(0.0,1.0,0.0), n,0.5 + waterInfo.r/2.0);
#endif
n = vec3(-n.x,n.y,-n.z);
// simulates how parallel the "point->sun", "view->point" vectors are.
float ndoth = dot(n , 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;
- 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;
-
- depth = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer);
- #else
- depth = waterDepth / (min(0.5,v.y)*1.5*min(0.5,v.y)*2.0);
- #endif
+#if USE_REAL_DEPTH
+ // Don't change these two. They should match the values in the config (TODO: dec uniforms).
+ float zNear = 2.0;
+ float zFar = 4096.0;
+
+ // 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;
+
+ depth = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer);
+#else
+ depth = waterDepth / (min(0.5,v.y)*1.5*min(0.5,v.y)*2.0);
+#endif
+
+#if USE_FANCY_EFFECTS
+ depth = max(depth,fancyeffects.a);
+#endif
- #if USE_FANCY_EFFECTS
- depth = max(depth,fancyeffects.a);
- #endif
-
// 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
+ fresnel = clamp(pow(1.05 - ndotv, 1.1),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 = clamp(fresnel*1.5,0.0,0.9);
fresnel *= min(1.0,log(1.0 + v.y*5.0));
- fresnel = 0.1 + fresnel * 0.8;
//gl_FragColor = vec4(fresnel,fresnel,fresnel,1.0);
//return;
- #if USE_SHADOWS_ON_WATER && USE_SHADOW
- float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw));
- #endif
+#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 = max(v.y,0.1);
-
+
float distoFactor = clamp(depth/2.0,0.0,7.0);
float murky = mix(200.0,0.1,pow(murkiness,0.25));
-
- #if USE_REFRACTION
- refrCoords = clamp( (0.5*refractionCoords.xy - n.xz * distoFactor*7.0) / refractionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
- vec3 refColor = texture2D(refractionMap, refrCoords).rgb;
- if (refColor.r > refColor.g + refColor.b + 0.25)
- {
- refrCoords = clamp( (0.5*refractionCoords.xy + n.xz) / refractionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
- 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
- refrColor = mix(color, colll, extFact);
- #endif
- #else
- // 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_REFLECTION
- // Reflections
- vec3 eye = reflect(v,n);
- //eye.y = min(-0.2,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);
- vec4 newpos = vec4(-worldPos.x/4.0,worldPos.y,-worldPos.z/4.0,1.0) + vec4(eye * tmin,0.0) - vec4(-mapSize/2.0,worldPos.y,-mapSize/2.0,0.0);
- //newpos = normalize(newpos);
- newpos *= skyBoxRot;
- newpos.y *= 4.0;
- reflColor = textureCube(skyCube, newpos.rgb).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;
-
- reflCoords = clamp( (0.5*reflectionCoords.xy - waviness * mix(1.0, 4.0,waviness/10.0) * n.zx) / reflectionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
- vec4 refTex = texture2D(reflectionMap, reflCoords);
- reflColor = refTex.rgb * refTex.a + reflColor*(1.0-refTex.a);
- #else
- // Temp fix for some ATI cards (see irc logs on th 1st of august betwee, fexor and wraitii)
- //reflCoords = clamp( (0.5*reflectionCoords.xy - waviness * mix(1.0, 20.0,waviness/10.0) * n.zx) / reflectionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
- //vec3 refTex = texture2D(reflectionMap, reflCoords).rgb;
- //reflColor = refTex.rgb;
- reflColor = vec3(0.15, 0.7, 0.82);
- #endif
-
+#if USE_REFRACTION
+ refrCoords = clamp( (0.5*refractionCoords.xy - n.xz * distoFactor*7.0) / refractionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
+ vec3 refColor = texture2D(refractionMap, refrCoords).rgb;
+ if (refColor.r > refColor.g + refColor.b + 0.25)
+ {
+ refrCoords = clamp( (0.5*refractionCoords.xy + n.xz) / refractionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
+ 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
+ refrColor = mix(color, colll, extFact);
+#endif
+#else
+ // 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_REFLECTION
+ // Reflections
+ vec3 eye = reflect(v,n);
+ //eye.y = min(-0.2,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);
+ vec4 newpos = vec4(-worldPos.x/4.0,worldPos.y,-worldPos.z/4.0,1.0) + vec4(eye * tmin,0.0) - vec4(-mapSize/2.0,worldPos.y,-mapSize/2.0,0.0);
+ //newpos = normalize(newpos);
+ newpos *= skyBoxRot;
+ newpos.y *= 4.0;
+ reflColor = textureCube(skyCube, newpos.rgb).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;
+
+ reflCoords = clamp( (0.5*reflectionCoords.xy - 40.0 * n.zx) / reflectionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
+ vec4 refTex = texture2D(reflectionMap, reflCoords);
+ fresnel = clamp(fresnel+refTex.a/3.0,0.0,1.0);
+ reflColor = refTex.rgb * refTex.a + reflColor*(1.0-refTex.a);
+
+#else
+ // Temp fix for some ATI cards (see irc logs on th 1st of august betwee, fexor and wraitii)
+ //reflCoords = clamp( (0.5*reflectionCoords.xy - waviness * mix(1.0, 20.0,waviness/10.0) * n.zx) / reflectionCoords.z + 0.5,0.0,1.0); // Unbias texture coords
+ //vec3 refTex = texture2D(reflectionMap, reflCoords).rgb;
+ //reflColor = refTex.rgb;
+ reflColor = vec3(0.15, 0.7, 0.82);
+#endif
+
// 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));
// Specular.
specular = pow(ndoth, mix(5.0,2000.0, clamp(v.y*v.y*2.0,0.0,1.0)))*sunColor * 1.5;// * sunColor * 1.5 * ww.r;
-
+
losMod = texture2D(losMap, losCoords.st).a;
losMod = losMod < 0.03 ? 0.0 : losMod;
float wavesFresnel = 1.0;
- #if USE_FANCY_EFFECTS
- wavesFresnel = mix(1.0-fancyeffects.a,1.0,clamp(depth,0.0,1.0));
- #endif
+#if USE_FANCY_EFFECTS
+ wavesFresnel = mix(1.0-fancyeffects.a,1.0,clamp(depth,0.0,1.0));
+#endif
vec3 colour;
- #if USE_SHADOWS_ON_WATER && USE_SHADOW
- float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + murkiness*0.2);
- colour = mix(refrColor, reflColor, fresShadow * wavesFresnel);
- #else
- colour = mix(refrColor, reflColor, fresnel * wavesFresnel);
- #endif
+#if USE_SHADOWS_ON_WATER && USE_SHADOW
+ float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + murkiness*0.2);
+ colour = mix(refrColor, reflColor, fresShadow * wavesFresnel);
+#else
+ colour = mix(refrColor, reflColor, fresnel * wavesFresnel);
+#endif
- #if USE_SHADOWS_ON_WATER && USE_SHADOW
- colour += shadow*specular;
- #else
- colour += specular;
- #endif
+#if USE_SHADOWS_ON_WATER && USE_SHADOW
+ colour += shadow*specular;
+#else
+ colour += specular;
+#endif
- #if USE_FANCY_EFFECTS
- vec4 FoamEffects = texture2D(waterEffectsTexOther, gl_FragCoord.xy/screenSize);
-
- vec3 foam1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa;
- vec3 foam2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa;
- vec3 foam3 = texture2D(normalMap, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa;
- vec3 foam4 = texture2D(normalMap2, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa;
- vec3 foaminterp = mix(foam1, foam2, moddedTime);
- foaminterp *= mix(foam3, foam4, moddedTime);
-
- foam1.x = foaminterp.x * WindCosSin.x - foaminterp.z * WindCosSin.y;
- //foam1.z = foaminterp.x * WindCosSin.y + foaminterp.z * WindCosSin.x;
- //foam1.y = foaminterp.y;
- float foam = FoamEffects.r * FoamEffects.a*0.4 + pow(foam1.x*(5.0+waviness),(2.6 - waviness/5.5));
- foam *= ndotl;
+#if USE_FANCY_EFFECTS
+ vec4 FoamEffects = texture2D(waterEffectsTexOther, gl_FragCoord.xy/screenSize);
- gl_FragColor.rgb = get_fog(colour) * losMod + foam * losMod;// + fancyeffects.a * losMod;
- #else
- gl_FragColor.rgb = get_fog(colour) * losMod;
- #endif
+ vec3 foam1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa;
+ vec3 foam2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa;
+ vec3 foam3 = texture2D(normalMap, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa;
+ vec3 foam4 = texture2D(normalMap2, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa;
+ vec3 foaminterp = mix(foam1, foam2, moddedTime);
+ foaminterp *= mix(foam3, foam4, moddedTime);
+
+ foam1.x = foaminterp.x * WindCosSin.x - foaminterp.z * WindCosSin.y;
+ //foam1.z = foaminterp.x * WindCosSin.y + foaminterp.z * WindCosSin.x;
+ //foam1.y = foaminterp.y;
+ float foam = FoamEffects.r * FoamEffects.a*0.4 + pow(foam1.x*(5.0+waviness),(2.6 - waviness/5.5));
+ foam *= ndotl;
+
+ gl_FragColor.rgb = get_fog(colour) * losMod + foam * losMod;// + fancyeffects.a * losMod;
+#else
+ 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*5.0,0.0,1.0);
- #endif
+#if !USE_REFRACTION
+ gl_FragColor.a = clamp(depth*2.0,0.0,1.0) * alphaCoeff;
+#else
+ gl_FragColor.a = clamp(depth*5.0,0.0,1.0);
+#endif
- #if USE_FANCY_EFFECTS
- if (fancyeffects.a < 0.05 && waterDepth < -1.0 )
- gl_FragColor.a = 0.0;
- #endif
+#if USE_FANCY_EFFECTS
+ if (fancyeffects.a < 0.05 && waterDepth < -1.0 )
+ gl_FragColor.a = 0.0;
+#endif
//gl_FragColor = vec4(sunColor,1.0);
}
Index: ps/trunk/source/renderer/WaterManager.cpp
===================================================================
--- ps/trunk/source/renderer/WaterManager.cpp (revision 16387)
+++ ps/trunk/source/renderer/WaterManager.cpp (revision 16388)
@@ -1,1133 +1,1150 @@
/* Copyright (C) 2014 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* Water settings (speed, height) and texture management
*/
#include "precompiled.h"
#include "graphics/Terrain.h"
#include "graphics/TextureManager.h"
#include "graphics/ShaderManager.h"
#include "graphics/ShaderProgram.h"
#include "lib/bits.h"
#include "lib/timer.h"
#include "lib/tex/tex.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/MathUtil.h"
#include "maths/Vector2D.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "renderer/WaterManager.h"
#include "renderer/Renderer.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpWaterManager.h"
#include "simulation2/components/ICmpRangeManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// WaterManager implementation
struct CoastalPoint
{
CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {};
int index;
CVector2D position;
};
struct SWavesVertex {
// vertex position
CVector3D m_BasePosition;
CVector3D m_ApexPosition;
CVector3D m_SplashPosition;
CVector3D m_RetreatPosition;
CVector2D m_PerpVect;
u8 m_UV[3];
// pad to a power of two
u8 m_Padding[5];
};
cassert(sizeof(SWavesVertex) == 64);
struct WaveObject
{
CVertexBuffer::VBChunk* m_VBvertices;
CBoundingBoxAligned m_AABB;
size_t m_Width;
float m_TimeDiff;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
WaterManager::WaterManager()
{
// water
m_RenderWater = false; // disabled until textures are successfully loaded
m_WaterHeight = 5.0f;
m_WaterCurrentTex = 0;
m_ReflectionTexture = 0;
m_RefractionTexture = 0;
m_ReflectionTextureSize = 0;
m_RefractionTextureSize = 0;
m_ReflectionFbo = 0;
m_RefractionFbo = 0;
m_FancyEffectsFBO = 0;
m_WaterTexTimer = 0.0;
m_WindAngle = 0.0f;
m_Waviness = 8.0f;
m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
m_Murkiness = 0.45f;
m_RepeatPeriod = 16.0f;
m_DistanceHeightmap = NULL;
m_BlurredNormalMap = NULL;
m_WindStrength = NULL;
m_ShoreWaves_VBIndices = NULL;
m_WaterUgly = false;
m_WaterFancyEffects = false;
m_WaterRealDepth = false;
m_WaterRefraction = false;
m_WaterReflection = false;
m_WaterShadows = false;
m_WaterType = L"ocean";
m_NeedsReloading = false;
m_NeedInfoUpdate = true;
m_depthTT = 0;
m_FancyTextureNormal = 0;
m_FancyTextureOther = 0;
m_FancyTextureDepth = 0;
m_ReflFboDepthTexture = 0;
m_RefrFboDepthTexture = 0;
m_MapSize = 0;
m_updatei0 = 0;
m_updatej0 = 0;
m_updatei1 = 0;
m_updatej1 = 0;
}
WaterManager::~WaterManager()
{
// Cleanup if the caller messed up
UnloadWaterTextures();
// TODO: when c++11 is around, use lambdas or something because short Korea is best Korea.
for (size_t i = 0; i < m_ShoreWaves.size(); ++i)
{
WaveObject* obj = m_ShoreWaves[i];
if (obj->m_VBvertices)
g_VBMan.Release(obj->m_VBvertices);
delete obj;
}
if (m_ShoreWaves_VBIndices)
g_VBMan.Release(m_ShoreWaves_VBIndices);
delete[] m_DistanceHeightmap;
delete[] m_BlurredNormalMap;
delete[] m_WindStrength;
if (!g_Renderer.GetCapabilities().m_PrettyWater)
return;
glDeleteTextures(1, &m_depthTT);
glDeleteTextures(1, &m_FancyTextureNormal);
glDeleteTextures(1, &m_FancyTextureOther);
glDeleteTextures(1, &m_FancyTextureDepth);
glDeleteTextures(1, &m_ReflFboDepthTexture);
glDeleteTextures(1, &m_RefrFboDepthTexture);
pglDeleteFramebuffersEXT(1, &m_FancyEffectsFBO);
pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
}
///////////////////////////////////////////////////////////////////
// Progressive load of water textures
int WaterManager::LoadWaterTextures()
{
// TODO: this doesn't need to be progressive-loading any more
// (since texture loading is async now)
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/default/diffuse%02d.dds", (int)i+1);
CTextureProperties textureProps(pathname);
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_WaterTexture[i] = texture;
}
if (!g_Renderer.GetCapabilities().m_PrettyWater)
{
// Enable rendering, now that we've succeeded this far
m_RenderWater = true;
return 0;
}
#if CONFIG2_GLES
#warning Fix WaterManager::LoadWaterTextures on GLES
#else
// 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/normal00%02d.png", m_WaterType.c_str(), (int)i+1);
CTextureProperties textureProps(pathname);
textureProps.SetWrap(GL_REPEAT);
textureProps.SetMaxAnisotropy(4);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_NormalMap[i] = texture;
}
// Load CoastalWaves
{
CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png");
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_WaveTex = texture;
}
// Load Foam
{
CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png");
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_FoamTex = texture;
}
m_ReflectionTextureSize = g_Renderer.GetHeight() * 0.66; // Higher settings give a better result
m_RefractionTextureSize = g_Renderer.GetHeight() * 0.33; // Lower settings actually sorta look better since it blurs.
if (round_down_to_pow2(m_ReflectionTextureSize)/m_ReflectionTextureSize < 0.65)
m_ReflectionTextureSize = round_up_to_pow2(m_ReflectionTextureSize);
else
m_ReflectionTextureSize = round_down_to_pow2(m_ReflectionTextureSize);
if (round_down_to_pow2(m_RefractionTextureSize)/m_RefractionTextureSize < 0.7)
m_RefractionTextureSize = round_up_to_pow2(m_RefractionTextureSize);
else
m_RefractionTextureSize = round_down_to_pow2(m_RefractionTextureSize);
// Create reflection texture
glGenTextures(1, &m_ReflectionTexture);
glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture);
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_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_ReflectionTextureSize, (GLsizei)m_ReflectionTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// Create refraction texture
glGenTextures(1, &m_RefractionTexture);
glBindTexture(GL_TEXTURE_2D, m_RefractionTexture);
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_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, (GLsizei)m_RefractionTextureSize, (GLsizei)m_RefractionTextureSize, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
// Create depth textures
glGenTextures(1, &m_ReflFboDepthTexture);
glBindTexture(GL_TEXTURE_2D, m_ReflFboDepthTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_ReflectionTextureSize, (GLsizei)m_ReflectionTextureSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
glGenTextures(1, &m_RefrFboDepthTexture);
glBindTexture(GL_TEXTURE_2D, m_RefrFboDepthTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_RefractionTextureSize, (GLsizei)m_RefractionTextureSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
// Create the Fancy Effects texture
glGenTextures(1, &m_FancyTextureNormal);
glBindTexture(GL_TEXTURE_2D, m_FancyTextureNormal);
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_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glGenTextures(1, &m_FancyTextureOther);
glBindTexture(GL_TEXTURE_2D, m_FancyTextureOther);
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_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glGenTextures(1, &m_FancyTextureDepth);
glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth);
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_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(GL_TEXTURE_2D, 0);
Resize();
// Create the water framebuffers
GLint currentFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, ¤tFbo);
m_ReflectionFbo = 0;
pglGenFramebuffersEXT(1, &m_ReflectionFbo);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_ReflectionFbo);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ReflectionTexture, 0);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_ReflFboDepthTexture, 0);
ogl_WarnIfError();
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
LOGWARNING("Reflection framebuffer object incomplete: 0x%04X", status);
g_Renderer.m_Options.m_WaterReflection = false;
}
m_RefractionFbo = 0;
pglGenFramebuffersEXT(1, &m_RefractionFbo);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_RefractionFbo);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_RefractionTexture, 0);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_RefrFboDepthTexture, 0);
ogl_WarnIfError();
status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
LOGWARNING("Refraction framebuffer object incomplete: 0x%04X", status);
g_Renderer.m_Options.m_WaterRefraction = false;
}
pglGenFramebuffersEXT(1, &m_FancyEffectsFBO);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_FancyTextureNormal, 0);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, m_FancyTextureOther, 0);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_FancyTextureDepth, 0);
ogl_WarnIfError();
status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
LOGWARNING("Fancy Effects framebuffer object incomplete: 0x%04X", status);
g_Renderer.m_Options.m_WaterRefraction = false;
}
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentFbo);
// Enable rendering, now that we've succeeded this far
m_RenderWater = true;
#endif
return 0;
}
///////////////////////////////////////////////////////////////////
// Resize: Updates the fancy water textures.
void WaterManager::Resize()
{
glBindTexture(GL_TEXTURE_2D, m_FancyTextureNormal);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL);
glBindTexture(GL_TEXTURE_2D, m_FancyTextureOther);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_SHORT, NULL);
glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth);
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
}
+# This is for Atlas. TODO: this copies code from init, should reuse it.
+void WaterManager::ReloadWaterNormalTextures()
+{
+ wchar_t pathname[PATH_MAX];
+ // 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/normal00%02d.png", m_WaterType.c_str(), (int)i+1);
+ CTextureProperties textureProps(pathname);
+ textureProps.SetWrap(GL_REPEAT);
+ textureProps.SetMaxAnisotropy(4);
+
+ CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
+ texture->Prefetch();
+ m_NormalMap[i] = texture;
+ }
+}
///////////////////////////////////////////////////////////////////
// Unload water textures
void WaterManager::UnloadWaterTextures()
{
for(size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
m_WaterTexture[i].reset();
if (!g_Renderer.GetCapabilities().m_PrettyWater)
return;
for(size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++)
m_NormalMap[i].reset();
glDeleteTextures(1, &m_ReflectionTexture);
glDeleteTextures(1, &m_RefractionTexture);
pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
}
///////////////////////////////////////////////////////////////////
// Calculate our binary heightmap from the terrain heightmap.
void WaterManager::RecomputeDistanceHeightmap()
{
size_t SideSize = m_MapSize*2;
if (m_DistanceHeightmap == NULL)
m_DistanceHeightmap = new float[SideSize*SideSize];
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
// Create a manhattan-distance heightmap.
// This is currently upsampled by a factor of 2 to get more precision
// This could be refined to only be done near the coast itself, but it's probably not necessary.
float level = SideSize;
for (size_t z = 0; z < SideSize; ++z)
{
level = SideSize;
for (size_t x = 0; x < SideSize; ++x)
m_DistanceHeightmap[z*SideSize + x] = terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight ? level = 0.f : ++level;
level = SideSize;
for (size_t x = SideSize-1; x != (size_t)-1; --x)
{
if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight)
level = 0.f;
else
{
++level;
if (level < m_DistanceHeightmap[z*SideSize + x])
m_DistanceHeightmap[z*SideSize + x] = level;
}
}
}
for (size_t x = 0; x < SideSize; ++x)
{
level = SideSize;
for (size_t z = 0; z < SideSize; ++z)
{
if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight)
level = 0.f;
else if (level > m_DistanceHeightmap[z*SideSize + x])
level = m_DistanceHeightmap[z*SideSize + x];
else
{
++level;
if (level < m_DistanceHeightmap[z*SideSize + x])
m_DistanceHeightmap[z*SideSize + x] = level;
}
}
level = SideSize;
for (size_t z = SideSize-1; z != (size_t)-1; --z)
{
if (terrain->GetExactGroundLevel(x*2, z*2) >= m_WaterHeight)
level = 0.f;
else if (level > m_DistanceHeightmap[z*SideSize + x])
level = m_DistanceHeightmap[z*SideSize + x];
else
{
++level;
if (level < m_DistanceHeightmap[z*SideSize + x])
m_DistanceHeightmap[z*SideSize + x] = level;
}
}
}
}
// This requires m_DistanceHeightmap to be defined properly.
void WaterManager::CreateWaveMeshes()
{
size_t SideSize = m_MapSize*2;
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
// TODO: when c++11 is around, use lambdas or something because short Korea is best Korea.
for (size_t i = 0; i < m_ShoreWaves.size(); ++i)
{
WaveObject* obj = m_ShoreWaves[i];
if (obj->m_VBvertices)
g_VBMan.Release(obj->m_VBvertices);
delete obj;
}
m_ShoreWaves.clear();
if (m_ShoreWaves_VBIndices)
{
g_VBMan.Release(m_ShoreWaves_VBIndices);
m_ShoreWaves_VBIndices = NULL;
}
if (m_Waviness < 5.0f && m_WaterType != L"ocean")
return;
// First step: get the points near the coast.
std::set CoastalPointsSet;
for (size_t z = 1; z < SideSize-1; ++z)
for (size_t x = 1; x < SideSize-1; ++x)
if (abs(m_DistanceHeightmap[z*SideSize + x]-1.0f) < 0.2f)
CoastalPointsSet.insert(z*SideSize + x);
// Second step: create chains out of those coastal points.
static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } };
std::vector > CoastalPointsChains;
while (!CoastalPointsSet.empty())
{
int index = *(CoastalPointsSet.begin());
int x = index % SideSize;
int y = (index - x ) / SideSize;
std::deque Chain;
Chain.push_front(CoastalPoint(index,CVector2D(x*2,y*2)));
// Erase us.
CoastalPointsSet.erase(CoastalPointsSet.begin());
// We're our starter points. At most we can have 2 points close to us.
// We'll pick the first one and look for its neighbors (he can only have one new)
// Up until we either reach the end of the chain, or ourselves.
// Then go down the other direction if there is any.
int neighbours[2] = { -1, -1 };
int nbNeighb = 0;
for (int i = 0; i < 8; ++i)
{
if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize))
{
if (nbNeighb < 2)
neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize;
++nbNeighb;
}
}
if (nbNeighb > 2)
continue;
for (int i = 0; i < 2; ++i)
{
if (neighbours[i] == -1)
continue;
// Move to our neighboring point
int xx = neighbours[i] % SideSize;
int yy = (neighbours[i] - xx ) / SideSize;
int indexx = xx + yy*SideSize;
int endedChain = false;
if (i == 0)
Chain.push_back(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
else
Chain.push_front(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
// If there's a loop we'll be the "other" neighboring point already so check for that.
// We'll readd at the end/front the other one to have full squares.
if (CoastalPointsSet.count(indexx) == 0)
break;
CoastalPointsSet.erase(indexx);
// Start checking from there.
while(!endedChain)
{
bool found = false;
nbNeighb = 0;
for (int p = 0; p < 8; ++p)
{
if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize))
{
if (nbNeighb >= 2)
{
CoastalPointsSet.erase(xx + yy*SideSize);
continue;
}
++nbNeighb;
// We've found a new point around us.
// Move there
xx = xx + around[p][0];
yy = yy + around[p][1];
indexx = xx + yy*SideSize;
if (i == 0)
Chain.push_back(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
else
Chain.push_front(CoastalPoint(indexx,CVector2D(xx*2,yy*2)));
CoastalPointsSet.erase(xx + yy*SideSize);
found = true;
break;
}
}
if (!found)
endedChain = true;
}
}
if (Chain.size() > 10)
CoastalPointsChains.push_back(Chain);
}
// (optional) third step: Smooth chains out.
// This is also really dumb.
for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
{
// Bump 1 for smoother.
for (int p = 0; p < 3; ++p)
{
for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j)
{
CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position;
CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f;
}
}
}
// Fourth step: create waves themselves, using those chains. We basically create subchains.
size_t waveSizes = 14; // maximal size in width.
// Construct indices buffer (we can afford one for all of them)
std::vector water_indices;
for (size_t a = 0; a < waveSizes-1;++a)
{
for (size_t rect = 0; rect < 7; ++rect)
{
water_indices.push_back(a*9 + rect);
water_indices.push_back(a*9 + 9 + rect);
water_indices.push_back(a*9 + 1 + rect);
water_indices.push_back(a*9 + 9 + rect);
water_indices.push_back(a*9 + 10 + rect);
water_indices.push_back(a*9 + 1 + rect);
}
}
// Generic indexes, max-length
m_ShoreWaves_VBIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
m_ShoreWaves_VBIndices->m_Owner->UpdateChunkVertices(m_ShoreWaves_VBIndices, &water_indices[0]);
float diff = (rand() % 50) / 5.0f;
for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
{
for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j)
{
if (CoastalPointsChains[i].size()- 1 - j < waveSizes)
break;
size_t width = waveSizes;
// First pass to get some parameters out.
float outmost = 0.0f; // how far to move on the shore.
float avgDepth = 0.0f;
int sign = 1;
CVector2D firstPerp(0,0), perp(0,0), lastPerp(0,0);
for (size_t a = 0; a < waveSizes;++a)
{
lastPerp = perp;
perp = CVector2D(0,0);
int nb = 0;
CVector2D pos = CoastalPointsChains[i][j+a].position;
CVector2D posPlus;
CVector2D posMinus;
if (a > 0)
{
++nb;
posMinus = CoastalPointsChains[i][j+a-1].position;
perp += pos-posMinus;
}
if (a < waveSizes-1)
{
++nb;
posPlus = CoastalPointsChains[i][j+a+1].position;
perp += posPlus-pos;
}
perp /= nb;
perp = CVector2D(-perp.Y,perp.X).Normalized();
if (a == 0)
firstPerp = perp;
if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f)
{
width = a+1;
break;
}
if (m_BlurredNormalMap[ (int)(pos.X/4) + (int)(pos.Y/4)*m_MapSize].Y < 0.9)
{
width = a-1;
break;
}
if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight)
sign = -1;
avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight;
float localOutmost = -2.0f;
while (localOutmost < 0.0f)
{
float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight;
if (depth < 0.0f || depth > 0.6f)
localOutmost += 0.2f;
else
break;
}
outmost += localOutmost;
}
if (width < 5)
{
j += 6;
continue;
}
outmost /= width;
if (outmost > -0.5f)
{
j += 3;
continue;
}
outmost = -0.5f + outmost * m_Waviness/10.0f;
avgDepth /= width;
if (avgDepth > -1.3f)
{
j += 3;
continue;
}
// we passed the checks, we can create a wave of size "width".
WaveObject* shoreWave = new WaveObject;
std::vector vertices;
shoreWave->m_Width = width;
shoreWave->m_TimeDiff = diff;
diff += (rand() % 100) / 25.0f + 4.0f;
for (size_t a = 0; a < width;++a)
{
CVector2D perp = CVector2D(0,0);
int nb = 0;
CVector2D pos = CoastalPointsChains[i][j+a].position;
CVector2D posPlus;
CVector2D posMinus;
if (a > 0)
{
++nb;
posMinus = CoastalPointsChains[i][j+a-1].position;
perp += pos-posMinus;
}
if (a < waveSizes-1)
{
++nb;
posPlus = CoastalPointsChains[i][j+a+1].position;
perp += posPlus-pos;
}
perp /= nb;
perp = CVector2D(-perp.Y,perp.X).Normalized();
SWavesVertex point[9];
float baseHeight = 0.04f;
float halfWidth = (width-1.0f)/2.0f;
float sideNess = sqrtf(clamp( (halfWidth - fabsf(a-halfWidth))/3.0f, 0.0f,1.0f));
point[0].m_UV[0] = a; point[0].m_UV[1] = 8;
point[1].m_UV[0] = a; point[1].m_UV[1] = 7;
point[2].m_UV[0] = a; point[2].m_UV[1] = 6;
point[3].m_UV[0] = a; point[3].m_UV[1] = 5;
point[4].m_UV[0] = a; point[4].m_UV[1] = 4;
point[5].m_UV[0] = a; point[5].m_UV[1] = 3;
point[6].m_UV[0] = a; point[6].m_UV[1] = 2;
point[7].m_UV[0] = a; point[7].m_UV[1] = 1;
point[8].m_UV[0] = a; point[8].m_UV[1] = 0;
point[0].m_PerpVect = perp;
point[1].m_PerpVect = perp;
point[2].m_PerpVect = perp;
point[3].m_PerpVect = perp;
point[4].m_PerpVect = perp;
point[5].m_PerpVect = perp;
point[6].m_PerpVect = perp;
point[7].m_PerpVect = perp;
point[8].m_PerpVect = perp;
static const float perpT1[9] = { 6.0f, 6.05f, 6.1f, 6.2f, 6.3f, 6.4f, 6.5f, 6.6f, 9.7f };
static const float perpT2[9] = { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 3.0f, 3.3f, 3.6f, 9.5f };
static const float perpT3[9] = { 1.1f, 0.7f, -0.2f, 0.0f, 0.6f, 1.3f, 2.2f, 3.6f, 9.0f };
static const float perpT4[9] = { 2.0f, 2.1f, 1.2f, 1.5f, 1.7f, 1.9f, 2.7f, 3.8f, 9.0f };
static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 };
static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 };
static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 };
for (size_t t = 0; t < 9; ++t)
{
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost),
pos.Y+sign*perp.Y*(perpT1[t]+outmost));
point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
pos.Y+sign*perp.Y*(perpT1[t]+outmost));
}
for (size_t t = 0; t < 9; ++t)
{
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost),
pos.Y+sign*perp.Y*(perpT2[t]+outmost));
point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
pos.Y+sign*perp.Y*(perpT2[t]+outmost));
}
for (size_t t = 0; t < 9; ++t)
{
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess),
pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess));
point[t].m_SplashPosition = CVector3D(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), baseHeight + heightT2[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess));
}
for (size_t t = 0; t < 9; ++t)
{
float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost),
pos.Y+sign*perp.Y*(perpT4[t]+outmost));
point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight),
pos.Y+sign*perp.Y*(perpT4[t]+outmost));
}
vertices.push_back(point[8]);
vertices.push_back(point[7]);
vertices.push_back(point[6]);
vertices.push_back(point[5]);
vertices.push_back(point[4]);
vertices.push_back(point[3]);
vertices.push_back(point[2]);
vertices.push_back(point[1]);
vertices.push_back(point[0]);
shoreWave->m_AABB += point[8].m_SplashPosition;
shoreWave->m_AABB += point[8].m_BasePosition;
shoreWave->m_AABB += point[0].m_SplashPosition;
shoreWave->m_AABB += point[0].m_BasePosition;
shoreWave->m_AABB += point[4].m_ApexPosition;
}
if (sign == 1)
{
// Let's do some fancy reversing.
std::vector reversed;
for (int a = width-1; a >= 0; --a)
{
for (size_t t = 0; t < 9; ++t)
reversed.push_back(vertices[a*9+t]);
}
vertices = reversed;
}
j += width/2-1;
shoreWave->m_VBvertices = g_VBMan.Allocate(sizeof(SWavesVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
shoreWave->m_VBvertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBvertices, &vertices[0]);
m_ShoreWaves.push_back(shoreWave);
}
}
}
void WaterManager::RenderWaves(const CFrustum& frustrum)
{
#if CONFIG2_GLES
#warning Fix WaterManager::RenderWaves on GLES
#else
if (g_Renderer.m_SkipSubmit || !m_WaterFancyEffects)
return;
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO);
GLuint attachments[2] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
pglDrawBuffers(2, attachments);
glClearColor(0.0f,0.0f, 0.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
CShaderDefines none;
CShaderProgramPtr shad = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", none);
shad->Bind();
shad->BindTexture(str_waveTex, m_WaveTex);
shad->BindTexture(str_foamTex, m_FoamTex);
shad->Uniform(str_time, (float)m_WaterTexTimer);
shad->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
for (size_t a = 0; a < m_ShoreWaves.size(); ++a)
{
if (!frustrum.IsBoxVisible(CVector3D(0,0,0), m_ShoreWaves[a]->m_AABB))
continue;
CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBvertices;
SWavesVertex *base=(SWavesVertex *)VBchunk->m_Owner->Bind();
// setup data pointers
GLsizei stride = sizeof(SWavesVertex);
shad->VertexPointer(3, GL_FLOAT, stride, &base[VBchunk->m_Index].m_BasePosition);
shad->TexCoordPointer(GL_TEXTURE0, 2, GL_UNSIGNED_BYTE, stride, &base[VBchunk->m_Index].m_UV);
// NormalPointer(gl_FLOAT, stride, &base[m_VBWater->m_Index].m_UV)
pglVertexAttribPointerARB(2, 2, GL_FLOAT, GL_TRUE, stride, &base[VBchunk->m_Index].m_PerpVect); // replaces commented above because my normal is vec2
shad->VertexAttribPointer(str_a_apexPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_ApexPosition);
shad->VertexAttribPointer(str_a_splashPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_SplashPosition);
shad->VertexAttribPointer(str_a_retreatPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_RetreatPosition);
shad->AssertPointersBound();
shad->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff);
shad->Uniform(str_width, (int)m_ShoreWaves[a]->m_Width);
u8* indexBase = m_ShoreWaves_VBIndices->m_Owner->Bind();
glDrawElements(GL_TRIANGLES, (GLsizei) (m_ShoreWaves[a]->m_Width-1)*(7*6),
GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_ShoreWaves_VBIndices->m_Index));
shad->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff + 6.0f);
// TODO: figure out why this doesn't work.
//g_Renderer.m_Stats.m_DrawCalls++;
//g_Renderer.m_Stats.m_WaterTris += m_ShoreWaves_VBIndices->m_Count / 3;
CVertexBuffer::Unbind();
}
shad->Unbind();
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
#endif
}
///////////////////////////////////////////////////////////////////
// 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;
}
}
delete[] normals;
}
///////////////////////////////////////////////////////////////////
// Calculate the strength of the wind at a given point on the map.
// This is too slow and should support limited recomputation.
void WaterManager::RecomputeWindStrength()
{
if (m_WindStrength == NULL)
m_WindStrength = new float[m_MapSize*m_MapSize];
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
float waterLevel = m_WaterHeight;
CVector2D windDir = CVector2D(cos(m_WindAngle),sin(m_WindAngle));
CVector2D perp = CVector2D(-windDir.Y, windDir.X);
// Our kernel will sample 5 points going towards the wind (generally).
int kernel[5][2] = { {(int)windDir.X*2,(int)windDir.Y*2}, {(int)windDir.X*5,(int)windDir.Y*5}, {(int)windDir.X*9,(int)windDir.Y*9}, {(int)windDir.X*16,(int)windDir.Y*16}, {(int)windDir.X*25,(int)windDir.Y*25} };
float* Temp = new float[m_MapSize*m_MapSize];
std::fill(Temp, Temp + m_MapSize*m_MapSize, 1.0f);
for (size_t j = 0; j < m_MapSize; ++j)
for (size_t i = 0; i < m_MapSize; ++i)
{
float curHeight = terrain->GetVertexGroundLevel(i,j);
if (curHeight >= waterLevel)
{
Temp[j*m_MapSize + i] = 0.3f; // blurs too strong otherwise
continue;
}
if (terrain->GetVertexGroundLevel(i + ceil(windDir.X),j + ceil(windDir.Y)) < waterLevel)
continue;
// Calculate how dampened our waves should be.
float tendency = 0.0f;
float oldHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[4][0],j+kernel[4][1]));
float currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[3][0],j+kernel[3][1]));
float avgheight = oldHeight + currentHeight;
tendency = currentHeight - oldHeight;
oldHeight = currentHeight;
currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[2][0],j+kernel[2][1]));
avgheight += currentHeight;
tendency += currentHeight - oldHeight;
oldHeight = currentHeight;
currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[1][0],j+kernel[1][1]));
avgheight += currentHeight;
tendency += currentHeight - oldHeight;
oldHeight = currentHeight;
currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[0][0],j+kernel[0][1]));
avgheight += currentHeight;
tendency += currentHeight - oldHeight;
float baseLevel = std::max(0.0f,1.0f - (avgheight/5.0f-waterLevel)/20.0f);
baseLevel *= baseLevel;
tendency /= 15.0f;
baseLevel -= tendency; // if the terrain was sloping downwards, increase baselevel. Otherwise reduce.
baseLevel = clamp(baseLevel,0.0f,1.0f);
// Draw on map. This is pretty slow.
float length = 35.0f * (1.0f-baseLevel/1.8f);
for (float y = 0; y < length; y += 0.6f)
{
int xx = clamp(i - y * windDir.X,0.0f,(float)(m_MapSize-1));
int yy = clamp(j - y * windDir.Y,0.0f,(float)(m_MapSize-1));
Temp[yy*m_MapSize + xx] = Temp[yy*m_MapSize + xx] < (0.0f+baseLevel/1.5f) * (1.0f-y/length) + y/length * 1.0f ?
Temp[yy*m_MapSize + xx] : (0.0f+baseLevel/1.5f) * (1.0f-y/length) + y/length * 1.0f;
}
}
int blurKernel[4][2] = { {(int)ceil(windDir.X),(int)ceil(windDir.Y)}, {(int)windDir.X*3,(int)windDir.Y*3}, {(int)ceil(perp.X),(int)ceil(perp.Y)}, {(int)-ceil(perp.X),(int)-ceil(perp.Y)} };
float blurValue;
for (size_t j = 2; j < m_MapSize-2; ++j)
for (size_t i = 2; i < m_MapSize-2; ++i)
{
blurValue = Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]];
m_WindStrength[j*m_MapSize + i] = blurValue * 0.25f;
}
delete[] Temp;
}
////////////////////////////////////////////////////////////////////////
// TODO: This will always recalculate for now
void WaterManager::SetMapSize(size_t size)
{
// TODO: Im' blindly trusting the user here.
m_MapSize = size;
m_NeedInfoUpdate = true;
m_updatei0 = 0;
m_updatei1 = size;
m_updatej0 = 0;
m_updatej1 = size;
SAFE_ARRAY_DELETE(m_DistanceHeightmap);
SAFE_ARRAY_DELETE(m_BlurredNormalMap);
SAFE_ARRAY_DELETE(m_WindStrength);
}
////////////////////////////////////////////////////////////////////////
// This will set the bools properly
void WaterManager::UpdateQuality()
{
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_WATERREFRACTION) != m_WaterRefraction) {
m_WaterRefraction = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION);
m_NeedsReloading = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION) != m_WaterReflection) {
m_WaterReflection = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION);
m_NeedsReloading = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER) != m_WaterShadows) {
m_WaterShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWSONWATER);
m_NeedsReloading = true;
}
}
bool WaterManager::WillRenderFancyWater()
{
if (!g_Renderer.GetCapabilities().m_PrettyWater)
return false;
if (!m_RenderWater || m_WaterUgly)
return false;
return true;
}
Index: ps/trunk/source/renderer/WaterManager.h
===================================================================
--- ps/trunk/source/renderer/WaterManager.h (revision 16387)
+++ ps/trunk/source/renderer/WaterManager.h (revision 16388)
@@ -1,192 +1,198 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* Water settings (speed, height) and texture management
*/
#ifndef INCLUDED_WATERMANAGER
#define INCLUDED_WATERMANAGER
#include "graphics/Texture.h"
#include "lib/ogl.h"
#include "maths/Matrix3D.h"
#include "maths/Vector2D.h"
#include "ps/Overlay.h"
#include "renderer/VertexBufferManager.h"
class CSimulation2;
class CFrustum;
struct CoastalPoint;
struct WaveObject;
/**
* Class WaterManager: Maintain rendering-related water settings and textures
* Anything that affects gameplay should go in CcmpWaterManager.cpp and passed to this (possibly as copy).
*/
class WaterManager
{
public:
CTexturePtr m_WaterTexture[60];
CTexturePtr m_NormalMap[60];
float* m_WindStrength; // How strong the waves are at point X. % of waviness.
float* m_DistanceHeightmap; // How far from the shore a point is. Manhattan
CVector3D* m_BlurredNormalMap; // Cache a slightly blurred map of the normals of the terrain.
// Waves vertex buffers
std::vector< WaveObject* > m_ShoreWaves; // TODO: once we get C++11, remove pointer
// Waves indices buffer. Only one since All Wave Objects have the same.
CVertexBuffer::VBChunk* m_ShoreWaves_VBIndices;
size_t m_MapSize;
ssize_t m_TexSize;
CTexturePtr m_WaveTex;
CTexturePtr m_FoamTex;
GLuint m_depthTT;
GLuint m_FancyTextureNormal;
GLuint m_FancyTextureOther;
GLuint m_FancyTextureDepth;
GLuint m_ReflFboDepthTexture;
GLuint m_RefrFboDepthTexture;
// used to know what to update when updating parts of the terrain only.
u32 m_updatei0;
u32 m_updatej0;
u32 m_updatei1;
u32 m_updatej1;
int m_WaterCurrentTex;
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.
// Use real depth or use the fake precomputed one.
bool m_WaterRealDepth;
// 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;
// requires also recreating the super fancy information.
bool m_NeedInfoUpdate;
float m_WaterHeight;
double m_WaterTexTimer;
float m_RepeatPeriod;
// Reflection and refraction textures for fancy water
GLuint m_ReflectionTexture;
GLuint m_RefractionTexture;
size_t m_ReflectionTextureSize;
size_t m_RefractionTextureSize;
// framebuffer objects
GLuint m_RefractionFbo;
GLuint m_ReflectionFbo;
GLuint m_FancyEffectsFBO;
// Model-view-projection matrices for reflected & refracted cameras
// (used to let the vertex shader do projective texturing)
CMatrix3D m_ReflectionMatrix;
CMatrix3D m_RefractionMatrix;
// Water parameters
std::wstring m_WaterType; // Which texture to use.
CColor m_WaterColor; // Color of the water without refractions. This is what you're seeing when the water's deep or murkiness high.
CColor m_WaterTint; // Tint of refraction in the water.
float m_Waviness; // How big the waves are.
float m_Murkiness; // How murky the water is.
float m_WindAngle; // In which direction the water waves go.
public:
WaterManager();
~WaterManager();
/**
* LoadWaterTextures: Load water textures from within the
* progressive load framework.
*
* @return 0 if loading has completed, a value from 1 to 100 (in percent of completion)
* if more textures need to be loaded and a negative error value on failure.
*/
int LoadWaterTextures();
/**
* Resize: Updates the fancy water textures so that water will render correctly
* with fancy water.
*/
void Resize();
/**
+ * ReloadWaterNormalTextures: Reload the normal textures so that changing
+ * water type in Atlas will actually do the right thing.
+ */
+ void ReloadWaterNormalTextures();
+
+ /**
* UnloadWaterTextures: Free any loaded water textures and reset the internal state
* so that another call to LoadWaterTextures will begin progressive loading.
*/
void UnloadWaterTextures();
/**
* RecomputeWindStrength: calculates the intensity of waves
*/
void RecomputeWindStrength();
/**
* RecomputeDistanceHeightmap: recalculates (or calculates) the distance heightmap.
*/
void RecomputeDistanceHeightmap();
/**
* RecomputeBlurredNormalMap: calculates the blurred normal map of the terrain. Slow.
*/
void RecomputeBlurredNormalMap();
/**
* CreateWaveMeshes: Creates the waves objects (and meshes).
*/
void CreateWaveMeshes();
/**
* Updates the map size. Will trigger a complete recalculation of fancy water information the next turn.
*/
void SetMapSize(size_t size);
/**
* Updates the settings to the one from the renderer, and sets m_NeedsReloading.
*/
void UpdateQuality();
/**
* Returns true if fancy water shaders will be used (i.e. the hardware is capable
* and it hasn't been configured off)
*/
bool WillRenderFancyWater();
void RenderWaves(const CFrustum& frustrum);
};
#endif // INCLUDED_WATERMANAGER
Index: ps/trunk/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp (revision 16387)
+++ ps/trunk/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp (revision 16388)
@@ -1,213 +1,217 @@
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "MessageHandler.h"
#include "../CommandProc.h"
#include "graphics/LightEnv.h"
#include "graphics/Terrain.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "renderer/PostprocManager.h"
#include "renderer/Renderer.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpWaterManager.h"
namespace AtlasMessage {
sEnvironmentSettings GetSettings()
{
sEnvironmentSettings s;
CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
s.waterheight = cmpWaterManager->GetExactWaterLevel(0, 0) / (65536.f * HEIGHT_SCALE);
WaterManager* wm = g_Renderer.GetWaterManager();
s.watertype = wm->m_WaterType;
s.waterwaviness = wm->m_Waviness;
s.watermurkiness = wm->m_Murkiness;
s.windangle = wm->m_WindAngle;
// CColor colours
#define COLOUR(A, B) A = Colour((int)(B.r*255), (int)(B.g*255), (int)(B.b*255))
COLOUR(s.watercolour, wm->m_WaterColor);
COLOUR(s.watertint, wm->m_WaterTint);
#undef COLOUR
float sunrotation = g_LightEnv.GetRotation();
if (sunrotation > (float)M_PI)
sunrotation -= (float)M_PI*2;
s.sunrotation = sunrotation;
s.sunelevation = g_LightEnv.GetElevation();
s.posteffect = g_Renderer.GetPostprocManager().GetPostEffect();
s.skyset = g_Renderer.GetSkyManager()->GetSkySet();
s.fogfactor = g_LightEnv.m_FogFactor;
s.fogmax = g_LightEnv.m_FogMax;
s.brightness = g_LightEnv.m_Brightness;
s.contrast = g_LightEnv.m_Contrast;
s.saturation = g_LightEnv.m_Saturation;
s.bloom = g_LightEnv.m_Bloom;
// RGBColor (CVector3D) colours
#define COLOUR(A, B) A = Colour((int)(B.X*255), (int)(B.Y*255), (int)(B.Z*255))
s.sunoverbrightness = MaxComponent(g_LightEnv.m_SunColor);
// clamp color to [0..1] before packing into u8 triplet
if(s.sunoverbrightness > 1.0f)
g_LightEnv.m_SunColor *= 1.0/s.sunoverbrightness; // (there's no operator/=)
// no component was above 1.0, so reset scale factor (don't want to darken)
else
s.sunoverbrightness = 1.0f;
COLOUR(s.suncolour, g_LightEnv.m_SunColor);
COLOUR(s.terraincolour, g_LightEnv.m_TerrainAmbientColor);
COLOUR(s.unitcolour, g_LightEnv.m_UnitsAmbientColor);
COLOUR(s.fogcolour, g_LightEnv.m_FogColor);
#undef COLOUR
return s;
}
void SetSettings(const sEnvironmentSettings& s)
{
CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
cmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(s.waterheight * (65536.f * HEIGHT_SCALE)));
WaterManager* wm = g_Renderer.GetWaterManager();
wm->m_Waviness = s.waterwaviness;
wm->m_Murkiness = s.watermurkiness;
wm->m_WindAngle = s.windangle;
- wm->m_WaterType = *s.watertype;
+ if (wm->m_WaterType != *s.watertype)
+ {
+ wm->m_WaterType = *s.watertype;
+ wm->ReloadWaterNormalTextures();
+ }
#define COLOUR(A, B) B = CColor(A->r/255.f, A->g/255.f, A->b/255.f, 1.f)
COLOUR(s.watercolour, wm->m_WaterColor);
COLOUR(s.watertint, wm->m_WaterTint);
#undef COLOUR
g_LightEnv.SetRotation(s.sunrotation);
g_LightEnv.SetElevation(s.sunelevation);
CStrW posteffect = *s.posteffect;
if (posteffect.length() == 0)
posteffect = L"default";
g_Renderer.GetPostprocManager().SetPostEffect(posteffect);
CStrW skySet = *s.skyset;
if (skySet.length() == 0)
skySet = L"default";
g_Renderer.GetSkyManager()->SetSkySet(skySet);
g_LightEnv.m_FogFactor = s.fogfactor;
g_LightEnv.m_FogMax = s.fogmax;
g_LightEnv.m_Brightness = s.brightness;
g_LightEnv.m_Contrast = s.contrast;
g_LightEnv.m_Saturation = s.saturation;
g_LightEnv.m_Bloom = s.bloom;
#define COLOUR(A, B) B = RGBColor(A->r/255.f, A->g/255.f, A->b/255.f)
COLOUR(s.suncolour, g_LightEnv.m_SunColor);
g_LightEnv.m_SunColor *= s.sunoverbrightness;
COLOUR(s.terraincolour, g_LightEnv.m_TerrainAmbientColor);
COLOUR(s.unitcolour, g_LightEnv.m_UnitsAmbientColor);
COLOUR(s.fogcolour, g_LightEnv.m_FogColor);
#undef COLOUR
}
BEGIN_COMMAND(SetEnvironmentSettings)
{
sEnvironmentSettings m_OldSettings, m_NewSettings;
void Do()
{
m_OldSettings = GetSettings();
m_NewSettings = msg->settings;
Redo();
}
void Redo()
{
SetSettings(m_NewSettings);
}
void Undo()
{
SetSettings(m_OldSettings);
}
void MergeIntoPrevious(cSetEnvironmentSettings* prev)
{
prev->m_NewSettings = m_NewSettings;
}
};
END_COMMAND(SetEnvironmentSettings)
BEGIN_COMMAND(RecalculateWaterData)
{
void Do()
{
Redo();
}
void Redo()
{
CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
cmpWaterManager->RecomputeWaterData();
}
void Undo()
{
Redo();
}
};
END_COMMAND(RecalculateWaterData)
QUERYHANDLER(GetEnvironmentSettings)
{
msg->settings = GetSettings();
}
QUERYHANDLER(GetSkySets)
{
std::vector skies = g_Renderer.GetSkyManager()->GetSkySets();
msg->skysets = std::vector(skies.begin(), skies.end());
}
QUERYHANDLER(GetPostEffects)
{
std::vector effects = g_Renderer.GetPostprocManager().GetPostEffects();
msg->posteffects = std::vector(effects.begin(), effects.end());
}
}