/*
 * Copyright 1993-2015 NVIDIA Corporation.  All rights reserved.
 *
 * Please refer to the NVIDIA end user license agreement (EULA) associated
 * with this source code for terms and conditions that govern your use of
 * this software. Any use, reproduction, disclosure, or distribution of
 * this software and related documentation outside the terms of the EULA
 * is strictly prohibited.
 *
 */

// GLSL shaders for particle rendering
#define STRINGIFY(A) #A

// particle vertex shader
const char *particleVS = STRINGIFY(
                             uniform float pointRadius;  // point size in world space    \n
                             uniform float pointScale;   // scale to calculate size in pixels \n
                             uniform vec4 eyePos;                                        \n
                             void main()                                                 \n
{
    \n
    vec4 wpos = vec4(gl_Vertex.xyz, 1.0);                   \n
    gl_Position = gl_ModelViewProjectionMatrix *wpos;      \n

    // calculate window-space point size                    \n
    vec4 eyeSpacePos = gl_ModelViewMatrix *wpos;           \n
    float dist = length(eyeSpacePos.xyz);                   \n
    gl_PointSize = pointRadius * (pointScale / dist);       \n

    gl_TexCoord[0] = gl_MultiTexCoord0; // sprite texcoord  \n
    gl_TexCoord[1] = eyeSpacePos;                           \n

    gl_FrontColor = gl_Color;                               \n
}                                                           \n
                         );

// motion blur shaders
const char *mblurVS = STRINGIFY(
                          uniform float timestep;                                    \n
                          void main()                                                \n
{
    \n
    vec3 pos    = gl_Vertex.xyz;                           \n
    vec3 vel    = gl_MultiTexCoord0.xyz;                   \n
    vec3 pos2   = (pos - vel*timestep).xyz;                \n // previous position \n

    gl_Position    = gl_ModelViewMatrix * vec4(pos, 1.0);  \n // eye space
    gl_TexCoord[0] = gl_ModelViewMatrix * vec4(pos2, 1.0); \n

    // aging                                                 \n
    float lifetime = gl_MultiTexCoord0.w;                    \n
    float age = gl_Vertex.w;                                 \n
    float phase = (lifetime > 0.0) ? (age / lifetime) : 1.0; \n // [0, 1]

    gl_TexCoord[1].x = phase;                                \n
    float fade = 1.0 - phase;                                \n
    //  float fade = 1.0;                                        \n

    //    gl_FrontColor = gl_Color;                              \n
    gl_FrontColor = vec4(gl_Color.xyz, gl_Color.w*fade);     \n
}                                                            \n
                      );

// motion blur geometry shader
// - outputs stretched quad between previous and current positions
const char *mblurGS =
    "#version 120\n"
    "#extension GL_EXT_geometry_shader4 : enable\n"
    STRINGIFY(
        uniform float pointRadius;  // point size in world space       \n
        void main()                                                    \n
{
    \n
    // aging                                                   \n
    float phase = gl_TexCoordIn[0][1].x;                       \n
    float radius = pointRadius;                                \n

    // eye space                                               \n
    vec3 pos = gl_PositionIn[0].xyz;                           \n
    vec3 pos2 = gl_TexCoordIn[0][0].xyz;                       \n
    vec3 motion = pos - pos2;                                  \n
    vec3 dir = normalize(motion);                              \n
    float len = length(motion);                                \n

    vec3 x = dir *radius;                                     \n
    vec3 view = normalize(-pos);                               \n
    vec3 y = normalize(cross(dir, view)) * radius;             \n
    float facing = dot(view, dir);                             \n

    // check for very small motion to avoid jitter             \n
    float threshold = 0.01;                                    \n

    if ((len < threshold) || (facing > 0.95) || (facing < -0.95))
    {
        \n
        pos2 = pos;
        \n
        x = vec3(radius, 0.0, 0.0);
        \n
        y = vec3(0.0, -radius, 0.0);
        \n
    }                                                          \n

    // output quad                                             \n
    gl_FrontColor = gl_FrontColorIn[0];                        \n
    gl_TexCoord[0] = vec4(0, 0, 0, phase);                     \n
    gl_TexCoord[1] = gl_PositionIn[0];                         \n
    gl_Position = gl_ProjectionMatrix * vec4(pos + x + y, 1);  \n
    EmitVertex();                                              \n

    gl_TexCoord[0] = vec4(0, 1, 0, phase);                     \n
    gl_TexCoord[1] = gl_PositionIn[0];                         \n
    gl_Position = gl_ProjectionMatrix * vec4(pos + x - y, 1);  \n
    EmitVertex();                                              \n

    gl_TexCoord[0] = vec4(1, 0, 0, phase);                     \n
    gl_TexCoord[1] = gl_PositionIn[0];                         \n
    gl_Position = gl_ProjectionMatrix * vec4(pos2 - x + y, 1); \n
    EmitVertex();                                              \n

    gl_TexCoord[0] = vec4(1, 1, 0, phase);                     \n
    gl_TexCoord[1] = gl_PositionIn[0];                         \n
    gl_Position = gl_ProjectionMatrix * vec4(pos2 - x - y, 1); \n
    EmitVertex();                                              \n
}                                                              \n
    );


const char *simplePS = STRINGIFY(
                           void main()                                                    \n
{
    \n
    gl_FragColor = gl_Color;                                   \n
}                                                              \n
                       );

// render particle without shadows
const char *particlePS = STRINGIFY(
                             uniform float pointRadius;                                         \n
                             void main()                                                        \n
{
    \n
    // calculate eye-space sphere normal from texture coordinates  \n
    vec3 N;                                                        \n
    N.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0);    \n
    float r2 = dot(N.xy, N.xy);                                    \n

    if (r2 > 1.0) discard;   // kill pixels outside circle         \n
    N.z = sqrt(1.0-r2);                                            \n

    //  float alpha = saturate(1.0 - r2);                              \n
    float alpha = clamp((1.0 - r2), 0.0, 1.0);                     \n
    alpha *= gl_Color.w;                                           \n

    gl_FragColor = vec4(gl_Color.xyz * alpha, alpha);              \n
}                                                                  \n
                         );

// render particle including shadows
const char *particleShadowPS = STRINGIFY(
                                   uniform float pointRadius;                                         \n
                                   uniform sampler2D shadowTex;                                       \n
                                   uniform sampler2D depthTex;                                        \n
                                   void main()                                                        \n
{
    \n
    // calculate eye-space sphere normal from texture coordinates  \n
    vec3 N;                                                        \n
    N.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0);    \n
    float r2 = dot(N.xy, N.xy);                                    \n

    if (r2 > 1.0) discard;                                         \n // kill pixels outside circle
    N.z = sqrt(1.0-r2);                                            \n
    vec4 eyeSpacePos = gl_TexCoord[1];                             \n
    vec4 eyeSpaceSpherePos = vec4(eyeSpacePos.xyz + N*pointRadius, 1.0); \n // point on sphere
    vec4 shadowPos = gl_TextureMatrix[0] * eyeSpaceSpherePos;      \n
    vec3 shadow = vec3(1.0) - texture2DProj(shadowTex, shadowPos.xyw).xyz;  \n
    //  float alpha = saturate(1.0 - r2);                              \n
    float alpha = clamp((1.0 - r2), 0.0, 1.0);                     \n
    alpha *= gl_Color.w;                                           \n

    gl_FragColor = vec4(gl_Color.xyz *shadow * alpha, alpha);     \n  // premul alpha
}
                               );

// render particle as lit sphere
const char *particleSpherePS = STRINGIFY(
                                   uniform float pointRadius;                                         \n
                                   uniform vec3 lightDir = vec3(0.577, 0.577, 0.577);                 \n
                                   void main()                                                        \n
{
    \n
    // calculate eye-space sphere normal from texture coordinates  \n
    vec3 N;                                                        \n
    N.xy = gl_TexCoord[0].xy*vec2(2.0, -2.0) + vec2(-1.0, 1.0);    \n
    float r2 = dot(N.xy, N.xy);                                    \n

    if (r2 > 1.0) discard;   // kill pixels outside circle         \n
    N.z = sqrt(1.0-r2);                                            \n

    // calculate depth                                             \n
    vec4 eyeSpacePos = vec4(gl_TexCoord[1].xyz + N*pointRadius, 1.0);   // position of this pixel on sphere in eye space \n
    vec4 clipSpacePos = gl_ProjectionMatrix *eyeSpacePos;         \n
    gl_FragDepth = (clipSpacePos.z / clipSpacePos.w)*0.5+0.5;      \n

    float diffuse = max(0.0, dot(N, lightDir));                    \n

    gl_FragColor = diffuse *gl_Color;                               \n
}                                                                  \n
                               );

const char *passThruVS = STRINGIFY(
                             void main()                                                        \n
{
    \n
    gl_Position = gl_Vertex;                                       \n
    gl_TexCoord[0] = gl_MultiTexCoord0;                            \n
    gl_FrontColor = gl_Color;                                      \n
}                                                                  \n
                         );

const char *texture2DPS = STRINGIFY(
                              uniform sampler2D tex;                                             \n
                              void main()                                                        \n
{
    \n
    gl_FragColor = texture2D(tex, gl_TexCoord[0].xy);              \n
}                                                                  \n
                          );

// 4 tap 3x3 gaussian blur
const char *blurPS = STRINGIFY(
                         uniform sampler2D tex;                                                                \n
                         uniform vec2 texelSize;                                                               \n
                         uniform float blurRadius;                                                             \n
                         void main()                                                                           \n
{
    \n
    vec4 c;                                                                           \n
    c = texture2D(tex, gl_TexCoord[0].xy + vec2(-0.5, -0.5)*texelSize*blurRadius);    \n
    c += texture2D(tex, gl_TexCoord[0].xy + vec2(0.5, -0.5)*texelSize*blurRadius);    \n
    c += texture2D(tex, gl_TexCoord[0].xy + vec2(0.5, 0.5)*texelSize*blurRadius);     \n
    c += texture2D(tex, gl_TexCoord[0].xy + vec2(-0.5, 0.5)*texelSize*blurRadius);    \n
    c *= 0.25;                                                                        \n

    gl_FragColor = c;                                                                 \n
}                                                                                     \n
                     );

// floor shader
const char *floorVS = STRINGIFY(
                          varying vec4 vertexPosEye;  // vertex position in eye space  \n
                          varying vec3 normalEye;                                      \n
                          void main()                                                  \n
{
    \n
    gl_Position = gl_ModelViewProjectionMatrix *gl_Vertex;  \n
    gl_TexCoord[0] = gl_MultiTexCoord0;                      \n
    vertexPosEye = gl_ModelViewMatrix *gl_Vertex;           \n
    normalEye = gl_NormalMatrix *gl_Normal;                 \n
    gl_FrontColor = gl_Color;                                \n
}                                                            \n
                      );

const char *floorPS = STRINGIFY(
                          uniform vec3 lightPosEye; // light position in eye space                      \n
                          uniform vec3 lightColor;                                                      \n
                          uniform sampler2D tex;                                                        \n
                          uniform sampler2D shadowTex;                                                  \n
                          varying vec4 vertexPosEye;  // vertex position in eye space                   \n
                          varying vec3 normalEye;                                                       \n
                          void main()                                                                   \n
{
    \n
    vec4 shadowPos = gl_TextureMatrix[0] * vertexPosEye;                      \n
    vec4 colorMap  = texture2D(tex, gl_TexCoord[0].xy);                       \n

    vec3 N = normalize(normalEye);                                            \n
    vec3 L = normalize(lightPosEye - vertexPosEye.xyz);                       \n
    float diffuse = max(0.0, dot(N, L));                                      \n

    vec3 shadow = vec3(1.0) - texture2DProj(shadowTex, shadowPos.xyw).xyz;    \n

    if (shadowPos.w < 0.0) shadow = lightColor;                               \n // avoid back projections
    gl_FragColor = vec4(gl_Color.xyz *colorMap.xyz *diffuse * shadow, 1.0); \n
}                                                                             \n
                      );
