// composite5.csh
// Розмісти: shaders/composite/composite5.csh
// Побудова воксельної сітки освітлення

#version 430 compatibility

/* RENDERTARGETS: 6,7 */
layout(location = 0) out vec4 voxelEmission;
layout(location = 1) out vec4 voxelAlbedoNormal;

// ========================
// UNIFORM-И
// ========================
uniform sampler2D colortex0; // Альбедо
uniform sampler2D colortex1; // Матеріали  
uniform sampler2D colortex2; // Лайтмапи
uniform sampler2D colortex3; // Нормалі
uniform sampler2D depthtex0;
uniform sampler2D depthtex1;

uniform sampler3D colortex6; // Попередня емісія
uniform sampler3D colortex7; // Попередні дані

uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferProjection;
uniform mat4 gbufferProjectionInverse;

uniform vec3 cameraPosition;
uniform vec3 previousCameraPosition;
uniform float viewWidth;
uniform float viewHeight;
uniform float frameTimeCounter;
uniform int frameCounter;

// ========================
// НАЛАШТУВАННЯ
// ========================
#ifndef VOXEL_VOLUME_SIZE
    #define VOXEL_VOLUME_SIZE 128
#endif

#define VOXEL_VOLUME_RADIUS (VOXEL_VOLUME_SIZE / 2)

#ifndef VOXEL_SIZE
    #define VOXEL_SIZE 2.0
#endif

// ========================
// ДОПОМІЖНІ ФУНКЦІЇ
// ========================
vec2 screenSize = vec2(viewWidth, viewHeight);
vec2 screenPixelSize = 1.0 / screenSize;

#define rcp(x) (1.0 / (x))
#define saturate(x) clamp(x, 0.0, 1.0)
#define cube(x) ((x) * (x) * (x))
#define max0(x) max(x, 0.0)

float maxOf(vec3 v) { return max(v.x, max(v.y, v.z)); }

vec2 EncodeNormal(vec3 n) {
    n.xy /= abs(n.x) + abs(n.y) + abs(n.z);
    if (n.z <= 0.0) {
        n.xy = (vec2(1.0) - abs(n.yx)) * (step(0.0, n.xy) * 2.0 - 1.0);
    }
    return n.xy * 0.5 + 0.5;
}

vec3 DecodeNormal(vec2 en) {
    en = en * 2.0 - 1.0;
    vec3 normal = vec3(en, 1.0 - abs(en.x) - abs(en.y));
    if (normal.z <= 0.0) {
        normal.xy = (vec2(1.0) - abs(en.yx)) * (step(0.0, en) * 2.0 - 1.0);
    }
    return normalize(normal);
}

float PackUnorm2x8(vec2 xy) {
    return dot(floor(255.0 * xy + 0.5), vec2(1.0 / 65535.0, 256.0 / 65535.0));
}

vec3 ViewToScreenSpace(vec3 viewPos) {
    vec3 NDCPos = mat3(gbufferProjection) * viewPos + gbufferProjection[3].xyz;
    NDCPos /= -viewPos.z;
    return NDCPos * 0.5 + 0.5;
}

vec3 ScreenToViewSpace(vec3 screenPos) {
    vec3 NDCPos = screenPos * 2.0 - 1.0;
    vec3 viewPos = mat3(gbufferProjectionInverse) * NDCPos + gbufferProjectionInverse[3].xyz;
    viewPos /= gbufferProjectionInverse[2].w * NDCPos.z + gbufferProjectionInverse[3].w;
    return viewPos;
}

vec3 GetNormals(ivec2 texel) {
    return DecodeNormal(texelFetch(colortex3, texel, 0).xy);
}

// ========================
// ОСНОВНИЙ КОД
// ========================

in vec2 texcoord;

void main() {
    ivec2 screenPos = ivec2(gl_FragCoord.xy);
    
    // Розгортаємо 2D у 3D для воксельної сітки
    int voxelIndex = screenPos.y * int(viewWidth) + screenPos.x;
    
    if (voxelIndex >= VOXEL_VOLUME_SIZE * VOXEL_VOLUME_SIZE * VOXEL_VOLUME_SIZE) {
        voxelEmission = vec4(0.0);
        voxelAlbedoNormal = vec4(0.0);
        return;
    }
    
    // Розпаковуємо у 3D координати
    int z = voxelIndex / (VOXEL_VOLUME_SIZE * VOXEL_VOLUME_SIZE);
    int remainder = voxelIndex % (VOXEL_VOLUME_SIZE * VOXEL_VOLUME_SIZE);
    int y = remainder / VOXEL_VOLUME_SIZE;
    int x = remainder % VOXEL_VOLUME_SIZE;
    
    vec3 voxelCoord = (vec3(x, y, z) + 0.5) / VOXEL_VOLUME_SIZE;
    
    // Конвертуємо у світову позицію
    vec3 relativePos = (voxelCoord * VOXEL_VOLUME_SIZE - VOXEL_VOLUME_RADIUS) * VOXEL_SIZE;
    vec3 worldPos = relativePos + cameraPosition;
    
    // Проектуємо на екран
    vec3 viewPos = mat3(gbufferModelView) * (worldPos - cameraPosition);
    vec3 screenSpace = ViewToScreenSpace(viewPos);
    
    // Перевірка меж екрану
    if (any(lessThan(screenSpace.xy, vec2(0.0))) || 
        any(greaterThan(screenSpace.xy, vec2(1.0))) ||
        screenSpace.z < 0.0 || screenSpace.z > 1.0) {
        
        // Поза екраном - затухання старих даних
        vec4 prevEmission = texture(colortex6, voxelCoord);
        vec4 prevData = texture(colortex7, voxelCoord);
        
        // Плавне затухання
        float fadeSpeed = 0.92;
        voxelEmission = prevEmission * fadeSpeed;
        voxelAlbedoNormal = prevData * fadeSpeed;
        return;
    }
    
    // Семплюємо дані з екрану
    ivec2 screenTexel = ivec2(screenSpace.xy * screenSize);
    screenTexel = clamp(screenTexel, ivec2(0), ivec2(screenSize) - 1);
    
    vec4 albedo = texelFetch(colortex0, screenTexel, 0);
    vec4 materialData = texelFetch(colortex1, screenTexel, 0);
    vec3 normal = GetNormals(screenTexel);
    
    float depth = texelFetch(depthtex0, screenTexel, 0).x;
    
    // Якщо немає геометрії
    if (depth >= 0.9999) {
        vec4 prevEmission = texture(colortex6, voxelCoord);
        voxelEmission = prevEmission * 0.9;
        voxelAlbedoNormal = vec4(0.0);
        return;
    }
    
    // Отримуємо material ID
    int materialID = int(materialData.g * 255.0 + 0.5);
    
    // Обчислюємо емісію
    vec3 albedoRaw = pow(albedo.rgb, vec3(2.2));
    float albedoLuminance = length(albedoRaw);
    
    vec3 emission = vec3(0.0);
    float density = 0.5;
    
    // Спрощений switch з блоків освітлення
    switch (materialID) {
        // Факели
        case 21:
            float glowMask = max(
                float(albedoRaw.r > 0.8 || albedoRaw.r > albedoRaw.g * 1.4),
                float(albedoRaw.g > 0.8 || albedoRaw.g > albedoRaw.r * 1.4 && albedoRaw.g > albedoRaw.b * 1.4)
            );
            emission = vec3(1.0, 0.7, 0.3) * 6.0 * glowMask;
            density = 0.05;
            break;
        
        // Вогонь
        case 22:
        case 15:
            emission = vec3(1.0, 0.6, 0.2) * 6.0 * cube(albedoLuminance);
            density = 0.1;
            break;
        
        // Glowstone
        case 23:
            emission = vec3(1.0, 0.9, 0.6) * 4.0 * cube(albedoLuminance);
            density = 0.05;
            break;
        
        // Soul fire
        case 26:
            emission = vec3(0.2, 0.6, 1.0) * 8.0 * float(albedoRaw.b > 0.5);
            density = 0.03;
            break;
        
        // Лампи
        case 80: case 81: case 82: case 83:
        case 84: case 85: case 86: case 87:
            float isYellow = float(albedoRaw.r > 0.76 && albedoRaw.g > 0.66);
            emission = vec3(1.0, 0.8, 0.4) * 20.0 * isYellow;
            density = 0.008;
            break;
        
        // Redstone
        case 25:
            emission = vec3(2.1, 0.9, 0.9) * 8.0 * float(albedoRaw.r > 0.65);
            density = 0.1;
            break;
        
        // За замовчуванням
        default:
            if (albedoLuminance > 0.7) {
                emission = albedoRaw * 0.5;
                density = 0.3;
            } else {
                density = 0.8;
            }
            break;
    }
    
    // Записуємо результат
    voxelEmission = vec4(emission, density);
    voxelAlbedoNormal = vec4(albedoRaw, PackUnorm2x8(EncodeNormal(normal)));
}