/////////////////////////////////////
// Unbound Visual + PT v7.3 by EminGT //
/////////////////////////////////////

//Common//
#include "/lib/common.glsl"

//////////Fragment Shader//////////Fragment Shader//////////Fragment Shader//////////
#ifdef FRAGMENT_SHADER

flat in int mat;

in vec2 texCoord;

flat in vec3 sunVec, upVec;

in vec4 position;
flat in vec4 glColor;

#ifdef CONNECTED_GLASS_EFFECT
    in vec2 signMidCoordPos;
    flat in vec2 absMidCoordPos;
#endif

//Pipeline Constants//

//Common Variables//
float SdotU = dot(sunVec, upVec);
float sunVisibility = clamp(SdotU + 0.0625, 0.0, 0.125) / 0.125;

#if defined CUSTOM_GI_VOXEL_INTERNAL && defined OVERWORLD
    #extension GL_ARB_shader_image_load_store : enable

    writeonly uniform image3D gi_voxel_img;
    #if GI_EMISSIVE_BLOCKS == 1
        writeonly uniform image3D gi_emission_img;
    #endif
    #include "/lib/voxelization/giFloodfill.glsl"

    // Voxelization (spec pasal 2): write this fragment's tinted albedo and a
    // binary opacity flag into the standalone GI voxel grid. Runs once per
    // shadow-casting fragment, i.e. every frame, so there's no separate
    // "dirty voxel" bookkeeping needed - the shadow pass already redraws
    // everything in shadowDistance each frame; begin.csh clears stale data.
    void GiVoxelizeFragment(vec3 albedo, float opacity, bool isSolid) {
        vec3 voxelPos = GiSceneToVoxel(position.xyz);
        if (!GiCheckInsideVolume(voxelPos)) return;
        imageStore(gi_voxel_img, ivec3(voxelPos), vec4(albedo, isSolid ? 1.0 : clamp(opacity, 0.0, 0.5)));
    }

    #if GI_EMISSIVE_BLOCKS == 1
        // Cheap per-block-ID emission lookup, matching the IDs assigned in
        // block.properties for light-emitting blocks. This runs in the
        // shadow pass (voxelization time), which only has the flat "mat" ID
        // and base texture color available - not the full IPBR material
        // pipeline (lightmaps, normals) used for the on-screen emissive
        // glow, so strengths here are an approximation tuned to feel
        // similar rather than pixel-identical to that pipeline's output.
        float GiGetEmissionStrength(int matID) {
            if (matID == 10068) return 3.0; // Lava
            if (matID == 10396) return 1.6; // Jack o'Lantern / Lit Pumpkin
            if (matID == 10412) return 2.0; // Glowstone / Light block
            if (matID == 10448) return 1.4; // Sea Lantern
            if (matID == 10496) return 1.1; // Torch
            if (matID == 10528) return 1.1; // Soul Torch
            if (matID == 10560) return 1.3; // Lantern
            if (matID == 10564) return 1.3; // Soul Lantern
            if (matID == 10604) return 1.2; // Redstone Torch: Lit
            if (matID == 10640) return 1.7; // Redstone Lamp: Lit
            if (matID == 10648) return 1.5; // Shroomlight
            if (matID == 10984) return 1.1; // Copper Torch
            if (matID == 10988) return 1.3; // Copper Lantern
            if (matID == 32016) return 3.5; // Beacon
            return 0.0;
        }

        void GiVoxelizeEmission(vec3 albedo, int matID) {
            float strength = GiGetEmissionStrength(matID);
            if (strength <= 0.0) return;

            vec3 voxelPos = GiSceneToVoxel(position.xyz);
            if (!GiCheckInsideVolume(voxelPos)) return;
            imageStore(gi_emission_img, ivec3(voxelPos), vec4(albedo * strength * float(GI_EMISSIVE_INTENSITY), 0.0));
        }
    #endif
#endif

//Common Functions//
void DoNaturalShadowCalculation(inout vec4 color1, inout vec4 color2) {
    color1.rgb *= glColor.rgb;
    color1.rgb = mix(vec3(1.0), color1.rgb, pow(color1.a, (1.0 - color1.a) * 0.5) * 1.05);
    color1.rgb *= 1.0 - pow(color1.a, 64.0);
    color1.rgb *= 0.1; // 423HDSS: Shadow color strength is stored 10 times lower to allow for water shadows going above 1.0

    color2.rgb = normalize(color1.rgb) * 0.5;
}

//Includes//
#ifdef CONNECTED_GLASS_EFFECT
    #include "/lib/materials/materialMethods/connectedGlass.glsl"
#endif

//Program//
void main() {
    vec4 color1 = texture2DLod(tex, texCoord, 0); // Shadow Color
    #ifdef SHADOW_COLORWHEEL
        vec2 lmCoord; // needed as otherwise undeclared in the function below
        float ao;
        vec4 overlayColor;

        clrwl_computeFragment(color1, color1, lmCoord, ao, overlayColor);
    #endif

    #if HIDE_ARMOR > 0
        if (currentRenderedItemId == 45124 && !isElytraFlying) discard;
    #endif

    #if defined CUSTOM_GI_VOXEL_INTERNAL && defined OVERWORLD
        {
            // ("bounce hanya ke block, bukan rumput/bunga" fix): mat IDs
            // 10005/10017/10021 are the waving-foliage groups from
            // block.properties - short grass, flowers, saplings, crops,
            // dead bush, tall grass/kelp/reeds, and the upper half of
            // double-tall plants. These are thin, alpha-tested AND they
            // sway every frame, so voxelizing them as solid geometry did
            // two bad things at once: (1) it made grass/flowers bounce and
            // receive light like a block, and (2) since their exact
            // silhouette inside a voxel cell shifts as they wave/as the
            // camera angle changes, the GI grid's content for that cell
            // kept changing frame to frame even when nothing "real" moved -
            // exactly the kind of flip-flopping input that reads as
            // aggressive ghosting/instability in the temporal accumulation
            // above. Skipping voxelization entirely for them (rather than
            // writing opacity 0) leaves whatever solid block is actually
            // underneath/behind them as the voxel's content, so the ground
            // still bounces light normally, it just isn't blended with the
            // grass blade sitting on top of it.
            // (reverted to v6.0 behaviour, "efek dobel" fix): a wider
            // `mat > 0 && mat < 50000` threshold was tried to also count
            // decorative block-entities (beds/chests/signs, mat 5000-9999)
            // as solid for GI, but that also pulled in thin/non-full-cube
            // blocks (slabs, stairs, etc.) whose actual mat IDs fall below
            // 10000 in this scheme - those got voxelized as a FULL solid
            // cube even though their real shape only fills part of it,
            // showing up as an oversized "phantom" GI shadow/highlight next
            // to the real geometry (the offset double-shape/line artifact).
            // Back to `mat >= 10000`, which only counts regular full-cube
            // terrain as solid.
            // ("light bounce bocor kena karpet" fix): carpets (10669/10845/10925/
            // 10927, including tripwire) and moss carpets are only 1/16 thick and
            // sit ON the floor - voxelizing them as a FULL solid cube made the GI
            // bounce hit a phantom carpet-height block, so bounce light read as
            // leaking/overlapping the carpet instead of the floor beneath it.
            // Skipping them like foliage leaves the real floor voxel underneath
            // as the cell's content, so bounce stays on the floor naturally.
            bool giIsFoliage = (mat == 10005 || mat == 10017 || mat == 10021
                             || mat == 10669 || mat == 10845 || mat == 10925 || mat == 10927);
            // Transmissive GI blocks: stained glass/panes (31000-32000) and
            // water (32000). These let GI rays PASS THROUGH while multiplying
            // the bounced light by their tinted albedo (see the transmissive
            // accumulation in GiTraceRay) instead of stopping the ray like a
            // solid wall - this is what makes light that travelled through a
            // colored glass window bounce with that glass's color, and light
            // entering water bounce tinted by the water color. (Glass/panes
            // 32008/32012 and ice 32004 stay SOLID for GI exactly as before.)
            bool giIsTransmissive = (mat >= 31000 && mat < 32000) || mat == 32000;
            bool giIsSolid = mat >= 10000 && !giIsTransmissive && !giIsFoliage;
            if (!giIsFoliage) {
                float giOpacity = 0.0;
                if (giIsTransmissive) giOpacity = (mat == 32000) ? 0.15 : 0.3;
                GiVoxelizeFragment(color1.rgb * glColor.rgb, giOpacity, giIsSolid);
                #if GI_EMISSIVE_BLOCKS == 1
                    GiVoxelizeEmission(color1.rgb * glColor.rgb, mat);
                #endif
            }
        }
    #endif

    #if PLAYER_SHADOW == -1
        if (entityId == 50016 || entityId == 50017) { // Player
            discard;
        }
    #endif

    #if SHADOW_QUALITY >= 1
        vec4 color2 = color1; // Light Shaft Color

        color2.rgb *= 0.25; // Natural Strength

        #if defined LIGHTSHAFTS_ACTIVE && LIGHTSHAFT_BEHAVIOUR == 1 && defined OVERWORLD
            float positionYM = position.y;
        #endif

        if (mat < 32008) {
            if (mat < 32000) {
                #if defined CONNECTED_GLASS_EFFECT || defined LIGHTSHAFTS_ACTIVE && LIGHTSHAFT_BEHAVIOUR == 1 && defined OVERWORLD
                    if (mat == 30008 || mat >= 31000) { // Tinted Glass || Stained Glass, Stained Glass Pane
                        #ifdef CONNECTED_GLASS_EFFECT
                            DoSimpleConnectedGlass(color1);
                        #endif
                        
                        #if defined LIGHTSHAFTS_ACTIVE && LIGHTSHAFT_BEHAVIOUR == 1 && defined OVERWORLD
                            positionYM = 0.0; // 86AHGA: For scene-aware light shafts to be less prone to get extreme under large glass planes
                        #endif
                    }
                #endif
                DoNaturalShadowCalculation(color1, color2);
            } else {
                if (mat == 32000) { // Water
                    vec3 worldPos = position.xyz + cameraPosition;

                    #if defined LIGHTSHAFTS_ACTIVE && LIGHTSHAFT_BEHAVIOUR == 1 && defined OVERWORLD
                        // For scene-aware light shafts to be more prone to get extreme near water
                        positionYM += 3.5;
                    #endif

                    // Water Caustics
                    #if WATER_CAUSTIC_STYLE < 3
                        #if MC_VERSION >= 11300
                            float wcl = GetLuminance(color1.rgb);
                            color1.rgb = color1.rgb * pow2(wcl) * 1.2;
                        #else
                            color1.rgb = mix(color1.rgb, vec3(GetLuminance(color1.rgb)), 0.88);
                            color1.rgb = pow2(color1.rgb) * vec3(2.5, 3.0, 3.0) * 0.96;
                        #endif
                    #else
                        #define WATER_SPEED_MULT_M WATER_SPEED_MULT * 0.035
                        vec2 causticWind = vec2(0.0, frameTimeCounter * WATER_SPEED_MULT_M);
                        vec2 cPos1 = worldPos.xz * 0.08 + causticWind;
                        vec2 cPos2 = worldPos.xz * 0.06 - causticWind;

                        float cMult = 14.0;
                        float offset = 0.001;

                        float caustic = 0.0;
                        caustic += dot(texture2D(gaux4, cPos1 + vec2(offset, 0.0)).rg, vec2(cMult))
                                 - dot(texture2D(gaux4, cPos1 - vec2(offset, 0.0)).rg, vec2(cMult));
                        caustic += dot(texture2D(gaux4, cPos2 + vec2(0.0, offset)).rg, vec2(cMult))
                                 - dot(texture2D(gaux4, cPos2 - vec2(0.0, offset)).rg, vec2(cMult));
                        color1.rgb = vec3(max0(min1(caustic * 0.8 + 0.35)) * 0.65 + 0.35);

                        #if MC_VERSION < 11300
                            color1.rgb *= vec3(0.3, 0.45, 0.9);
                        #endif
                    #endif

                    #if WATER_CAUSTIC_INTENSITY != 100
                        #define WATER_CAUSTIC_INTENSITY_M WATER_CAUSTIC_INTENSITY * 0.01
                        color1.rgb = mix(vec3(1.0), color1.rgb, WATER_CAUSTIC_INTENSITY_M);
                    #endif

                    #if MC_VERSION >= 11300
                        #if WATERCOLOR_MODE >= 2
                            color1.rgb *= glColor.rgb;
                        #else
                            color1.rgb *= vec3(0.3, 0.45, 0.9);
                        #endif
                    #endif
                    color1.rgb *= vec3(0.6, 0.8, 1.1);
                    color1.rgb = pow(color1.rgb, vec3(0.75)) * 0.5;
                    ////

                    // Underwater Light Shafts
                    vec3 worldPosM = worldPos;

                    #if WATER_FOG_MULT > 100
                        #define WATER_FOG_MULT_M WATER_FOG_MULT * 0.01;
                        worldPosM *= WATER_FOG_MULT_M;
                    #endif

                    vec2 waterWind = vec2(syncedTime * 0.01, 0.0);
                    float waterNoise = texture2DLod(noisetex, worldPosM.xz * 0.012 - waterWind, 0.0).g;
                          waterNoise += texture2DLod(noisetex, worldPosM.xz * 0.05 + waterWind, 0.0).g;

                    float factor = max(2.5 - 0.025 * length(position.xz), 0.8333) * 1.3;
                    waterNoise = pow(waterNoise * 0.5, factor) * factor * 1.3;

                    #if MC_VERSION >= 11300 && WATERCOLOR_MODE >= 2
                        color2.rgb = normalize(sqrt1(glColor.rgb)) * vec3(0.24, 0.22, 0.26);
                    #else
                        color2.rgb = vec3(0.08, 0.12, 0.195);
                    #endif
                    color2.rgb *= waterNoise * (1.0 + sunVisibility - rainFactor);
                    ////

                    #ifdef UNDERWATERCOLOR_CHANGED
                        color1.rgb *= vec3(UNDERWATERCOLOR_RM, UNDERWATERCOLOR_GM, UNDERWATERCOLOR_BM);
                        color2.rgb *= vec3(UNDERWATERCOLOR_RM, UNDERWATERCOLOR_GM, UNDERWATERCOLOR_BM);
                    #endif
                } else /*if (mat == 32004)*/ { // Ice
                    color1.rgb *= color1.rgb;
                    color1.rgb *= color1.rgb;
                    color1.rgb = mix(vec3(1.0), color1.rgb, pow(color1.a, (1.0 - color1.a) * 0.5) * 1.05);
                    color1.rgb *= 1.0 - pow(color1.a, 64.0);
                    color1.rgb *= 0.14; // 423HDSS

                    color2.rgb = normalize(pow(color1.rgb, vec3(0.25))) * 0.5;
                }
            }
        } else {
            if (mat < 32020) { // Glass, Glass Pane, Beacon (32008, 32012, 32016)
                #ifdef CONNECTED_GLASS_EFFECT
                    if (mat == 32008) { // Glass
                        DoSimpleConnectedGlass(color1);
                    }
                    if (mat == 32012) { // Glass Pane
                        DoSimpleConnectedGlass(color1);
                    }
                #endif
                if (color1.a > 0.5) color1 = vec4(0.0, 0.0, 0.0, 1.0);
                else color1 = vec4(vec3(0.1 * (1.0 - GLASS_OPACITY)), color1.a); // 423HDSS
                color2.rgb = vec3(0.3);

                #if defined LIGHTSHAFTS_ACTIVE && LIGHTSHAFT_BEHAVIOUR == 1 && defined OVERWORLD
                    positionYM = 0.0; // 86AHGA
                #endif
            } else {
                DoNaturalShadowCalculation(color1, color2);
            }
        }
    #endif

    /* DRAWBUFFERS:0 */
    gl_FragData[0] = color1; // Shadow Color

    #if SHADOW_QUALITY >= 1
        #if defined LIGHTSHAFTS_ACTIVE && LIGHTSHAFT_BEHAVIOUR == 1 && defined OVERWORLD
            color2.a = 0.25 + max0(positionYM * 0.05); // consistencyMEJHRI7DG
        #endif

        /* DRAWBUFFERS:01 */
        gl_FragData[1] = color2; // Light Shaft Color
    #endif
}

#endif

//////////Vertex Shader//////////Vertex Shader//////////Vertex Shader//////////
#ifdef VERTEX_SHADER

flat out int mat;

out vec2 texCoord;

flat out vec3 sunVec, upVec;

out vec4 position;
flat out vec4 glColor;

#ifdef CONNECTED_GLASS_EFFECT
    out vec2 signMidCoordPos;
    flat out vec2 absMidCoordPos;
#endif

//Pipeline Constants//
#if COLORED_LIGHTING_INTERNAL > 0
    #extension GL_ARB_shader_image_load_store : enable
#endif

//Attributes//
attribute vec4 mc_Entity;
attribute vec4 mc_midTexCoord;

#if COLORED_LIGHTING_INTERNAL > 0
    attribute vec3 at_midBlock;
#endif

//Common Variables//
vec2 lmCoord;

#if COLORED_LIGHTING_INTERNAL > 0
    writeonly uniform uimage3D voxel_img;

    #ifdef PUDDLE_VOXELIZATION
        writeonly uniform uimage2D puddle_img;
    #endif

    #if WORLD_SPACE_REFLECTIONS_INTERNAL > 0
        writeonly uniform uimage3D wsr_img;
        writeonly uniform uimage3D wsr_lod_img;
    #endif
#endif

//Common Functions//

//Includes//
#include "/lib/util/spaceConversion.glsl"

#if defined WAVING_ANYTHING_TERRAIN || defined WAVING_WATER_VERTEX
    #include "/lib/materials/materialMethods/wavingBlocks.glsl"
#endif

#if COLORED_LIGHTING_INTERNAL > 0
    #include "/lib/voxelization/lightVoxelization.glsl"

    #ifdef PUDDLE_VOXELIZATION
        #include "/lib/voxelization/puddleVoxelization.glsl"
    #endif

    #if WORLD_SPACE_REFLECTIONS_INTERNAL > 0
        #include "/lib/voxelization/reflectionVoxelization.glsl"
    #endif
#endif

//Program//
void main() {
    texCoord = gl_MultiTexCoord0.xy;
    lmCoord = GetLightMapCoordinates();
    glColor = gl_Color;
    sunVec = GetSunVector();
    upVec = normalize(gbufferModelView[1].xyz);
    mat = int(mc_Entity.x + 0.5);

    position = shadowModelViewInverse * shadowProjectionInverse * ftransform();

    #if defined WAVING_ANYTHING_TERRAIN || defined WAVING_WATER_VERTEX
        DoWave(position.xyz, mat);
    #endif

    #ifdef CONNECTED_GLASS_EFFECT
        vec2 midCoord = (gl_TextureMatrix[0] * mc_midTexCoord).st;
        vec2 texMinMidCoord = texCoord - midCoord;
        signMidCoordPos = sign(texMinMidCoord);
        absMidCoordPos  = abs(texMinMidCoord);
    #endif

    #ifdef PERPENDICULAR_TWEAKS
        if (mat == 10005 || mat == 10017) { // Foliage
            #ifndef CONNECTED_GLASS_EFFECT
                vec2 midCoord = (gl_TextureMatrix[0] * mc_midTexCoord).st;
                vec2 texMinMidCoord = texCoord - midCoord;
            #endif
            if (texMinMidCoord.y < 0.0) {
                vec3 normal = gl_NormalMatrix * gl_Normal;
                position.xyz += normal * 0.35;
            }
        }
    #endif

    if (mat == 32000) { // Water
        position.y += 0.015 * max0(length(position.xyz) - 50.0);
    }

    #if COLORED_LIGHTING_INTERNAL > 0
        if (gl_VertexID % 4 == 0) {
            #if defined SUN_BOUNCE_VOXEL_LIGHT && defined OVERWORLD
                vec3 voxelFaceNormal = mat3(shadowModelViewInverse) * gl_NormalMatrix * gl_Normal;
                vec3 sunVecWorld = mat3(gbufferModelViewInverse) * sunVec;
                // lmCoord.y = vanilla skylight (1.0 = open sky, 0.0 = fully
                // covered/underground). Used to keep Sun/Moon Bounce restricted
                // to faces genuinely exposed to the sky, instead of any face
                // that merely faces the sun/moon's direction regardless of
                // roofs/ceilings above it. See lightVoxelization.glsl.
                UpdateVoxelMap(mat, voxelFaceNormal, sunVecWorld, lmCoord.y);
            #else
                UpdateVoxelMap(mat, vec3(0.0), vec3(0.0), 0.0);
            #endif
            #ifdef PUDDLE_VOXELIZATION
                UpdatePuddleVoxelMap(mat);
            #endif
            #if WORLD_SPACE_REFLECTIONS_INTERNAL > 0
                vec3 normal = mat3(shadowModelViewInverse) * gl_NormalMatrix * gl_Normal;
                UpdateSceneVoxelMap(mat, normal, position.xyz);
            #endif
        }

        #if WORLD_SPACE_REFLECTIONS_INTERNAL > 0 && WORLD_SPACE_PLAYER_REF == 1
            UpdatePlayerVertexList(position.xyz);
        #endif
    #endif

    gl_Position = shadowProjection * shadowModelView * position;

    float lVertexPos = sqrt(gl_Position.x * gl_Position.x + gl_Position.y * gl_Position.y);
    float distortFactor = lVertexPos * shadowMapBias + (1.0 - shadowMapBias);
    gl_Position.xy *= 1.0 / distortFactor;

    #ifdef SHADOW_TEXEL_SNAP
        // See SHADOW_TEXEL_SNAP definition in common.glsl. Applied after
        // the distortion above (so it snaps to the texel grid that
        // actually gets rasterized) and before the z-scale below (so it
        // never touches depth/bias).
        float shadowTexelSize = 2.0 / float(shadowMapResolution);
        gl_Position.xy = floor(gl_Position.xy / shadowTexelSize + 0.5) * shadowTexelSize;
    #endif

    gl_Position.z = gl_Position.z * 0.2;
}

#endif
