#pragma language glsl3
#ifdef VERTEX

out vec3 EyeDirection_cameraspace;
out vec3 LightDirection_cameraspace;

uniform mat4 projectionMatrix;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;

uniform vec3 lightPosition;
uniform bool debug;



vec4 position(mat4 transform_projection, vec4 vertex_position) {
  vec3 Position_cameraspace = (viewMatrix * modelMatrix * vertex_position).xyz; 
  EyeDirection_cameraspace = vec3(0,0,0) - Position_cameraspace;

  vec3 LightPosition_cameraspace = (viewMatrix * vec4(lightPosition,1)).xyz;
  LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace;

  return projectionMatrix*viewMatrix*modelMatrix*vertex_position;
}

#endif

#ifdef PIXEL

in vec3 EyeDirection_cameraspace;

uniform vec3 volumePosition = vec3(0);
uniform vec3 volumeScale;

uniform sampler2D gbuffer_position;
uniform sampler2D gbuffer_normal;

uniform sampler3D vbuffer_occlusion;
uniform sampler3D vbuffer_normal;

uniform bool debug;

uniform vec3 lightPosition;
uniform vec3 lightColor;
uniform float lightPower;
uniform float lightRange;

uniform int voxelVolumeSize = 128;
uniform float voxelVolumeInverseSize; // Size of a voxel

vec2 tc;

vec4 voxelJitterNoise(vec4 p4) {
  p4 = fract(p4 * vec4(443.897, 441.423, 437.195, 444.129));
  p4 += dot(p4, p4.wzxy + vec4(19.19));
  return fract((p4.xxyz + p4.yzzw) * p4.zywx);
}

float traceShadowCone(vec3 normal, vec3 from, vec3 to) {
  const float aperture = tan(radians(1));
  const float s = 0.33333;
  float doubledAperture = max(voxelVolumeInverseSize, 2.0 * aperture);
  // from += normal * voxelVolumeInverseSize * 2;
  vec3 direction = to - from;
  float maxDistance = length(direction), 
        dist = 8 * voxelVolumeInverseSize,
        accumulator = 0.0;
  direction /= maxDistance;
  maxDistance = min(maxDistance, 1.41421356237);
  vec3 position = from + (direction * dist);
  vec3 noise = normalize(voxelJitterNoise(vec4(position + (vec3(gl_FragCoord.xyz) * 1.0), fract(tc.x * 0.01) * 100.0)).xyz);
  while ((accumulator < 1) && (dist < maxDistance)) {
    float diameter = max(voxelVolumeInverseSize * 0.5, doubledAperture * dist);
    float mipMapLevel = max(0.0, log2((diameter * float(voxelVolumeSize)) + 0.0));
    accumulator += (1.0 - accumulator) * clamp(textureLod(vbuffer_occlusion, position,0).r, 0.0, 1.0);
    dist += max(diameter, voxelVolumeInverseSize) * s;
    position = from + (direction * dist);
  }
  return clamp(1.0 - accumulator, 0.0, 1.0);
}
float att (float dist, float radius) {
  return clamp(1.0 - (dist*dist)/(radius*radius), 0.0, 1.0);
}
vec3 safeNormalize(vec3 n) {
  float l = max(length(n), 1e-6);
  n /= l;
  return n;
}
mat3 vctRotationMatrix(vec3 axis, float angle) {
  axis = normalize(axis);
  float s = sin(angle), c = cos(angle), oc = 1.0 - c;
  vec3 as = axis * s;
  return (mat3(axis.x * axis, axis.y * axis, axis.z * axis) * oc) +
         mat3(c, -as.z, as.y, as.z, c, -as.x, -as.y, as.x, c);
}
vec4 effect(vec4 color, sampler2D texturee, vec2 textureCoords, vec2 screen_coords) {
  if (debug) {
    if (color.a==0) {
      discard;
    }else{
      return color*Texel(texturee,textureCoords);
    }
  }
  vec2 coord    = screen_coords / love_ScreenSize.xy;
  tc = coord;
  vec2 samplePoint = screen_coords/love_ScreenSize.xy;

  vec3 position = Texel(gbuffer_position,  samplePoint).xyz ;
  vec3 normal   = Texel(gbuffer_normal,    samplePoint).xyz*2-vec3(1);
  
  vec3 n = normalize(normal);
  vec3 l = normalize(lightPosition - position);

  float distance = distance( lightPosition , position );

  if (distance>lightRange) {
    discard;
  }

  float cosTheta = clamp(dot(n,  l), 0,1);
  
  // vec3 E = normalize(EyeDirection_cameraspace);
  // vec3 R = reflect(-l,n);
  // float cosAlpha = clamp(dot(E,R), 0,1); 
  vec3 lightPositionVoxel = (lightPosition-volumePosition)/(volumeScale*2)+0.5;
  vec3 fragPositionVoxel = (position-volumePosition)/(volumeScale*2)+0.5;

  vec3  t0 = cross(vec3(0.0, 1.0, 0.0), normal);
   vec3 t1 = cross(vec3(0.0, 0.0, 1.0), normal);
   vec3 tangent = normalize((length(t0) < length(t1)) ? t1 : t0);
   vec3 bitangent = normalize(cross(tangent, normal));

  tangent = safeNormalize(cross(bitangent, normal));
  mat3 tangentSpace = 
  vctRotationMatrix(normal, voxelJitterNoise(vec4(fragPositionVoxel + normal + (vec3(gl_FragCoord.xyz) * 10.0), fract(tc.x * 0.1) *5.0)).x*10) * 
  mat3(tangent, bitangent, normal);

 vec3 shadow = vec3(traceShadowCone(n,fragPositionVoxel,lightPositionVoxel));

  vec4 result = vec4(shadow* lightColor *lightPower * cosTheta*att(distance,lightRange/2) ,1);

  return result;
}

#endif

