uniform sampler2DShadow shadowtex0;

#ifdef SHADOW_COLOR
uniform sampler2DShadow shadowtex1;
uniform sampler2D shadowcolor0;
#endif

/*
uniform sampler2D shadowtex0;

#ifdef SHADOW_COLOR
uniform sampler2D shadowtex1;
uniform sampler2D shadowcolor0;
#endif
*/

vec2 shadowOffsets[9] = vec2[9](
    vec2( 0.0, 0.0),
    vec2( 0.0, 1.0),
    vec2( 0.7, 0.7),
    vec2( 1.0, 0.0),
    vec2( 0.7,-0.7),
    vec2( 0.0,-1.0),
    vec2(-0.7,-0.7),
    vec2(-1.0, 0.0),
    vec2(-0.7, 0.7)
);

/*
float texture2DShadow(sampler2D shadowtex, vec3 shadowPos) {
    float shadow = texture2D(shadowtex,shadowPos.st).x;
    shadow = clamp((shadow-shadowPos.z)*65536.0,0.0,1.0);
    return shadow;
}
*/

vec2 offsetDist(float x, int s) {
	float n = fract(x * 1.414) * 6.28;
    return vec2(cos(n), sin(n)) * 2.0 * x / s;
}

vec3 SampleBasicShadow(vec3 shadowPos) {
    float shadow0 = shadow2D(shadowtex0, vec3(shadowPos.st, shadowPos.z)).x;

    vec3 shadowCol = vec3(0.0);
    #ifdef SHADOW_COLOR
    if(shadow0 < 1.0){
        shadowCol = texture2D(shadowcolor0, shadowPos.st).rgb *
                    shadow2D(shadowtex1, vec3(shadowPos.st, shadowPos.z)).x;
    }
    #endif

    return clamp(shadowCol * (1.0 - shadow0) + shadow0, vec3(0.0), vec3(1.0));
}

vec3 SampleFilteredShadow(vec3 shadowPos, float offset) {
    float shadow0 = 0.0;
    
    for(int i = 0; i < 9; i++){
        vec2 shadowOffset = shadowOffsets[i] * offset;
        shadow0 += shadow2D(shadowtex0, vec3(shadowPos.st + shadowOffset, shadowPos.z)).x;
    }
    shadow0 /= 9.0;

    vec3 shadowCol = vec3(0.0);
    #ifdef SHADOW_COLOR
    if(shadow0 < 1.0){
        for(int i = 0; i < 9; i++){
            vec2 shadowOffset = shadowOffsets[i] * offset;
            shadowCol += texture2D(shadowcolor0, shadowPos.st + shadowOffset).rgb *
                         shadow2D(shadowtex1, vec3(shadowPos.st + shadowOffset, shadowPos.z)).x;
        }
        shadowCol /= 9.0;
    }
    #endif

    return clamp(shadowCol * (1.0 - shadow0) + shadow0, vec3(0.0), vec3(1.0));
}

vec3 SampleTAAFilteredShadow(vec3 shadowPos, float offset) {
    float noise = InterleavedGradientNoise();

    float shadow0 = 0.0;
    
    for(int i = 0; i < 3; i++){
        vec2 shadowOffset = offsetDist(noise + i, 3) * offset;
        shadow0 += shadow2D(shadowtex0, vec3(shadowPos.st + shadowOffset, shadowPos.z)).x;
    }
    shadow0 /= 3.0;

    vec3 shadowCol = vec3(0.0);
    #ifdef SHADOW_COLOR
    if(shadow0 < 1.0){
        for(int i = 0; i < 3; i++){
            vec2 shadowOffset = offsetDist(noise + i, 3) * offset;
            shadowCol += texture2D(shadowcolor0, shadowPos.st + shadowOffset).rgb *
                         shadow2D(shadowtex1, vec3(shadowPos.st + shadowOffset, shadowPos.z)).x;
        }
        shadowCol /= 3.0;
    }
    #endif

    return clamp(shadowCol * (1.0 - shadow0) + shadow0, vec3(0.0), vec3(1.0));
}

vec3 GetShadow(vec3 shadowPos, float bias, float offset) {
    shadowPos.z -= bias;

    #ifdef SHADOW_FILTER
    #ifdef TAA
    vec3 shadow = SampleTAAFilteredShadow(shadowPos, offset);
    #else
    vec3 shadow = SampleFilteredShadow(shadowPos, offset);
    #endif
    #else
    vec3 shadow = SampleBasicShadow(shadowPos);
    #endif

    return shadow;
}


float GetCloudShadow(vec3 worldPos) {
	vec2 wind = vec2(
		frametime * CLOUD_SPEED * 0.0005,
		sin(frametime * CLOUD_SPEED * 0.05) * 0.001
	) * CLOUD_HEIGHT / 15.0;

    worldPos += cameraPosition;

    vec3 worldLightVec = (gbufferModelViewInverse * vec4(lightVec, 0.0)).xyz;

//	float cloudHeight = CLOUD_HEIGHT * CLOUD_VOLUMETRIC_SCALE + 70;
	float cloudHeight = CLOUD_HEIGHT * 16.0 + 70;
    worldPos.xz += worldLightVec.xz / worldLightVec.y * max(cloudHeight - worldPos.y, 0.0);


//    float scaledThickness = CLOUD_THICKNESS * CLOUD_VOLUMETRIC_SCALE;
    float scaledThickness = CLOUD_THICKNESS * 16.0;
    float cloudFadeOut = 1.0 - clamp((worldPos.y - cloudHeight) / scaledThickness, 0.0, 1.0);


//    vec2 coord = worldPos.xz / CLOUD_VOLUMETRIC_SCALE;
    vec2 coord = worldPos.xz / 16.0;

	coord.xy *= 0.004;

	float noiseBase = texture2D(noisetex, coord * 0.25 + wind).r;
	#if CLOUDS == 2
	noiseBase = texture2D(colortex8, coord * 0.25 + wind).r;
	#endif	


	float noise = mix(noiseBase, 1.0, 0.33 * rainStrength) * 21.0;
		
	noise = max(noise - CLOUD_AMOUNT, 0.0);
	noise *= CLOUD_DENSITY * 0.125;
	noise *= (1.0 - 0.75 * rainStrength);
	noise = noise / sqrt(noise * noise + 0.5);
    noise *= cloudFadeOut;

	return 1.0 - noise * CLOUD_OPACITY * 0.85;
}