//
// Fragment shader, Tiled Directional Flow
//
// (c) 2010 frans van hoesel, university of groningen
//
//
// this shader creates animated water by transforming normalmaps
// the scaling and rotation of the normalmaps is done per tile
// and is constant per tile. Each tile can have its own parameters
// for rotation, scaling, and speed of translation
// To hide the seams between the tiles, all seams have another tile
// centered over the seam. The opacity of the tiles decreases towards the
// edge of the tiles, so the edge isn't visible at all.
// Basically, all points have four tiles (A,B,C and D), mixed together
// (although at the edges the contribution of a tile to this mix is 
// reduced to zero).
// The mixing of the tiles each with different parameters gives a nice
// animated look to the water. It is no longer just sliding in one direction, but
// appears to move more like real water. 
//
// The resulting sum of normalmaps, is used to calculate the refraction of the clouds 
// in a cube map and can also be used for other nice effects. In this example the 
// colormap of the material under water is distorted to fake some kind of refraction
// (for this example the water is a bit too transparent, but it shows this refraction
// better) 
//
// A flowmap determines in the red and green channel the normalized direction of the
// flow and in the blue channel wavelength.
// The alpha channel is used for the transparency of the water. Near the edge, the 
// water becomes less deep and more transparent. Also near the edge, the waves tend
// to be smaller, so the same alpha channel also scales the height of the waves.
// Currently the wavelength is in its own channel (blue), but could be premultiplied
// to the red and green channels. This makes this channel available for changing the 
// speed of the waves per tile.
//
// Further improvements:
// Besides the obvious improvements mentioned in the code (such as premultiplying
// the direction of the waves with the scale, or moving the texscale multiplication
// to the texture coordinates), one could get rid of tiling in this code and pass it 
// tiled geometry. This way the whole lookup of the flowmap (which is constant over 
// each tile) could be moved to the vertexshader, removing the the construction of 
// the flow rotation matrix. As this is done 4 times per pixel, it might give a big 
// performance boost (one does need to pass on 4 constant matrices to the fragment
// shader, which will cost you a bit of performance).
// 
//
//    Based on a work of Frans van Hoesel, (c) 2010
//
//
// 
//////////////////////////////////////////////////////////////////////////////////


// fragment shader

#version 120

uniform sampler2D   normalTexture; // 0
uniform samplerCube backgroundTexture; // 1
uniform sampler2D   flowTexture; // 3
uniform sampler2D   waterTexture; //4

varying vec3  normal;
varying vec3  eyeDir; 
varying vec2  texCoord;
varying float time;

void main (void)
{

    // texScale determines the amount of tiles generated.
    float texScale = 35.0;
    // texScale2 determines the repeat of the water texture (the normalmap) itself
    float texScale2 = 100.0;
    float myangle;
    float transp;
    vec3 mynormal;

    vec2 mytexCoord = texCoord * texScale;
    // ff is the factor that blends the tiles.
 	vec2 ff =  abs(2.0*(fract(mytexCoord)) - 1.0) -0.5; 	  
    // take a third power, to make the area with more or less equal contribution
    // of more tile bigger
	ff = 0.5 - 4.0*ff*ff*ff;
	  // ffscale is a scaling factor that compensates for the effect that
	  // adding normal vectors together tends to get them closer to the average normal
	  // which is a visible effect. For more or less random waves, this factor
	  // compensates for it 
	vec2 ffscale = sqrt(ff*ff + (1.0-ff)*(1.0-ff));
    vec2 Tcoord = texCoord  * texScale2;
    
    // offset makes the water move
	vec2 offset = vec2(time,0);
    
    // I scale the texCoord and floor the value to create the tiling
    // This could have be replace by an extremely lo-res texture lookup
    // using NEAREST pixel.
    vec3 flowColor = texture2D( flowTexture, floor(mytexCoord)/ texScale).rgb;
    
    // flowdir is supposed to go from -1 to 1 and the line below
    // used to be flowColor.xy * 2.0 - 1.0, but saves a multiply by
    // moving this factor two to the flowColor.b
    vec2 flowdir = flowColor.xy -0.5;    
   
    // flowColor.b is used for the inverse length of the wave
    // could be premultiplied in flowColor.xy, but this is easier for editing flowtexture
    flowdir *= flowColor.b;
    
    // build the rotation matrix that scales and rotates the complete tile
    mat2 rotmat = mat2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);
    
    // this is the normal for tile A
    vec2 normalT0 = texture2D(normalTexture, rotmat * Tcoord - offset).rg;
    
    // for the next tile (B) I shift by half the tile size in the x-direction
    flowColor = texture2D( flowTexture, floor((mytexCoord + vec2(0.5,0)))/ texScale ).rgb;
    flowdir = flowColor.b * (flowColor.xy - 0.5);
    rotmat = mat2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);
	  
	  // and the normal for tile B...
	  // multiply the offset by some number close to 1 to give it a different speed
	  // The result is that after blending the water starts to animate and look
	  // realistic, instead of just sliding in some direction.
	  // This is also why I took the third power of ff above, so the area where the
	  // water animates is as big as possible
	  // adding a small arbitrary constant isn't really needed, but helps to show
	  // a bit less tiling in the beginning of the program. After a few seconds, the
	  // tiling cannot be seen anymore so this constant could be removed.
	  // For the quick demo I leave them in. In a simulation that keeps running for
	  // some time, you could just as well remove these small constant offsets
	  vec2 normalT1 = texture2D(normalTexture, rotmat * Tcoord - offset*1.06+0.62).rg ; 
	  
	  // blend them together using the ff factor
	  // use ff.x because this tile is shifted in the x-direction 
	  vec2 normalTAB = ff.x * normalT0 + (1.0-ff.x) * normalT1;
    
    // the scaling of normalTab and normalTCD is moved to a single scale of
    // normalT later in the program, which is mathematically identical to
	// normalTAB = (normalTAB - 0.5) / ffscale.x + 0.5;
	  
	  // tile C is shifted in the y-direction 
    flowColor = texture2D( flowTexture, floor((mytexCoord + vec2(0.0,0.5)))/ texScale ).rgb;
    flowdir = flowColor.b * (flowColor.xy - 0.5);
    rotmat = mat2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);	      
    normalT0 = texture2D(normalTexture, rotmat * Tcoord - offset*1.33+0.27).rg;
	   
	// tile D is shifted in both x- and y-direction
	flowColor = texture2D( flowTexture, floor((mytexCoord + vec2(0.5,0.5)))/ texScale ).rgb;
    flowdir = flowColor.b * (flowColor.xy - 0.5);
    rotmat = mat2(flowdir.x, -flowdir.y, flowdir.y ,flowdir.x);
	normalT1 = texture2D(normalTexture, rotmat * Tcoord - offset*1.24).rg;
	   
	vec2 normalTCD = ff.x * normalT0 + (1.0-ff.x) * normalT1;
	  // normalTCD = (normalTCD - 0.5) / ffscale.x + 0.5;
    
    // now blend the two values together
	vec2 normalT = ff.y * normalTAB + (1.0-ff.y) * normalTCD;
	  
	// this line below used to be here for scaling the result
	//normalT = (normalT - 0.5) / ffscale.y + 0.5;

	  // below the new, direct scaling of normalT
    normalT = (normalT - 0.5) / (ffscale.y * ffscale.x);
    // scaling by 0.3 is arbritrary, and could be done by just
    // changing the values in the normal map
    // without this factor, the waves look very strong
    normalT *= 0.3; 
     // to make the water more transparent 
    transp = texture2D( flowTexture, texCoord ).a;
    // and scale the normals with the transparency
    normalT *= transp*transp;
 
    // assume normal of plane is 0,0,1 and produce the normalized sum of adding normalT to it
    mynormal = vec3(normalT,sqrt(1.0-normalT.x*normalT.x - normalT.y*normalT.y));

    vec3 reflectDir = reflect(eyeDir, mynormal);
	
	vec3 backgroundColor = vec3 (textureCube(backgroundTexture, -reflectDir)); 
    
    // very ugly version of fresnel effect
    // but it gives a nice transparent water, but not too transparent
    myangle = dot(mynormal,normalize(eyeDir));
    myangle = 0.95-0.6*myangle*myangle;
    
    // blend in the color of the plane below the water	
    
    // add in a little distortion of the colormap for the effect of a refracted
    // view of the image below the surface. 
    // (this isn't really tested, just a last minute addition
    // and perhaps should be coded differently
    
    // the correct way, would be to use the refract routine, use the alpha channel for depth of 
    // the water (and make the water disappear when depth = 0), add some watercolor to the colormap
    // depending on the depth, and use the calculated refractdir and the depth to find the right
    // pixel in the colormap.... who knows, something for the next version

    vec2 colorCoord;

    colorCoord.x = texCoord.x + mynormal.x / texScale2 * 0.03 * transp;
    colorCoord.y = texCoord.y + mynormal.y / texScale2 * 0.03 * transp;

	vec4 waterColor = texture2D(waterTexture, colorCoord).rgba;
	
	vec3 water = mix(waterColor.rgb, backgroundColor, myangle * transp);

    gl_FragColor = vec4 (water, transp * waterColor.a);
	
	// note that smaller waves appear to move slower than bigger waves
	// one could use the tiles and give each tile a different speed if that
	// is what you want 
}