##start
##vertex

#version 330
#extension GL_ARB_explicit_attrib_location : enable


layout(location=0) in vec3 in_Position;
layout(location=1) in vec3 in_Normal;
layout(location=2) in vec2 in_Tc;

uniform mat4 modelViewProjMatrix;
uniform mat4 modelViewMatrix;

uniform mat4 modelMatrix; //used for bedrock

uniform mat4 depthBiasMV;

uniform vec3 sunDir;

uniform sampler2D depthTex;


out vec4 n;
out vec4 wpos;
out vec4 wposView;
out vec2 tex;
out vec4 shadowCoord;

void main()
{



   n = normalize(modelViewMatrix*vec4(in_Normal,0));

   wpos =  vec4(in_Position,0);
   wposView = modelViewMatrix*vec4(in_Position,1);
   gl_Position = modelViewProjMatrix*vec4(in_Position,1);
   tex = in_Tc;

   shadowCoord = depthBiasMV * modelMatrix* vec4(in_Position,1);
   //shadowCoord /= shadowCoord.w;

}



##end

##start
##geometry
#version 150
#extension GL_EXT_geometry_shader4 : enable

layout(triangles) in;
in vec4[3] n;
in vec4[3] wpos;
in vec2[3] tex;
in vec4[3] shadowCoord;
in vec4[3] wposView;

layout(triangle_strip, max_vertices = 3) out;

out vec4 normal;
out vec3 tc;
out vec4 shadowCoord2;
out vec4 v;
out float specular;

#define OCTREE_ID_AIR 0
#define OCTREE_ID_DIRT 1
#define OCTREE_ID_GRASS 2
#define OCTREE_ID_BRICK 3
#define OCTREE_ID_STONE 4
#define OCTREE_ID_SAND 5
#define OCTREE_ID_BEDROCK 6
#define OCTREE_ID_BARK 7
#define OCTREE_ID_LEAF 8
#define OCTREE_ID_ROOFTILES 9
#define OCTREE_ID_CACTUS 10
#define OCTREE_ID_PYRAMID 11
#define OCTREE_ID_STUCCO 12
#define OCTREE_ID_MARBLE 13
#define OCTREE_ID_METAL 14
#define OCTREE_ID_METAL_DARKER 15
#define OCTREE_ID_WOOD_PLANKS_DARK 16
#define OCTREE_ID_WOOD_PLANKS 17
#define OCTREE_ID_WHEEL 18
#define OCTREE_ID_DIAMOND_STONE 19
#define OCTREE_ID_TENT 20
#define OCTREE_ID_BALLON 21
#define OCTREE_ID_BASKET 22
#define OCTREE_ID_STONE_CRACKS 23

#define EPSILON 0.00001


bool inEpsilonRange(float a, float b){
    b+=1.0f;
    return (a <= b+EPSILON && a >= b-EPSILON);
}

bool inEpsilonRange(int a, float b){
    return inEpsilonRange(float(a),float(b));
}


float getSpecular(float id){
    float specularFactor = 0.0;
    if (inEpsilonRange(OCTREE_ID_DIRT,id)){
       specularFactor = 0.05;
    }else if (inEpsilonRange(OCTREE_ID_GRASS,id)){
        specularFactor = 0.1;
     }else if (inEpsilonRange(OCTREE_ID_BRICK,id)){
        specularFactor = 0.2;
     }else if (inEpsilonRange(OCTREE_ID_STONE,id)){
        specularFactor = 0.1;
     }else if (inEpsilonRange(OCTREE_ID_SAND,id)){
        specularFactor = 0.05;
     }else if (inEpsilonRange(OCTREE_ID_BEDROCK,id)){
        specularFactor = 0.1;
     }else if (inEpsilonRange(OCTREE_ID_BARK,id)){
        specularFactor = 0.05;
     }else if (inEpsilonRange(OCTREE_ID_LEAF,id)){
        specularFactor = 0.15;
     }else if (inEpsilonRange(OCTREE_ID_ROOFTILES,id)){
        specularFactor = 0.3;
     }else if (inEpsilonRange(OCTREE_ID_CACTUS,id)){
        specularFactor = 0.1;
     }else if (inEpsilonRange(OCTREE_ID_PYRAMID,id)){
        specularFactor = 0.1;
     } else if (inEpsilonRange(OCTREE_ID_STUCCO,id)){
        specularFactor = 0.1;
     }else if (inEpsilonRange(OCTREE_ID_MARBLE,id)){
        specularFactor = 0.2;
     }else if (inEpsilonRange(OCTREE_ID_METAL,id)){
        specularFactor = 0.5;
     }else if (inEpsilonRange(OCTREE_ID_METAL_DARKER,id)){
        specularFactor = 0.8;
     }else if (inEpsilonRange(OCTREE_ID_WOOD_PLANKS_DARK,id)){
        specularFactor = 0.2;
     } else if (inEpsilonRange(OCTREE_ID_WOOD_PLANKS,id)){
        specularFactor = 0.2;
     } else if (inEpsilonRange(OCTREE_ID_WHEEL,id)){
        specularFactor = 0.2;
     } else if (inEpsilonRange(OCTREE_ID_DIAMOND_STONE,id)){
        specularFactor = 0.2;
     }  else if (inEpsilonRange(OCTREE_ID_TENT,id)){
        specularFactor = 0.1;
     }else if (inEpsilonRange(OCTREE_ID_BALLON,id)){
     specularFactor = 0.2;
  }  else if (inEpsilonRange(OCTREE_ID_BASKET,id)){
     specularFactor = 0.2;
  }   else if (inEpsilonRange(OCTREE_ID_STONE_CRACKS,id)){
        specularFactor = 0.1;
     }    else {
        specularFactor = 10.0;
    }
    return specularFactor;
}

void main() {

    vec4 averageNormal = vec4(0,0,0,0);

    averageNormal+=n[0];
    averageNormal+=n[2];
    averageNormal = normalize(averageNormal);



    vec2[3] tc2;
    bool bot = (wpos[0].y==wpos[1].y)? ((wpos[0].z<wpos[1].z)?true:false): ((wpos[0].y<wpos[1].y)?true:false);


    if(bot){
        tc2[0] = vec2(0,0);
        tc2[1] = vec2(0,1);
        tc2[2] = vec2(1,1);
    }else{
        tc2[0] = vec2(1,1);
        tc2[1] = vec2(1,0);
        tc2[2] = vec2(0,0);
    }



    float cubelength = distance(wpos[0], wpos[1]) * 2;

      for(int i = 0; i <  gl_VerticesIn; i++) {
        tc2[i] = cubelength *tc2[i] ;
        gl_Position = gl_PositionIn[i];
        tc = vec3(tc2[i],tex[0].x-1);
        normal = averageNormal;
        shadowCoord2 = shadowCoord[i];
        v =wposView[i];
        specular = getSpecular(tc.z);
        EmitVertex();
      }
      EndPrimitive();
}


##end

##start
##fragment

#version 150 core
#extension GL_EXT_texture_array : enable

in vec4 v;
in vec4 normal;
in vec3 tc;
in vec4 shadowCoord2;
in float specular;

out vec4 out_color;

uniform vec3 flashLightPos;
uniform vec3 flashLightDir;
uniform vec3 sunDir;
uniform vec4 sunColor;

uniform vec3 firePos;
uniform float fireNoise;

uniform float flashLightIntensity;
uniform mat4 modelViewMatrix;
uniform mat4 viewMatrix;


uniform sampler2DShadow depthTex;
uniform sampler2DArray diffuseTexArray;

uniform float outsideShadow;

uniform int night;
uniform int rain;
uniform int lightning;
uniform float fogFactor;
uniform float lightningFactor;
uniform float flashLightFOV;



void main(void)
{
//    out_color = normal;
//    return;
    vec4 shadowCoord = shadowCoord2/shadowCoord2.w;

    float ambientFactor = 0.0;
    if (night == 1){
        ambientFactor =0.05;
    } else {
        ambientFactor = 0.1;
    }

    float specularFactor = 0.0;
    specularFactor = specular;

    if (rain == 1){
        //TODO make it dependent of the normal
//        specularFactor *= 3;
        specularFactor += 0.1;
        clamp(specularFactor,0.0f,1.0f);
    }



    //phong lightning

    vec3 N = vec3(normal);

    vec3 L;
    if(night==1 && lightning == 0)
        L = -normalize( vec3(v)- vec3(viewMatrix*vec4(flashLightPos,1)));
    else
        L = normalize(sunDir);

    vec3 E = normalize( vec3(-v)); // we are in Eye Coordinates, so EyePos is (0,0,0)
    vec3 R = normalize(-reflect(L,N));

    vec4 diffuseColor = texture2DArray(diffuseTexArray, tc);
    vec4 diffuse = diffuseColor*max(0,dot(vec3(normal),L))*sunColor;
    vec4 ambient = diffuseColor*ambientFactor*sunColor;
    vec4 specular = vec4(1,1,1,0)*pow(max(dot(R,E),0.0),30.0f);
    specular = clamp(specular, 0.0, 1.0);
    specular *= specularFactor;
    //vec4 specular = vec4(0,0,0,0);


    vec4 c = diffuse;



    float bias = 0.0005;
    float visibility = 1.0f;
    float borderfactor = 1.0f;

    bool outsideLightsView = false;
    //check if fragment is outside of lights view

    if (shadowCoord.x < 0 || shadowCoord.x > 1 || shadowCoord.y < 0 || shadowCoord.y > 1 || shadowCoord.z < 0 || shadowCoord.z > 1){
//        out_color = outsideShadow*c+ ambient;
        outsideLightsView = true;
    } else {

     if (night == 1 && lightning == 0){
        const float minRange =0.3f;
        const float maxRange = 0.5f;
        float dist = distance(vec2(0.5,0.5),shadowCoord.st);

        borderfactor = dist-minRange;
        borderfactor = 1-(borderfactor/(maxRange-minRange));


        borderfactor= clamp(borderfactor,0.0,1.0) *flashLightIntensity;
    }



//    float sum = 0;
//    float x, y;
//    float offset = 1.0/1024.0/10.0;
//    for (y = -1.5; y <= 1.5; y += 1.0){
//        for (x = -1.5; x <= 1.5; x += 1.0){
//         sum += texture(depthTex, vec3(shadowCoord.x+x*offset, shadowCoord.y+y*offset, shadowCoord.z -bias));
//        }
//    }

//    visibility = sum / 16.0;
    
   visibility = texture(depthTex, vec3(shadowCoord.x, shadowCoord.y, shadowCoord.z ));



    //no hightlights in shadow
    if (visibility == 1){
        c += specular;
    }
	
	
}




//======== Fire
//const float maxDis = 30.0f;
const float minRange2 = 15.0f;
const float maxRange2 = 30.0f;

float dist = distance(vec3(v),firePos);
float falloff = dist-minRange2;
falloff = 1-(falloff/(maxRange2-minRange2));

dist = clamp(falloff,0,1);





vec4 fireColor = vec4(226,88,34,1)/vec4(255,255,255,1);

L = -normalize( vec3(v)- firePos);
vec4 fireDiff = diffuseColor*max(0,dot(vec3(normal),L))*fireColor;
fireDiff = fireDiff*dist*fireNoise*2.0f;

//==============

//======== Fog

const float LOG2 = 1.442695;
float depth = length(vec3(v));
float density = 0.02;
float fog = exp2( -density * density * depth * depth * LOG2 );
fog = clamp(fog, 0, 1.0);
vec4 fogColor = vec4(0.8,0.8,0.8,1);

//==============


//======== lightning
//const float maxDis = 30.0f;

vec4 flashLightDiff = vec4(0,0,0,0);
vec4 flashLightSpec = vec4(0,0,0,0);

float flashLightBorder = 1.0f;
if(lightning == 1){

L = -normalize( vec3(v)- vec3(modelViewMatrix*vec4(flashLightPos,1)));
vec3 E = normalize( vec3(-v)); // we are in Eye Coordinates, so EyePos is (0,0,0)
vec3 R = normalize(-reflect(L,N));
flashLightDiff = diffuseColor*max(0,dot(vec3(normal),L))*sunColor;
float d = dot(L,vec3(modelViewMatrix*vec4(flashLightDir,0)));

flashLightSpec = vec4(1,1,1,0)*pow(max(dot(R,E),0.0),30.0f);
flashLightSpec = clamp(flashLightSpec, 0.0, 1.0);
flashLightSpec *= specularFactor;
flashLightDiff+= flashLightSpec;


const float minFov = 10.0f;
const float maxFov = 70.0f;
float f = 1-(flashLightFOV-minFov)/(maxFov - minFov);

//dirty hack :P
float minRange = mix(0.89,0.996,f);
float maxRange = mix(0.92,0.999,f);


//        float minRange = 0.9;
//        float maxRange = 0.98;

        flashLightBorder = d-minRange;
        flashLightBorder = (flashLightBorder/(maxRange-minRange));
        flashLightBorder = clamp(flashLightBorder,0.0,1.0);

}
//==============


float flashLightFalloff = 1.0f;
    //limit flashlight range
    if (night == 1){
        const float minRange = 10.0f;
        float maxRange = 70.0f*flashLightIntensity;
        //linear fall off between min and max range
        float dist = length(v);

        float falloff = dist-minRange;
        falloff = 1-(falloff/(maxRange-minRange));
        flashLightFalloff*=  clamp(falloff,0.0,1.0);

    }


visibility = clamp(visibility,0.1f /*for contrast*/,1.0f);
if (outsideLightsView){
    if (night==1 && lightning == 0){
        out_color =  /*outsideShadow**//*c+*/ ambient + fireDiff;

    } else {
         if (lightning == 1)
         out_color =  mix(c*lightningFactor+ ambient + fireDiff,fogColor, fogFactor*(1-fog));
         else
        out_color =  mix(c+ ambient + fireDiff,fogColor, fogFactor*(1-fog));

    }
//    out_color = c;

} else {
    if(lightning == 1)
    out_color = borderfactor*mix(visibility*c*lightningFactor+flashLightDiff*flashLightFalloff*flashLightBorder*flashLightIntensity,fogColor, fogFactor*(1-fog)) + ambient + fireDiff;
    else
    out_color = borderfactor*mix(visibility*flashLightFalloff*c+flashLightDiff,fogColor, fogFactor*(1-fog)) + ambient + fireDiff;

}




}




##end
