#version 410 core
layout(location = 0) in uvec2 aPacked;

uniform mat4 uMVP;
uniform vec3 uChunkOrigin;
uniform float uDayLight; // 0..1 sky brightness from time of day
uniform float uAmbient;  // minimum brightness floor (per-dimension)
uniform float uTime;     // seconds-scale clock (day-cycle derived, deterministic)
uniform float uWaveAmp;  // liquid vertex-wave amplitude (0 = waves off)
uniform vec3 uCamPos;
uniform float uTrip;
uniform float uTripTime;

out vec2 vUV;
flat out float vLayer;
out float vShade;
out float vDist;
out vec3 vWorld;
flat out float vLiquid;

void main() {
    uint px = aPacked.x & 0x3ffu;
    uint pz = (aPacked.x >> 10u) & 0x3ffu;
    uint py = ((aPacked.x >> 20u) & 0xfffu) | ((aPacked.y & 0x3u) << 12u);
    vec3 aPos = vec3(float(px), float(py), float(pz)) / 32.0;

    uint layer = (aPacked.y >> 2u) & 0x3ffu;
    float shade = float((aPacked.y >> 12u) & 0x3fu) / 63.0;
    float skyLight = float((aPacked.y >> 18u) & 0xfu) / 15.0;
    float blockLight = float((aPacked.y >> 22u) & 0xfu) / 15.0;
    uint axis = (aPacked.y >> 26u) & 0x3u;
    bool explicitUV = ((aPacked.y >> 28u) & 1u) != 0u;
    bool liquid = ((aPacked.y >> 31u) & 1u) != 0u;
    vLiquid = liquid ? 1.0 : 0.0;

    if (explicitUV) {
        vUV = vec2(float((aPacked.y >> 29u) & 1u),
                   float((aPacked.y >> 30u) & 1u));
    } else if (axis == 0u) {
        vUV = vec2(aPos.z, -aPos.y);
    } else if (axis == 1u) {
        vUV = vec2(aPos.x, -aPos.z);
    } else {
        vUV = vec2(aPos.x, -aPos.y);
    }
    vLayer = float(layer);
    float sky = skyLight * uDayLight;
    float light = max(sky, blockLight);
    float bright = mix(uAmbient, 1.0, light); // ambient floor so nothing is pure black
    vShade = shade * bright;

    vec3 world = aPos + uChunkOrigin;
    vWorld = world;
    // Liquid surface waves are render-only: gl_Position wobbles, vWorld doesn't,
    // so lighting/fog stay stable. Amplitude stays under 0.05 to avoid z-fights.
    vec3 posW = world;
    if (uTrip > 0.001) {
        vec3 center = floor(world) + vec3(0.5);
        vec3 local = world - center;
        float beat = sin(dot(center, vec3(0.31, 0.47, 0.23)) + uTripTime * 2.2);
        float stretch = 1.0 + beat * 0.36 * uTrip;
        float squash = inversesqrt(max(stretch, 0.35));
        posW = center + local * vec3(squash, stretch, squash);
        posW.x += sin(center.y * 0.55 + uTripTime * 1.3) * 0.10 * uTrip;
        posW.z += cos(center.x * 0.43 - uTripTime * 1.1) * 0.10 * uTrip;
    }
    if (liquid) posW.y += (sin(world.x * 0.9 + uTime * 1.7) +
                           cos(world.z * 1.1 + uTime * 1.3)) * 0.045 * uWaveAmp;
    vec4 clip = uMVP * vec4(posW, 1.0);
    // CPU fog-ring culling is radial, so shader fog must use the same metric.
    // clip.w is only forward depth and left diagonal chunks visibly unfogged
    // when the radial culler removed them.
    vDist = length(world - uCamPos);
    gl_Position = clip;
}
