/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2015 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
#ifndef OSGEARTH_DRIVER_SIMPLE_OCEAN_SHADERS
#define OSGEARTH_DRIVER_SIMPLE_OCEAN_SHADERS 1

#include <osgEarth/VirtualProgram>

namespace
{
    static char source_vertProxy[] =
        "#version " GLSL_VERSION_STR "\n"
        GLSL_DEFAULT_PRECISION_FLOAT "\n"

        "vec2 ocean_xyz_to_spherical(in vec3 xyz) \n"
        "{ \n"
        "    float r = length(xyz); \n"
        "    float lat = acos(xyz.z/r); \n"
        "    float lon = atan(xyz.y, xyz.x); \n"
        "    return vec2(lon,lat); \n"
        "} \n"

        "uniform mat4 osg_ViewMatrixInverse; \n"
        "uniform mat4 osg_ViewMatrix; \n"
        "uniform float osg_FrameTime; \n"
        "uniform sampler2D ocean_data; \n"                 // heightfield encoded into 16 bit texture
        "uniform float ocean_seaLevel; \n"                 // sea level offset

        "varying vec4 osg_FrontColor; \n"
        "varying vec4 ocean_surface_tex_coord; \n"
        "varying float ocean_v_msl; \n"                    // elevation (MSL) of camera
        "varying float ocean_v_range; \n"                  // distance from camera to current vertex
        "varying float ocean_v_enorm; \n"                  // normalized terrain height at vertex [0..1]

        "varying vec3 oe_Normal; \n" 
        "uniform sampler2D ocean_surface_tex; \n"        // surface texture
        "uniform bool ocean_has_surface_tex; \n"         // whether there's a surface texture

        "void oe_ocean_vertex(inout vec4 VertexVIEW) \n"
        "{ \n"
        "   osg_FrontColor = gl_Color; \n"

        // adjust our vert for the sea level - extrude along the normal vector 
        // (this must be done in view space to preserve precision)
        "   vec4 mvVertex = VertexVIEW; \n"
        "   vec3 mvNormal = oe_Normal; \n"
        "   vec4 mvVertex2 = vec4(mvVertex.xyz + (mvNormal * ocean_seaLevel), mvVertex.w ); \n"

        "   VertexVIEW = mvVertex2; \n"

        // read normalized [0..1] elevation data from the height texture:
        "   ocean_v_enorm = texture2D( ocean_data, gl_MultiTexCoord0.st ).r; \n"

        // send interpolated params to the fs:
        "   vec4 eye = osg_ViewMatrixInverse * vec4(0,0,0,1); \n"

        // height of camera above sea level:
        "   ocean_v_msl = length(eye.xyz/eye.w) - 6378137.0 + ocean_seaLevel; \n"

        // disatnce to camera:
        "   ocean_v_range = ocean_v_msl; \n"

        // scale the texture mapping to something reasonable:
        "   vec4 worldVertex = osg_ViewMatrixInverse * mvVertex; \n"
        "   vec2 lonlat = ocean_xyz_to_spherical( worldVertex.xyz/worldVertex.w ); \n"
        "   ocean_surface_tex_coord.xy = lonlat / 0.0005; \n"
        "   ocean_surface_tex_coord.zw = ocean_surface_tex_coord.xy; \n"
        "   ocean_surface_tex_coord.w -= mod(0.1*osg_FrameTime,25.0)/25.0;\n"

        // fake waves:
        "   if (ocean_has_surface_tex) { \n"
        "       vec4 t0 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.xy); \n"
        "       vec4 t1 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.zw); \n"
        "       oe_Normal = normalize((3.0*t0.rgb*t1.rgb)*2.0-1.0); \n"
        "   } \n"
        "} \n";


    char source_fragProxy[] = 
        "#version " GLSL_VERSION_STR "\n"
        GLSL_DEFAULT_PRECISION_FLOAT "\n"

        // clamps a value to the vmin/vmax range, then re-maps it to the r0/r1 range:
        "float ocean_remap( float val, float vmin, float vmax, float r0, float r1 ) \n"
        "{ \n"
        "    float vr = (clamp(val, vmin, vmax)-vmin)/(vmax-vmin); \n"
        "    return r0 + vr * (r1-r0); \n"
        "} \n"

        "varying float ocean_v_msl; \n"                  // elevation (MSL) of camera
        "varying float ocean_v_range; \n"                // distance from camera to current vertex
        "varying float ocean_v_enorm; \n"                // normalized terrain height at vertex [0..1]
        "varying vec4 ocean_surface_tex_coord; \n"       // surface texture coords

        "uniform bool ocean_has_surface_tex; \n"         // whether there's a surface texture
        "uniform sampler2D ocean_surface_tex; \n"        // surface texture
        "uniform float ocean_seaLevel; \n"               // sea level offset
        "uniform float ocean_lowFeather; \n"             // offset from sea level at which to start feathering out
        "uniform float ocean_highFeather; \n"            // offset from sea level at which to stop feathering out
        "uniform vec4  ocean_baseColor; \n"              // base ocean color before processing

        "uniform float ocean_max_range; \n"              // maximum visible distance of the ocean
        "uniform float ocean_fade_range; \n"             // distance over which to fade in the ocean

        "void oe_ocean_fragment(inout vec4 color) \n"
        "{ \n"
        "    color = ocean_baseColor; \n"

        // amplify the range's effect on alpha when the camera elevation gets low
        "    float rangeFactor = ocean_remap( ocean_v_msl, -10000.0, 10000.0, 10.0, 1.0 ); \n"

        // affect alpha based on the distance from the camera
        "    float rangeEffect = ocean_remap(\n"
        "       ocean_v_range,\n"
        "       ocean_max_range - ocean_fade_range, ocean_max_range * rangeFactor,\n"
        "       1.0, 0.0); \n"

        // start with the surface texture.
        "    if (ocean_has_surface_tex) \n"
        "    { \n"
        "        vec4 texel1 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.xy); \n"
        "        vec4 texel2 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.zw); \n"
        "        vec4 texel  = vec4(texel1.rgb*texel2.rgb, texel2.a); \n"
        "        vec4 nearColor = vec4(mix(color.rgb, texel.rgb, texel.a), color.a); \n"
        "        color = mix(color, nearColor, rangeEffect); \n"
        "    } \n"

        // un-normalize the heightfield data
        "    float terrainHeight = (ocean_v_enorm * 65535.0) - 32768.0; \n"

        // heightfield's effect on alpha [0..1]
        "    float terrainEffect = ocean_remap( terrainHeight, ocean_seaLevel+ocean_lowFeather, ocean_seaLevel+ocean_highFeather, 1.0, 0.0 ); \n" 

        // color it
        "    color = vec4( color.rgb, terrainEffect * rangeEffect ); \n" //* color.a ); \n"

        //"    color = vec4( 1, 0, 0, 1 ); \n" // debugging
        "} \n";



    char source_vertMask[] =
        "#version " GLSL_VERSION_STR "\n"
        GLSL_DEFAULT_PRECISION_FLOAT "\n"

        "vec2 ocean_xyz_to_spherical(in vec3 xyz) \n"
        "{ \n"
        "    float r = length(xyz); \n"
        "    float lat = acos(xyz.z/r); \n"
        "    float lon = atan(xyz.y, xyz.x); \n"
        "    return vec2(lon,lat); \n"
        "} \n"

        "uniform mat4 osg_ViewMatrixInverse; \n"
        "uniform float osg_FrameTime; \n"
        "uniform float ocean_seaLevel; \n"
        "varying vec4 osg_FrontColor; \n"
        "varying vec4 ocean_mask_tex_coord; \n"
        "varying vec4 ocean_surface_tex_coord; \n"
        "varying float ocean_v_msl; \n"
        "varying float ocean_v_range; \n"

        "void oe_ocean_vertex(inout vec4 VertexMODEL) \n"
        "{ \n"
        "   osg_FrontColor = gl_Color; \n"

        // adjust our vert for the sea level - extrude along the normal vector 
        // (this must be done in modelview space to preserve precision)
        "   vec4 mvVertex = VertexMODEL; \n"
        "   vec3 mvNormal = gl_NormalMatrix * gl_Normal; \n"
        "   vec4 mvVertex2 = vec4(mvVertex.xyz + (mvNormal * ocean_seaLevel), mvVertex.w ); \n"

        "   VertexMODEL = mvVertex2; \n"
        //"   gl_Position = gl_ProjectionMatrix * mvVertex2; \n"

        // ocean mask texture coordinate:
        "   ocean_mask_tex_coord = gl_MultiTexCoord0; \n"

        // send interpolated params to the fs:
        "   vec4 eye = osg_ViewMatrixInverse * vec4(0,0,0,1); \n"

        // height of camera above sea level:
        "   ocean_v_msl = length(eye.xyz/eye.w) - 6378137.0 + ocean_seaLevel; \n"

        // disatnce to camera:
        "   ocean_v_range = ocean_v_msl; \n"

        // scale the texture mapping to something reasonable:
        "   vec4 worldVertex = osg_ViewMatrixInverse * mvVertex; \n"
        "   vec2 lonlat = ocean_xyz_to_spherical( worldVertex.xyz/worldVertex.w ); \n"
        "   ocean_surface_tex_coord.xy = lonlat / 0.0005; \n"
        "   ocean_surface_tex_coord.zw = ocean_surface_tex_coord.xy; \n"
        "   ocean_surface_tex_coord.x += mod(osg_FrameTime,100.0)/100.0;\n"
        //"   ocean_surface_tex_coord.w -= mod(osg_FrameTime,50.0)/50.0;\n"
        "} \n";


    char source_fragMask[] = 

        "#version " GLSL_VERSION_STR "\n"
        GLSL_DEFAULT_PRECISION_FLOAT "\n"

        // clamps a value to the vmin/vmax range, then re-maps it to the r0/r1 range:
        "float ocean_remap( float val, float vmin, float vmax, float r0, float r1 ) \n"
        "{ \n"
        "    float vr = (clamp(val, vmin, vmax)-vmin)/(vmax-vmin); \n"
        "    return r0 + vr * (r1-r0); \n"
        "} \n"

        "varying float ocean_v_msl; \n"                  // elevation (MSL) of camera
        "varying float ocean_v_range; \n"                // distance from camera to current vertex
        "varying vec4 ocean_mask_tex_coord; \n"          // tex coord for the mask texture
        "varying vec4 ocean_surface_tex_coord; \n"       // surface texture coords

        "uniform sampler2D ocean_data; \n"               // ocean mask texture
        "uniform bool ocean_has_surface_tex; \n"         // whether there's a surface texture
        "uniform sampler2D ocean_surface_tex; \n"        // surface texture

        "uniform float ocean_seaLevel; \n"               // sea level offset
        "uniform vec4 ocean_baseColor; \n"               // base ocean color before processing

        "uniform float ocean_max_range; \n"              // maximum visible distance of the ocean
        "uniform float ocean_fade_range; \n"             // distance over which to fade in the ocean

        "void oe_ocean_fragment(inout vec4 color) \n"
        "{ \n"
        "    color = ocean_baseColor; \n"

        // amplify the range's effect on alpha when the camera elevation gets low
        "    float rangeFactor = ocean_remap( ocean_v_msl, -10000.0, 10000.0, 10.0, 1.0 ); \n"

        // affect alpha based on the distance from the camera
        "    float rangeEffect = ocean_remap(\n"
        "       ocean_v_range,\n"
        "       ocean_max_range - ocean_fade_range, ocean_max_range * rangeFactor,\n"
        "       1.0, 0.0); \n"

        // start with the surface texture.
        "    if (ocean_has_surface_tex) \n"
        "    { \n"
        "        vec4 texel1 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.xy); \n"
        "        vec4 texel2 = texture2D(ocean_surface_tex, ocean_surface_tex_coord.zw); \n"
        "        vec4 texel  = vec4(texel1.rgb*texel2.rgb, texel2.a); \n"
        "        vec4 nearColor = vec4(mix(color.rgb, texel.rgb, texel.a), color.a); \n"
        "        color = mix(color, nearColor, rangeEffect); \n"
        "    } \n"

        // effect of the terrain mask [0..1] in the alpha component.
        "    float maskEffect = texture2D(ocean_data, ocean_mask_tex_coord.xy).a; \n"

        // color it
        "    color = vec4( color.rgb, maskEffect * rangeEffect * color.a ); \n"

        //"    color = vec4( 1, 0, 0, 1 ); \n" // debugging
        "} \n";
}

#endif // OSGEARTH_DRIVER_SIMPLE_OCEAN_SHADERS
