	
	#version 300 es

	precision highp float;

	#ifndef NORMAL_MAP
		#define NORMAL_MAP 0
	#endif
	
	#ifndef PARALLAX
		#define PARALLAX 0
	#endif
	
	in vec3 position;
	in vec2 textcoord;
	
	in vec3 normal;
	in vec3 tangent;
	in vec3 bitangent;
	
	uniform mat4 world;
	uniform mat4 view;
	uniform mat4 viewProjection;
	uniform mat4 worldInverseTranspose;
	uniform float normals;
	

	
	
	out vec2 v_textcoord;
	
    out vec4 v_normal;
	out vec4 v_tangent;
	out vec4 v_binormal;
	out vec2 v_offset;
	out vec4 v_worldposition;
	out vec4 v_position;
	
	out float v_depth;
	
	out vec3 v_view;
	
	
	
	//mat3 transpose(mat3 matrix) {
//
//		return mat3( matrix[0].x, matrix[1].x, matrix[2].x,
	//				 matrix[0].y, matrix[1].y, matrix[2].y,
	//				 matrix[0].z, matrix[1].z, matrix[2].z );
	//			 
	//}
	
	void main(void) {
	
		v_textcoord = textcoord;
		
		v_normal  =  normalize(worldInverseTranspose * vec4(normal, 0.0));
		
		
		#if NORMAL_MAP == 1
			
			v_tangent =   normalize(worldInverseTranspose * vec4(tangent, 0.0) );
			v_binormal =   normalize(worldInverseTranspose * vec4(bitangent, 0.0) );
			//v_binormal = vec4(cross(v_normal.xyz, v_tangent.xyz), 0.0);
			
		#endif
		
		
		#if PARALLAX == 1

			const float height_map_range = 0.32;

			vec3 vs_normal = ( view * v_normal ).xyz;
			vec3 vs_tangent = ( view * v_tangent ).xyz;
			vec3 vs_bitangent = ( view * vec4(v_binormal, 0.0) ).xyz;
			//vec3 vs_bitangent = bitangent;
			
			
			vec4 a = view * vec4(position, 1.0);
			
			v_view = a.xyz;

			mat3 tbn = mat3( normalize(vs_tangent), normalize(vs_bitangent), normalize(vs_normal) );
			mat3 vs_to_ts = transpose( tbn );

			vec3 ts_view = vs_to_ts * v_view;

			v_offset = (ts_view.xy / ts_view.z) * height_map_range;
		
		

		
		#endif
		
		
		
		v_worldposition = world * vec4(position, 1.0);
		v_position 		= viewProjection * vec4(position, 1.0);
		v_depth  = v_position.z;
		
		gl_Position = v_position;

	}

	
	
	// #keplerEngine - Split
	
	#version 300 es
	
	precision highp float;
	
	#define PI 3.14159265358979323846
	
	


	
	
	layout(location = 0) out vec4 deferred_diffuse_roughness;
	layout(location = 1) out vec4 deferred_normal_depth;
	layout(location = 2) out vec4 deferred_position_material_ID;
	layout(location = 3) out vec4 tangent;
	
	//layout(location = 3) out vec4 deferred_material_1;
	

	in vec2 v_textcoord;
	in vec4 v_normal;
	in vec4 v_tangent;
	in vec4 v_binormal;
	
	in vec4 v_worldposition;
	in vec4 v_position;
	
	in float v_depth;
	
	in vec2 v_offset;
	in vec3 v_view;
	
	
	const float gamma = 2.2;

	//pbr
	uniform samplerCube reflectionSampler;
	
	uniform vec3 cameraPosition;
	uniform vec3 lightGeometry;
	uniform float clearCoat;
	uniform vec3 clearCoatColor;
	uniform float clearCoatThickness;
	uniform float clearCoatRoughness;
	
	uniform float clearCoatIOR;
	uniform float anisotropy;
	
	uniform vec3 lightDirection;	
	uniform vec3 lightColor;	
	uniform float lightIntensity;
	uniform float environmentLuminance;
	uniform float v_metallic;
	uniform float v_reflectance;
	uniform float lightType;
	uniform vec3 lightPosition;
	
	#define ANISOTROPY 0
	#define USE_IES_PROFILE 0
	#define TRANSPARENT_MATERIAL 0
	#define TRANSLUCENT_MATERIAL 0
	#define CUBEMAP_EDGE_FIXUP 0

	#ifndef NORMAL_MAP
	#define NORMAL_MAP 0
	#endif
	
	#ifndef TEXTURE
	#define TEXTURE 0
	#endif	
	
	#ifndef ROUGHNESS_MAP
	#define ROUGHNESS_MAP 0
	#endif	
	
	#ifndef PARALLAX
	#define PARALLAX 0
	#endif
	
	precision highp float;
	
	uniform sampler2D diffuseSampler;
	uniform sampler2D normalSampler;
	uniform sampler2D roughnessSampler;
	uniform sampler2D heightSampler;
	uniform sampler2D shadowNoiseSampler;
	uniform sampler2D shadowDepthSampler;
	
	uniform float normals;
	uniform float far;
	uniform float roughness;
	uniform float metallic;
	uniform float alpha;
	uniform float shadingModelID;
	
	uniform vec3 diffuseColor;

	
	uniform float attenuation; 
	
	uniform float SourceRadius;
	uniform float SourceLength;
	uniform float specular;
	uniform float mode;
	uniform float uvMultiplier;
	uniform float render_type;
	uniform float shadowBias;

	uniform mat4 lightViewProjection;
	
	
	#include "physically_based_shading_full.shader"
	
	vec3 sampleReflection( vec3 r ) {
		return texture(reflectionSampler, r).xyz;
	}
	
	vec3 HDR_ACES(const vec3 x) {
		// Narkowicz 2015, "ACES Filmic Tone Mapping Curve"
		const float a = 2.51;
		const float b = 0.03;
		const float c = 2.43;
		const float d = 0.59;
		const float e = 0.14;
		return (x * (a * x + b)) / (x * (c * x + d) + e);
	}
	vec3 tonemap(const vec3 x) {
		return HDR_ACES(x);
	}

	float linearToSRGB(float c) {
		return (c <= 0.0031308) ? c * 12.92 : (pow(abs(c), 1.0 / 2.4) * 1.055) - 0.055;
	}

	vec3 linearToSRGB(vec3 c) {
		return vec3(linearToSRGB(c.r), linearToSRGB(c.g), linearToSRGB(c.b));
	}

	
	float DecodeFloatRGBA( vec4 rgba ) {
		return (rgba).x;
	
	  //return dot( rgba, vec4(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 160581375.0) );
	}
	
	float shadow_sample(sampler2D depthMap, vec2 coord)
	{
		return  texture(depthMap, coord).x;//DecodeFloatRGBA() ;
	}
	
	vec2 DoubleSampleRotated(sampler2D depthMap, vec4 p, vec4 rotMatr, vec4 kernel) {

		vec4 rotatedOff;

		rotatedOff = rotMatr.xyzw * kernel.xxww +
					 rotMatr.zwxy * kernel.yyzz;

		vec4 fetchPos = p.xyxy + rotatedOff;// + rotatedOff

		vec2 result;
		
		result.x = shadow_sample(depthMap, fetchPos.xy);
		result.y = shadow_sample(depthMap, fetchPos.zw);

		
		return result;
	}
	
	
	
	float PCF(sampler2D depthMap, vec4 p, vec2 randDirTC, float depth)
	{
	
		vec2 kernelRadius = vec2(4.0);
		
		vec4 irreg_kernel_2d[8];
		irreg_kernel_2d[0] = vec4(-0.556641,-0.037109,-0.654297, 0.111328); 
		irreg_kernel_2d[1] = vec4(0.173828,0.111328,0.064453, -0.359375); 
		irreg_kernel_2d[2] = vec4(0.001953,0.082031,-0.060547, 0.078125); 
		irreg_kernel_2d[3] = vec4(0.220703,-0.359375,-0.062500, 0.001953);
		irreg_kernel_2d[4] = vec4(0.242188,0.126953,-0.250000, -0.140625); 
		irreg_kernel_2d[5] = vec4(0.070313,-0.025391,0.148438, 0.082031); 
		irreg_kernel_2d[6] = vec4(-0.078125,0.013672,-0.314453, 0.013672);
		irreg_kernel_2d[7] = vec4(0.117188,-0.140625,-0.199219, 0.117188);

		vec2 vInvShadowMapWH = vec2(1.0 / 2048.0);
		
		const int kernelSize = 8;
		
		mediump float P_Z = depth; // p.z;

		vec4 p0 = vec4(p.xyz, 1.0);

		
		mediump vec2 rotScale = vec2(kernelRadius.y * 2.0);

		float shadowTest = 0.0;

		#define KERNEL_STEP_SIZE 2

			vec2 rotSample = 2.0 * texture(shadowDepthSampler, randDirTC.xy).xy - 1.0;
			rotSample.xy = normalize(rotSample.xy);
			rotSample.xy *= (kernelRadius.xy * vInvShadowMapWH.xy);

			vec4 rot = vec4(rotSample.x, -rotSample.y, rotSample.y, rotSample.x);

			const int kernelOffset = 0;

			for(int i=kernelOffset; i<kernelSize; i+=KERNEL_STEP_SIZE) // Loop over taps
			{

				mediump vec4 sampleDepth = vec4(0.0);
				vec4 irr =  irreg_kernel_2d[i+0];
				sampleDepth.xy = DoubleSampleRotated(depthMap, p0, rot, irr);
				sampleDepth.zw = DoubleSampleRotated(depthMap, p0, rot, irreg_kernel_2d[i+1]);
			
				mediump vec4 InShadow;
				InShadow.x = ( P_Z < sampleDepth.x + shadowBias ) ? 1. : 0.0;
				InShadow.y = ( P_Z < sampleDepth.y + shadowBias ) ? 1. : 0.0;
				InShadow.z = ( P_Z < sampleDepth.z + shadowBias  ) ? 1. : 0.0;
				InShadow.w = ( P_Z < sampleDepth.w + shadowBias  ) ? 1. : 0.0;

		
				const mediump float quality = 8.0; // 8 == high
				const mediump float fInvSamplNum = (1.0 / quality);
				
				shadowTest += dot(InShadow, vec4(fInvSamplNum));
			}

		return shadowTest;
	}
	float poissonPCFmultitap(vec4 projCoords, float shadowDepth, vec2 uv)
	{
		const mediump float step = 1.0 - 1.0 / 8.0;
		const mediump float fScale = 0.025; // 0.025
		
		mediump float n = 0.0;
		
		mediump vec3 directions[8];
		float vSampleScale = 1.0 / 2048.0;
		
		
		directions[0] = normalize(vec3( 1.0, 1.0, 1.0))*fScale*(n+=step);
		directions[1] = normalize(vec3(-1.0,-1.0,-1.0))*fScale*(n+=step);
		directions[2] = normalize(vec3(-1.0,-1.0, 1.0))*fScale*(n+=step);
		directions[3] = normalize(vec3(-1.0, 1.0,-1.0))*fScale*(n+=step);
		directions[4] = normalize(vec3(-1.0, 1.0 ,1.0))*fScale*(n+=step);
		directions[5] = normalize(vec3( 1.0,-1.0,-1.0))*fScale*(n+=step);
		directions[6] = normalize(vec3( 1.0,-1.0, 1.0))*fScale*(n+=step);
		directions[7] = normalize(vec3( 1.0, 1.0,-1.0))*fScale*(n+=step);

		mediump vec3 randomSample = texture(shadowNoiseSampler, vec2(64.0, 64.0) * uv.xy / 4.0).xyz  * 2.0 - 1.0;
		
		

		float sum = 0.0;

		// for( int i = 0; i < 4; i++ ) {
		// 	vec3 sampler = reflect(directions[0], randomSample) * vSampleScale;	
			
			float pixelDepth = DecodeFloatRGBA( texture(shadowDepthSampler, projCoords.xy ) ) ; // + sampler.xy     + sampler.z

			if( pixelDepth + shadowBias > shadowDepth) {
				sum += 1.0;
			} else {
				sum += 0.0;
			}
		// }

		return sum;
	}
	
	
	float linestep(float min, float max, float value) {  
		return clamp((value - min) / (max - min), 0., 1.);
	}

	float reduceBleeding(float p_max, float amount) {
		return linestep(amount, 1.0, p_max);
	}

	float ChebyshevUpperBound(vec2 moments, float distance) {  
		if (distance <= moments.x)
			return 1.0;
			
		float g_minVariance = .00007;

		float variance = moments.y - (moments.x*moments.x);
		variance = max(variance,g_minVariance);

		float d = distance - moments.x;
		float p_max = variance / (variance + d*d);

		return reduceBleeding(p_max, .725);
	}
	
	float calculateShadowOcclusion( vec3 worldPosition, vec2 uv ) {

		vec4 projCoords = lightViewProjection *  vec4(worldPosition, 1.0) ;
		
		float shadowDepth = length( lightPosition - worldPosition );

		projCoords.xy /= projCoords.w;
		projCoords = 0.49 * projCoords + 0.5;

		vec4 moments = texture( shadowDepthSampler, projCoords.xy );
		mediump vec2 randomSample = texture(shadowNoiseSampler, vec2(1024.) * uv / 64.0).xy  * 2.0 - 1.0;
			
		float outFrustum = 0.0;
	
		if(projCoords.x <  0.0 || projCoords.x > 1.0) {
			return 1.0;
		}
		
		if(projCoords.y <  0.0 || projCoords.y > 1.0) {
			return 1.0;
		}
		
		if(projCoords.z <  1.0) {
			return 1.0;
		}	
			
		//return ChebyshevUpperBound(moments.xy, shadowDepth);
		//if(uv.x < .5)
		//return DecodeFloatRGBA( texture(shadowDepthSampler, projCoords.xy ) ) / 10.0;
		//else
		//return shadowDepth;
		
		
		//return moments.x;
		//return projCoords.x;
		//return texture( shadowDepthSampler, uv ).x;//projCoords.xy 
		//return poissonPCFmultitap(projCoords, shadowDepth, uv);
		//return PCF(sampler2D depthMap, vec4 p, vec2 randDirTC);
		return PCF( shadowDepthSampler, projCoords, randomSample, shadowDepth );
		//return 1.0 - ChebyshevUpperBound(moments.xy, shadowDepth);
		// return smoothShadow(projCoords, shadowDepth,uv); 

	}
	


	void main() {
		vec2 textureCoordinate = v_textcoord;
		vec3 normal;
		
			#if NORMAL_MAP == 1
			 
				vec4 normalMap = texture(normalSampler, textureCoordinate * uvMultiplier) * 2.0 - 1.0;
				normal = normalize((v_tangent.xyz * normalMap.x) + (v_binormal.xyz * normalMap.y) + (v_normal.xyz * normalMap.z));
			
				mat3 tangentToWorld = transpose(mat3( v_tangent.xyz  ,
													  v_binormal.xyz  ,
													  v_normal.xyz));
				
				
				normal = normalMap.xyz * tangentToWorld;
			#else

				normal = v_normal.xyz;

			#endif
		
		
		
		#if PARALLAX == 1

			const float max_samples = 30.0;
			const float min_samples = 8.0;

			vec3 view = normalize(v_view);


			float num_steps = mix(max_samples, min_samples, dot(view, normal));

			float current_height = 0.0;
			float step_size = 1.0 / float(num_steps);
			float prev_height = 1.0;

			float step_index = 0.0;

			
			vec2 tex_offset_per_step = step_size * v_offset;
			vec2 tex_current_offset = v_textcoord;
			float current_bound = 1.0;
			float parallax_amount = 0.0;

			vec2 pt1 = vec2(0.0);
			vec2 pt2 = vec2(0.0);

			vec2 tex_offset = vec2(0.0);

			
			for(int x = 0; x < 30; x++) {
				if(step_index < num_steps)
				{
					tex_current_offset -= tex_offset_per_step;

					current_height = texture(heightSampler, tex_current_offset).r; //, dx, dy

					current_bound -= step_size;

					if(current_height > current_bound)
					{
						pt1 = vec2(current_bound, current_height);
						pt2 = vec2(current_bound + step_size, prev_height);

						tex_offset = tex_current_offset - tex_offset_per_step;

						step_index = num_steps + 1.0;
					}
					else
					{
						step_index++;
						prev_height = current_height;
					}
				}
			}

			float delta1 = pt1.x - pt1.y;
			float delta2 = pt2.x - pt2.y;
			float denominator = delta2 - delta1;

			if(denominator == 0.0)
			{
				parallax_amount = 0.0;
			}
			else
			{
				parallax_amount = (pt1.x * delta2 - pt2.x * delta1) / denominator;
			}
			
			vec2 parallax_offset = v_offset * (1.0 - parallax_amount);
			textureCoordinate.xy -= parallax_offset;

		
		#endif
		float gamma = 1.4;
	
		vec3 baseColor;
		vec4 diffuseMap;
		
		#if TEXTURE == 1
			
			diffuseMap = ( texture(diffuseSampler, textureCoordinate * uvMultiplier));//toLinear
			baseColor = diffuseMap.rgb;
		
			
		#else
		
			baseColor = diffuseColor;
			
		#endif	
		
		
		float f_roughness = roughness;
	
		
		#if ROUGHNESS_MAP == 1
		 
			f_roughness = texture(roughnessSampler, textureCoordinate).x;
			
		#endif
		
		vec3 diffuse = baseColor ;
		//
		
		
		float opacity = 1.0;
		float metallic = v_metallic / 2.0;
		float reflectance = v_reflectance / 2.0;

		
		vec3 viewDir = (cameraPosition - v_worldposition.xyz);
		vec3 reflectionVector = reflect(-viewDir.xyz, normalize(normal.xyz));
		vec3 reflectionSample = sampleReflection( reflectionVector );

		if(render_type == 0.0) {
		
			// Material properties
			deferredMaterialData materialData;
			
			materialData.ambientOcclusion 	= 1.0;
			materialData.normal 			= normalize( normal.xyz );
			materialData.baseColor 			= sRGBtoLinear(diffuse.rgb);
			materialData.position 			= v_worldposition.xyz;
			materialData.index 				= shadingModelID;
			materialData.roughness 			= f_roughness;

			materialData.clearCoat 			= clearCoat;
			materialData.clearCoatRoughness = clearCoatRoughness;
			materialData.clearCoatThickness = clearCoatThickness;
			materialData.clearCoatColor 	= clearCoatColor;
			
			materialData.alpha 				= 1.0;
			materialData.shadowOcclusion 	= calculateShadowOcclusion( v_worldposition.xyz, textureCoordinate );
			materialData.metallic			= metallic;
			materialData.reflectance		= reflectance;
			materialData.tangent			= v_tangent.xyz;
			
			
			// Light properties
			deferredLightData lightData;
			
			lightData.position 			= lightPosition;
			lightData.direction 		= lightDirection;
			lightData.color				= lightColor;
			lightData.intensity			= lightIntensity;
			lightData.inverseFalloff 	= lightGeometry.x;
			lightData.length 			= lightGeometry.y;
			lightData.radius 			= lightGeometry.z;
	
			vec4 pbr  = physically_based_shading( materialData, lightData );
			
			float exposure = 1.7;

			pbr.rgb *= exposure;
			
			
			deferred_diffuse_roughness = vec4(linearToSRGB(tonemap(pbr.rgb)), 1.0);
			
			
			if(shadingModelID == 100.0) {
				deferred_diffuse_roughness = vec4(reflectionSample, 1.0);
			}
			
			//if(textureCoordinate.x > .5)
			//deferred_diffuse_roughness = vec4(vec3(shadow), 1.0);

		} else {
			deferred_diffuse_roughness = vec4( (diffuse), f_roughness); //  //f_roughness kaj niet vergeten!!!
			deferred_normal_depth = vec4(normalize(normal), v_depth);
			deferred_position_material_ID = vec4(v_worldposition.xyz, float( shadingModelID ) );
		}


	
	}

