#version 450
// UBO variant of shaders/chunk.vert for the Metal/Vulkan backends. Loose uniforms
// are gathered into a std140 Globals block (binding 0); the GL backend keeps the
// original loose-uniform shader. main() is identical to the GL source.
layout(location = 0) in uvec2 aPacked;

layout(std140, binding = 0) uniform Globals {
    mat4  uMVP;
    vec3  uChunkOrigin;
    float uDayLight;
    float uAmbient;
    float uAlphaCutoff;
    vec3  uFogColor;
    float uFogStart;
    float uFogEnd;
    int   uDynLightCount;
    vec4  uDynLightPosRadius0;
    vec4  uDynLightPosRadius1;
    vec4  uDynLightPosRadius2;
    vec4  uDynLightPosRadius3;
    vec4  uDynLightColor0;
    vec4  uDynLightColor1;
    vec4  uDynLightColor2;
    vec4  uDynLightColor3;
    int   uFogMode;
    float uFogDensity;
    float uFogHeightFalloff;
    float uTime;
    float uWaveAmp;
    vec3  uCamPos;
    float uTrip;
    float uTripTime;
};

layout(location = 0) out vec2 vUV;
layout(location = 1) flat out float vLayer;
layout(location = 2) out float vShade;
layout(location = 3) out float vDist;
layout(location = 4) out vec3 vWorld;
layout(location = 5) 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;
    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);
    vDist = length(world - uCamPos);
    gl_Position = clip;
}
