///////////////////////////////////////////////////
//
// material shaders
//
///////////////////////////////////////////////////

shader 0 "refractmask" [
    in vec4 vvertex;
    uniform mat4 camprojmatrix;
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
    }
] [
    @(gfetchdefs tex0)
    uniform vec3 gdepthpackparams;
    layout(location = 0) out vec4 fragcolor;
    void main(void)
    {
        @(if (= $gdepthformat 1) [result [
            vec3 packdepth = gfetch(tex0, gl_FragCoord.xy).rgb;
        ]] [result [
            @(gdepthunpack depth [gfetch(tex0, gl_FragCoord.xy)])
            @(gpackdepth packdepth depth)
        ]])
        fragcolor = vec4(packdepth, 0.0);
    }
]

lazyshader 0 "waterminimap" [
    in vec4 vvertex;
    uniform mat4 camprojmatrix;
    @(ginterpvert)
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
        @(gdepthpackvert)
    }
] [
    uniform vec3 watercolor;
    @(ginterpfrag)

    void main(void)
    {
        gcolor = vec4(watercolor, 0.0);
        @(gnormpack [vec3(0.5, 0.5, 1.0)])
        @(gdepthpackfrag)
    }
]

watershader = [
  lazyshader 0 $arg1 [
    in vec4 vvertex;
    in vec2 vtexcoord0;
    uniform mat4 camprojmatrix;
    uniform vec3 camera;
    out vec2 texcoord0, texcoord1;
    out vec3 surface;
    @(? (>= (strstr $arg1 "reflect") 0) [
        uniform mat4 raymatrix;
        out vec3 esurface;
    ])
    @(ginterpvert 1)
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
        surface = vvertex.xyz;
        @(? (>= (strstr $arg1 "reflect") 0) [
            esurface = (raymatrix * vvertex).xyz;
        ])
        texcoord0 = vtexcoord0 * 0.18;
        texcoord1 = vtexcoord0 * 0.08;
        @(gdepthpackvert 1)
    }
  ] [
    @(gfetchdefs [tex7 tex8 tex9])
    uniform float millis;
    uniform vec3 camera;
    uniform mat4 linearworldmatrix;
    in vec2 texcoord0, texcoord1;
    in vec3 surface;
    uniform sampler2D tex0, tex1;
    uniform vec4 viewsize;
    uniform vec3 watercolor, waterdeepcolor, waterdeepfade;
    uniform float waterfog, waterspec;
    uniform vec4 waterreflect, waterrefract;
    uniform float refractdepth;
    @(? (>= (strstr $arg1 "caustics") 0) [
        uniform vec3 causticsS, causticsT;
        uniform vec3 causticsblend;
        uniform sampler2D tex2, tex3;
    ])
    @(? (>= (strstr $arg1 "env") 0) [
        uniform samplerCube tex4;
    ])
    @(? (>= (strstr $arg1 "reflect") 0) [
        uniform mat4 raymatrix;
        in vec3 esurface;
    ])
    @(ginterpfrag 1)

    void main(void)
    {
        vec3 camdir = camera - surface, camvec = normalize(camdir);
        vec3 bump  = texture2D(tex1, texcoord0 + millis*vec2( 0.25,  0.75)*0.1250).rgb;
        vec3 bump2 = texture2D(tex1, texcoord0 + millis*vec2(-0.75, -0.25)*0.1450).rgb;
        vec3 bump3 = texture2D(tex1, texcoord1 + millis*vec2(-0.50,  0.50)*0.0805).rgb;
        vec3 bump4 = texture2D(tex1, texcoord1 + millis*vec2( 0.25, -0.75)*0.0825).rgb;
        bump = normalize(bump + bump2 + bump3 + bump4 - 2.0);
        vec2 rtc = bump.xy * waterrefract.w;

        float rmask = clamp(refractdepth*(lineardepth - dot(gfetch(tex7, gl_FragCoord.xy + rtc).rgb, gdepthunpackparams)), 0.0, 1.0);
        rtc = gl_FragCoord.xy + rtc*rmask;
        vec3 rcolor = gfetch(tex8, rtc).rgb * waterrefract.xyz;
        float rdepth = dot(gfetch(tex7, rtc).rgb, gdepthunpackparams);
        vec3 rpos = (linearworldmatrix * vec4(rdepth*rtc, rdepth, 1.0)).xyz;

        @(if (>= (strstr $arg1 "under") 0) [result [
            float above = rpos.z - surface.z;
            float alpha = clamp(above, 0.0, 1.0);
        ]] [result [
            vec3 rdir = rpos.xyz - camera;
            float raydepth = length(rdir)*(1.0 + camdir.z/rdir.z);
            float deep = surface.z - rpos.z;
            float alpha = clamp(deep*0.5, 0.0, 1.0);

            @(? (>= (strstr $arg1 "caustics") 0) [
                vec2 ctc = vec2(dot(causticsS, rpos.xyz), dot(causticsT, rpos.xyz));
                float caustics = causticsblend.x*texture2D(tex2, ctc).r + causticsblend.y*texture2D(tex3, ctc).r + causticsblend.z;
                rcolor *= caustics;
            ])

            rcolor = mix(watercolor, rcolor, clamp(exp2(waterfog * raydepth), 0.0, 1.0));
            rcolor = mix(waterdeepcolor, rcolor, clamp(exp2(deep * waterdeepfade), 0.0, 1.0));
        ]])

        vec3 reflectdir = -reflect(camvec, bump);
        reflectdir.z = abs(reflectdir.z);
        @(if (>= (strstr $arg1 "reflect") 0) [result [
            reflectdir.z += 0.125;
            vec3 edir = (raymatrix * vec4(waterreflect.w*reflectdir, 0.0)).xyz;
            vec3 epos = esurface + edir;
            @(loopconcat i 4 [result [
                @(gdepthunpack [edepth@[i]] [gfetchproj(tex9, epos)] [
                    if(edepth@[i] < epos.z || edepth@[i] > esurface.z) epos += edir;
                ] [
                    edepth@[i] = edepth@[i]*gdepthscale.y + gdepthscale.z;
                    if(gdepthscale.x < epos.z*edepth@[i] || gdepthscale.x > esurface.z*edepth@[i]) epos += edir;
                ])
            ]])
            vec2 etc = epos.xy/epos.z;
            @(? $msaalight [
                etc = clamp(etc, vec2(0.0), viewsize.xy);
            ])
            vec3 reflect = gfetch(tex8, etc).rgb * waterreflect.xyz;
            float edgefade = clamp(4.0*(0.5 - max(abs(etc.x*viewsize.z - 0.5)*0.75 + 0.25/4.0, abs(etc.y*viewsize.w - 0.5))), 0.0, 1.0);
            float fresnel = 0.25 + 0.75*pow(clamp(1.0 - dot(camvec, bump), 0.0, 1.0), 4.0);
            rcolor = mix(rcolor, reflect, fresnel*edgefade);
        ]] [if (>= (strstr $arg1 "env") 0) [result [
            vec3 reflect = textureCube(tex4, reflectdir).rgb*0.5;
            float fresnel = 0.5*pow(clamp(1.0 - dot(camvec, bump), 0.0, 1.0), 4.0);
            rcolor = mix(rcolor, reflect, fresnel);
        ]]])

        gcolor.rgb = vec3(0.0);
        @(gspecpack 2.0 [waterspec*alpha])
        @(gnormpack bump alpha)
        gglow.rgb = rcolor*alpha;
        @(gdepthpackfrag)
    }
  ]
]

watershader "water"
watershader "watercaustics"
watershader "waterenv"
watershader "waterenvcaustics"
watershader "waterreflect"
watershader "waterreflectcaustics"
watershader "underwater"

causticshader = [
    lazyshader 0 $arg1 [
        in vec4 vvertex;
        void main(void)
        {
            gl_Position = vvertex;
        }
    ] [
        @(gfetchdefs tex9)
        uniform mat4 causticsmatrix;
        uniform vec3 causticsblend;
        uniform sampler2D tex0, tex1;
        uniform vec4 waterdeepfade;
        layout(location = 0) out vec4 fragcolor;
        void main(void)
        {
            @(gdepthunpack depth [gfetch(tex9, gl_FragCoord.xy)] [
                vec3 ctc = (causticsmatrix * vec4(depth*gl_FragCoord.xy, depth, 1.0)).xyz;
            ] [
                vec4 ctc = causticsmatrix * vec4(gl_FragCoord.xy, depth, 1.0);
                ctc.xyz /= ctc.w;
            ])
            float caustics = causticsblend.x*texture2D(tex0, ctc.xy).r + causticsblend.y*texture2D(tex1, ctc.xy).r + causticsblend.z;
            caustics *= clamp(ctc.z, 0.0, 1.0) * clamp(exp2(ctc.z*waterdeepfade.w), 0.0, 1.0);
            fragcolor.rgb = vec3(0.5 + caustics);
        }
    ]
]
causticshader caustics

waterfogshader = [
    lazyshader 0 $arg1 [
        in vec4 vvertex;
        void main(void)
        {
            gl_Position = vvertex;
        }
    ] [
        @(gfetchdefs tex9)
        uniform vec3 camera;
        uniform mat4 worldmatrix;
        uniform vec3 fogcolor;
        uniform vec2 fogdensity;
        uniform vec3 waterdeepcolor, waterdeepfade;
        uniform float waterheight;
        layout(location = 0) out vec4 fragcolor;
        void main(void)
        {
            @(gdepthunpack depth [gfetch(tex9, gl_FragCoord.xy)] [
                vec3 pos = (worldmatrix * vec4(depth*gl_FragCoord.xy, depth, 1.0)).xyz;
            ] [
                vec4 pos = worldmatrix * vec4(gl_FragCoord.xy, depth, 1.0);
                pos.xyz /= pos.w;
            ])
            float fogbelow = waterheight - pos.z;
            float fogcoord = distance(camera, pos.xyz);
            float foglerp = clamp(1.0 - exp2(fogcoord*fogdensity.x)*fogdensity.y, 0.0, 1.0);
            foglerp *= clamp(2.0*fogbelow + 0.5, 0.0, 1.0);
            vec3 color = mix(waterdeepcolor, fogcolor, clamp(exp2(fogbelow*waterdeepfade), 0.0, 1.0));
            fragcolor.rgb = color;
            fragcolor.a = foglerp;
        }
    ]
]
waterfogshader waterfog

lazyshader 0 "lava" [
    in vec4 vvertex;
    in vec3 vnormal;
    in vec2 vtexcoord0;
    uniform mat4 camprojmatrix;
    out mat3 world;
    out vec2 texcoord0;
    @(ginterpvert)
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
        texcoord0 = vtexcoord0;
        vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x));
        vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z));
        world = mat3(tangent, bitangent, vnormal);
        @(gdepthpackvert)
    }
] [
    uniform sampler2D tex0, tex1;
    uniform float lavaglow, lavaspec;
    in mat3 world;
    in vec2 texcoord0;
    @(ginterpfrag)

    void main(void)
    {
        vec3 diffuse = texture2D(tex0, texcoord0).rgb;
        vec3 bump = texture2D(tex1, texcoord0).rgb*2.0-1.0;
        vec3 bumpw = normalize(world * bump);
        gcolor.rgb = diffuse;
        @(gspecpack 0.0 lavaspec)
        @(gnormpack bumpw)
        gglow.rgb = diffuse*lavaglow;
        @(gdepthpackfrag)
    }
]

lazyshader 0 "waterfallenv" [
    in vec4 vvertex;
    in vec3 vnormal;
    in vec2 vtexcoord0;
    uniform mat4 camprojmatrix;
    uniform vec4 camera;
    out vec3 camdir;
    out mat3 world;
    out vec2 texcoord0;
    @(ginterpvert 1)
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
        texcoord0 = vtexcoord0;
        camdir = camera.xyz - vvertex.xyz;
        vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x));
        vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z));
        world = mat3(tangent, bitangent, vnormal);
        @(gdepthpackvert 1)
    }
] [
    @(gfetchdefs [tex7 tex8])
    uniform samplerCube tex3;
    uniform sampler2D tex0, tex1;
    uniform vec3 waterfallcolor;
    uniform float waterfallspec;
    uniform vec4 waterfallrefract;
    uniform float refractdepth;
    in vec3 camdir;
    in mat3 world;
    in vec2 texcoord0;
    @(ginterpfrag 1)

    void main(void)
    {
        vec3 camvec = normalize(camdir);
        vec3 diffuse = texture2D(tex0, texcoord0).rgb;
        vec3 bump = texture2D(tex1, texcoord0).rgb*2.0 - 1.0;
        vec3 bumpw = normalize(world * bump);

        vec2 rtc = bump.xy * waterfallrefract.w;
        float rmask = clamp(refractdepth*(lineardepth - dot(gfetch(tex7, gl_FragCoord.xy + rtc).rgb, gdepthunpackparams)), 0.0, 1.0);
        rtc = gl_FragCoord.xy + rtc*rmask;
        vec3 rcolor = gfetch(tex8, rtc).rgb * waterfallrefract.xyz;

        float invfresnel = dot(camvec, bumpw);
        vec3 env = textureCube(tex3, 2.0*bumpw*invfresnel - camvec).rgb;
        env *= 0.1 + 0.4*pow(clamp(1.0 - invfresnel, 0.0, 1.0), 2.0);

        gcolor.rgb = vec3(0.0);
        @(gspecpack 2.0 [waterfallspec*(1.0 - dot(diffuse, vec3(0.33)))]);
        @(gnormpack bumpw)
        gglow.rgb = mix(rcolor, waterfallcolor, diffuse) + env;
        @(gdepthpackfrag)
    }
]

lazyshader 0 "waterfall" [
    in vec4 vvertex;
    in vec3 vnormal;
    in vec2 vtexcoord0;
    uniform mat4 camprojmatrix;
    out mat3 world;
    out vec2 texcoord0;
    @(ginterpvert 1)
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
        texcoord0 = vtexcoord0;
        vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x));
        vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z));
        world = mat3(tangent, bitangent, vnormal);
        @(gdepthpackvert 1)
    }
] [
    @(gfetchdefs [tex7 tex8])
    uniform sampler2D tex0, tex1;
    uniform vec3 waterfallcolor;
    uniform float waterfallspec;
    uniform vec4 waterfallrefract;
    uniform float refractdepth;
    in mat3 world;
    in vec2 texcoord0;
    @(ginterpfrag 1)

    void main(void)
    {
        vec3 diffuse = texture2D(tex0, texcoord0).rgb;
        vec3 bump = texture2D(tex1, texcoord0).rgb*2.0 - 1.0;
        vec3 bumpw = normalize(world * bump);

        vec2 rtc = bump.xy * waterfallrefract.w;
        float rmask = clamp(refractdepth*(lineardepth - dot(gfetch(tex7, gl_FragCoord.xy + rtc).rgb, gdepthunpackparams)), 0.0, 1.0);
        rtc = gl_FragCoord.xy + rtc*rmask;
        vec3 rcolor = gfetch(tex8, rtc).rgb * waterfallrefract.xyz;

        gcolor.rgb = vec3(0.0);
        @(gspecpack 2.0 [waterfallspec*(1.0 - dot(diffuse, vec3(0.33)))])
        @(gnormpack bumpw)
        gglow.rgb = mix(rcolor, waterfallcolor, diffuse);
        @(gdepthpackfrag)
    }
]

lazyshader 0 "glassenv" [
    in vec4 vvertex;
    in vec3 vnormal;
    in vec2 vtexcoord0;
    uniform mat4 camprojmatrix;
    uniform vec4 camera;
    out vec3 camdir;
    out mat3 world;
    out vec2 texcoord0;
    @(ginterpvert 1)
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
        texcoord0 = vtexcoord0;
        camdir = camera.xyz - vvertex.xyz;
        vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x));
        vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z));
        world = mat3(tangent, bitangent, vnormal);
        @(gdepthpackvert 1)
    }
] [
    @(gfetchdefs [tex7 tex8])
    uniform samplerCube tex0;
    uniform sampler2D tex1;
    uniform float glassspec;
    uniform vec4 glassrefract;
    uniform float refractdepth;
    in vec3 camdir;
    in mat3 world;
    in vec2 texcoord0;
    @(ginterpfrag 1)

    void main(void)
    {
        vec3 camvec = normalize(camdir);
        vec3 bump = texture2D(tex1, texcoord0).rgb*2.0 - 1.0;
        vec3 bumpw = normalize(world * bump);

        vec2 rtc = bump.xy * glassrefract.w;
        float rmask = clamp(refractdepth*(lineardepth - dot(gfetch(tex7, gl_FragCoord.xy + rtc).rgb, gdepthunpackparams)), 0.0, 1.0);
        rtc = gl_FragCoord.xy + rtc*rmask;
        vec3 rcolor = gfetch(tex8, rtc).rgb;
        rcolor *= glassrefract.xyz;

        float invfresnel = dot(camvec, bumpw);
        vec3 env = textureCube(tex0, 2.0*bumpw*invfresnel - camvec).rgb;
        env *= 0.1 + 0.4*pow(clamp(1.0 - invfresnel, 0.0, 1.0), 2.0);

        gcolor.rgb = vec3(0.0);
        @(gspecpack 2.0 glassspec)
        @(gnormpack bumpw)
        gglow.rgb = rcolor + env;
        @(gdepthpackfrag)
    }
]

lazyshader 0 "glass" [
    in vec4 vvertex;
    in vec3 vnormal;
    in vec2 vtexcoord0;
    uniform mat4 camprojmatrix;
    out mat3 world;
    out vec2 texcoord0;
    @(ginterpvert 1)
    void main(void)
    {
        gl_Position = camprojmatrix * vvertex;
        texcoord0 = vtexcoord0;
        vec3 tangent = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), abs(vnormal.x));
        vec3 bitangent = mix(vec3(0.0, 0.0, -1.0), vec3(0.0, 1.0, 0.0), abs(vnormal.z));
        world = mat3(tangent, bitangent, vnormal);
        @(gdepthpackvert 1)
    }
] [
    @(gfetchdefs [tex7 tex8])
    uniform sampler2D tex1;
    uniform float glassspec;
    uniform vec4 glassrefract;
    uniform float refractdepth;
    in mat3 world;
    in vec2 texcoord0;
    @(ginterpfrag 1)

    void main(void)
    {
        vec3 bump = texture2D(tex1, texcoord0).rgb*2.0 - 1.0;
        vec3 bumpw = normalize(world * bump);

        vec2 rtc = bump.xy * glassrefract.w;
        float rmask = clamp(refractdepth*(lineardepth - dot(gfetch(tex7, gl_FragCoord.xy + rtc).rgb, gdepthunpackparams)), 0.0, 1.0);
        rtc = gl_FragCoord.xy + rtc*rmask;
        vec3 rcolor = gfetch(tex8, rtc).rgb;
        rcolor *= glassrefract.xyz;

        gcolor.rgb = vec3(0.0);
        @(gspecpack 2.0 glassspec)
        @(gnormpack bumpw)
        gglow.rgb = rcolor;
        @(gdepthpackfrag)
    }
]

