vec3 calcAmbientLight(vec3 wNormal, vec2 lightmap, vec3 ambientUp, vec3 ambientDown, vec3 ambientRight,
                      vec3 ambientLeft, vec3 ambientB, vec3 ambientF, vec3 RSML, float diffuseSun, vec3 torch)
{
    vec3 ambientCoefs = wNormal / dot(abs(wNormal), vec3(1.0));

    vec3 clampedCoefs = max(ambientCoefs, 0.0);
    vec3 clampedCoefsNeg = max(-ambientCoefs, 0.0);

    float mixstrength = clamp(pow(shadowblend, 0.3), 0.0, 1.0);
    float oneMinusMixStrength = 1.0 - mixstrength;
    vec3 sun = sunLight;
#ifndef SHADOWS_ENABLED
    // Calculate luma (luminosity / perceived brightness)
    float luma = dot(sun.rgb, vec3(0.299, 0.587, 0.114));

    // Increase vibrance by pushing color away from gray (amplify difference from luma)
    float vibranceFactor = 1.5; // Increase this for more vibrance
    sun.rgb = mix(vec3(luma), sun.rgb, vibranceFactor);
#endif

#ifndef OVERWORLD
    sun = mix(sunLight, vec3(1), 0.5) * 0.01;

    float skymap = 1.0;
#else
    float skymap = lightmap.y;
#endif

    float skymap2 = skymap * skymap;

    // Precompute factors to minimize operations
    float diffuseSunClamped = clamp(diffuseSun, 0.5, 1.0);

    // Compute bounce lighting

    float bounceFactor = (0.15 * diffuseSunClamped) * oneMinusMixStrength + 0.25;
    vec3 bounce = sun * 0 * skymap;

#ifdef NETHER
    bounce = vec3(1, 0.25, 0.12) * 3.0;
#endif

#ifdef END
    bounce = vec3(1, 0.30, 0.12) * 1.0;
#endif

    // Adjust ambient sides based on mixstrength
    // ambientRight = ambientDown * oneMinusMixStrength + ambientRight * mixstrength;
    //   ambientLeft = ambientDown * oneMinusMixStrength + ambientLeft * mixstrength;

    // Combine ambient light components
    vec3 ambientCombined = (ambientUp * clampedCoefs.y +
                            ambientDown * clampedCoefsNeg.y +
                            ambientRight * clampedCoefs.x +
                            ambientLeft * clampedCoefsNeg.x +
                            ambientB * clampedCoefs.z +
                            ambientF * clampedCoefsNeg.z) *
                               skymap2 +
                           RSML;

    // Mix ambientCombined with bounce lighting
    ambientCombined = ambientCombined * 0.9 + bounce * 0.1;

#ifndef OVERWORLD

    ambientCombined *= 2.0;
#endif
#ifndef SHADOWS_ENABLED

    //  ambientCombined *= 2;
#endif
    // Calculate diffuse lighting
    vec3 diffuseLight = diffuseSun * sun + ambientCombined;

    // Compute darkness factors
    float darknessFactor = darknessLightFactor * darknessLightFactor * darknessLightFactor;
    diffuseLight *= (1.0 - 6.0 * darknessFactor);

    // Prepare torch and small light contributions
    vec3 torchContribution = torch * hdrMulttorch;
    float screenBrightnessTerm = 1.0 + screenBrightness * MIN_LIGHT_BOOST;
    vec3 smallLightContribution = vec3(0.002, 0.005, 0.006) * screenBrightnessTerm;

    // Sum up the light components and apply final adjustments
    vec3 light = max(diffuseLight + torchContribution + smallLightContribution + nightVision * 0.1, 0.001);
    light *= 1.0 - darknessLightFactor;

    return light;
}

vec3 calcAmbientLight(vec3 Normal, vec2 lightmap, vec3 ambientUp, float diffuseSun, vec3 torch)
{

    vec3 ambient = calcAmbientLight(Normal, lightmap.xy, ambientUp, ambientUp, ambientUp, ambientUp, ambientUp, ambientUp, vec3(0.6 * ambientUp.b * lightmap.y), diffuseSun, torch);

    return ambient;
}
// Function to calculate the torch lighting with optional LPV support
#ifdef LPV_ENABLED
    #include "/lib/lpv/lpv_common.glsl"
    #include "/lib/lpv/lpv_render.glsl"
#endif

vec3 CalculateTorch(
    vec2 lightmap,
    vec3 normal,
    vec3 fragpos,
    float ao)
{
    // Cache lightmap.x to avoid redundant swizzles.
    float lm = lightmap.x;

    // Initialize torch color.
    vec3 torch = vec3(3.000, 1.3, 0.42);
    float lightmapPow = lm * lm * lm;
    float lightFactor = lightmapPow + lm * 0.025;
    torch *= lightFactor;
    torch = clamp(torch, 0.0, 4.0) * 1.5;

#ifdef LPV_ENABLED
    // Precompute normal offset multiplier.
    const float normOffsetScale = LPV_NORMAL_STRENGTH * 0.01;
    vec3 normalOffset = normal * normOffsetScale;

    // Transform fragpos into LPV space.
    vec3 transformedPos = toWorldSpace(fragpos);
    vec3 lpvPos = GetLpvPosition(transformedPos) + normalOffset;

    // Simplify 1.0 + (1.0 - ao) to (2.0 - ao)
    vec3 LpvTorchLight = GetLpvBlockLight(SampleLpvLinear(lpvPos)) * (2.0 - ao);

    // Compute LPV light factor from lightmap and apply.
    float lpvLightFactor = clamp(lm * (lm + 0.25), 0.0, 10.0);

    LpvTorchLight *= lpvLightFactor;

    // Precompute reciprocal of LPV_BLEND_DISTANCE (if LPV_BLEND_DISTANCE is a constant).
    float invBlend = 1.0 / LPV_BLEND_DISTANCE;

    // Compute cubic radius based on LPV position.
    vec3 radiusA = (((LpvSize3 - 1.0) - lpvPos) * invBlend);
    vec3 radiusB = (lpvPos * invBlend);
    vec3 cubicRadius = clamp(min(radiusA, radiusB), 0.0, 1.0);

    // Combine the three components.
    float LpvFadeF = cubicRadius.x * cubicRadius.y * cubicRadius.z;

    // Smooth the fade factor.
    LpvFadeF = 1.0 - pow(1.0 - pow(LpvFadeF, 1.5), 3.0);

    #ifdef DYNAMIC_LIGHTS_COMP
    // Optionally adjust fade factor based on the distance between LPV and torch colors.
    LpvFadeF = clamp(1.0 - pow(distance(LpvTorchLight, torch), 0.25), 0.0, 1.0);
    #endif

    // Blend the base torch with the LPV torch light.
    // if (texcoord.x > 0.5)

    torch = mix(torch, LpvTorchLight, LpvFadeF);
    // torch = vec3(LpvFadeF);
#endif
#ifdef NETHER
    torch *= 2.0;
#endif
    return torch;
}