// ColoredAmbientOcclusion.glsl
// Цветная амбиент окклюзия на основе ванильных блоков
// Проецирует цвет блоков снизу на 2 блока вверх

//============================================================================//
// CONFIGURATION
//============================================================================//

#ifndef CAO_SAMPLES
#define CAO_SAMPLES 8 // [4 6 8 12]
#endif

#ifndef CAO_HEIGHT
#define CAO_HEIGHT 2.0 // Высота проекции в блоках [1.0 2.0 3.0]
#endif

#ifndef CAO_INTENSITY
#define CAO_INTENSITY 0.6 // [0.3 0.5 0.6 0.8 1.0]
#endif

//============================================================================//
// SHADOW SAMPLING
//============================================================================//

// Получить мировую позицию из shadow map
vec3 GetShadowWorldPos(vec2 shadowCoord, float shadowDepth) {
    vec3 shadowClipPos = vec3(shadowCoord, shadowDepth) * 2.0 - 1.0;
    
    // Обратная дисторсия
    float distFactor = DistortionFactor(shadowClipPos.xy);
    shadowClipPos.xy *= distFactor;
    shadowClipPos.z /= 0.2;
    
    // Shadow clip → Shadow view → World
    vec4 shadowViewPos = shadowProjectionInverse * vec4(shadowClipPos, 1.0);
    shadowViewPos.xyz /= shadowViewPos.w;
    
    vec4 worldPos = shadowModelViewInverse * vec4(shadowViewPos.xyz, 1.0);
    
    return worldPos.xyz;
}

// Сэмплировать цвет блока из shadow map
vec3 SampleBlockColor(vec2 shadowCoord) {
    if (any(lessThan(shadowCoord, vec2(0))) || any(greaterThan(shadowCoord, vec2(1)))) {
        return vec3(0.0);
    }
    
    return texture(shadowcolor0, shadowCoord).rgb;
}

//============================================================================//
// COLORED AMBIENT OCCLUSION
//============================================================================//

vec3 CalculateColoredAO(vec3 worldPos, vec3 worldNormal, float dither) {
    // Работаем только с вертикальными или почти вертикальными поверхностями
    if (abs(worldNormal.y) > 0.5) {
        return vec3(1.0); // Не применяем к полу/потолку
    }
    
    vec3 totalColor = vec3(0.0);
    float totalWeight = 0.0;
    
    // Определяем направление поиска (сторона блока)
    vec3 sideDir = normalize(vec3(worldNormal.x, 0.0, worldNormal.z));
    
    // Сэмплируем блоки снизу в радиусе CAO_HEIGHT
    for (int i = 0; i < CAO_SAMPLES; i++) {
        float fi = float(i) + dither;
        float t = fi / float(CAO_SAMPLES);
        
        // Позиция для проверки (ниже текущей точки)
        vec3 sampleWorldPos = worldPos;
        sampleWorldPos.y -= t * CAO_HEIGHT;
        
        // Небольшое смещение в сторону нормали
        sampleWorldPos += sideDir * 0.1;
        
        // Преобразуем в shadow space
        vec3 shadowViewPos = mat3(shadowModelView) * sampleWorldPos;
        vec3 shadowClipPos = projMAD(shadowProjection, shadowViewPos);
        
        // Применяем дисторсию
        float distFactor = DistortionFactor(shadowClipPos.xy);
        shadowClipPos = DistortShadowSpace(shadowClipPos, distFactor);
        
        vec2 shadowCoord = shadowClipPos.xy * 0.5 + 0.5;
        
        // Проверяем глубину
        float shadowDepth = texture(shadowtex0, shadowCoord).x;
        float sampleDepth = shadowClipPos.z * 0.5 + 0.5;
        
        // Если блок есть
        if (abs(shadowDepth - sampleDepth) < 0.01) {
            // Получаем цвет блока
            vec3 blockColor = SampleBlockColor(shadowCoord);
            
            // Вес основан на расстоянии по Y
            float weight = 1.0 - t;
            weight = weight * weight; // квадратичное затухание
            
            totalColor += blockColor * weight;
            totalWeight += weight;
        }
    }
    
    if (totalWeight > 0.001) {
        vec3 aoColor = totalColor / totalWeight;
        
        // Увеличиваем насыщенность
        float luma = dot(aoColor, vec3(0.299, 0.587, 0.114));
        aoColor = mix(vec3(luma), aoColor, 1.3);
        
        // Применяем интенсивность
        return mix(vec3(1.0), aoColor, CAO_INTENSITY);
    }
    
    return vec3(1.0);
}

//============================================================================//
// INTEGRATION
//============================================================================//

vec3 ApplyColoredAO(vec3 color, vec3 worldPos, vec3 worldNormal, float dither) {
    vec3 aoColor = CalculateColoredAO(worldPos, worldNormal, dither);
    return color * aoColor;
}