/********************************************************************************/
/*                                                                              */
/*    3D LUT Color Grading                                                     */
/*    Ported from Noble Shaders (https://github.com/BelmuTM/Noble)             */
/*    Copyright (C) 2026  Belmu                                                */
/*    Adapted for Unbound Visual + PT v7.3 by EminGT                                   */
/*                                                                              */
/*    This file is free software: you can redistribute it and/or modify        */
/*    it under the terms of the GNU General Public License as published by     */
/*    the Free Software Foundation, either version 3 of the License, or        */
/*    (at your option) any later version.                                      */
/*                                                                              */
/*    This program is distributed in the hope that it will be useful,          */
/*    but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
/*    GNU General Public License for more details.                             */
/*                                                                              */
/*    You should have received a copy of the GNU General Public License        */
/*    along with this program.  If not, see <https://www.gnu.org/licenses/>.   */
/*                                                                              */
/*    NOTE: Because this file is derived from GPLv3-licensed code, any         */
/*    build of this pack that includes it must also be distributed under       */
/*    GPLv3 (source available, same license, changes marked) if you share      */
/*    it with others. Keep this header intact.                                */
/*                                                                              */
/********************************************************************************/

/*
    [Original references, carried over from Noble Shaders]:
        spolsh. (2017). Crystal Drops. https://www.shadertoy.com/view/XlSBRW
        NVIDIA GPU Gems 2, Ch. 24 - Using Lookup Tables to Accelerate Color Transformations
        https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/chapter-24-using-lookup-tables-accelerate-color
*/

// The LUT PNGs are 512x512 images that pack a 64-step 3D LUT as an 8x8 grid of
// 64x64 tiles (one tile per "blue" slice). LUT_BUFFER is a colortex slot that
// shaders.properties statically loads one of the bundled lutN.png files into
// (see the "texture.composite5.colortexN" lines), instead of it being rendered
// to like a normal buffer.
#ifndef LUT_BUFFER
    #define LUT_BUFFER colortex10
#endif

#if LUT > 0

    const int   lutTileSize = 8;
    const int   lutSize     = lutTileSize * lutTileSize;
    const vec2  lutRes      = vec2(lutSize * lutTileSize);

    const float rcpLutTileSize = 1.0 / lutTileSize;
    const vec2  rcpLutTexSize  = 1.0 / lutRes;

    // Manual bilinear sampling of a single LUT tile. Regular hardware linear
    // filtering would bleed across tile borders (sampling into the neighboring
    // blue-slice tile), so each tap is fetched by hand and blended manually.
    vec3 LutTapLinear(const in vec2 coords) {
        vec2 f          = fract(coords * lutRes);
        vec2 texelSize  = rcpLutTexSize;

        vec2 uv0 = coords - f * texelSize;
        vec2 uv1 = uv0 + vec2(texelSize.x, 0.0);
        vec2 uv2 = uv0 + vec2(0.0, texelSize.y);
        vec2 uv3 = uv0 + vec2(texelSize.x, texelSize.y);

        vec3 s0 = texture2DLod(LUT_BUFFER, uv0, 0.0).rgb;
        vec3 s1 = texture2DLod(LUT_BUFFER, uv1, 0.0).rgb;
        vec3 s2 = texture2DLod(LUT_BUFFER, uv2, 0.0).rgb;
        vec3 s3 = texture2DLod(LUT_BUFFER, uv3, 0.0).rgb;

        vec3 x1 = mix(s0, s1, f.x);
        vec3 x2 = mix(s2, s3, f.x);
        return mix(x1, x2, f.y);
    }

    void ApplyLUT(inout vec3 color) {
        vec3 original = color;

        // Keep a hair inside 0-1 so the lookup never lands exactly on a tile edge
        vec3 lutIn = clamp(color, vec3(0.02745098039), vec3(0.96862745098));

        lutIn.b *= (lutSize - 1.0);
        int bLow  = int(lutIn.b);
        int bHigh = bLow + 1;

        vec2 offLow  = vec2(bLow  % lutTileSize, bLow  / lutTileSize) * rcpLutTileSize;
        vec2 offHigh = vec2(bHigh % lutTileSize, bHigh / lutTileSize) * rcpLutTileSize;

        vec3 graded = mix(
            LutTapLinear(offLow  + lutIn.rg * rcpLutTileSize),
            LutTapLinear(offHigh + lutIn.rg * rcpLutTileSize),
            lutIn.b - bLow
        );

        // Blend instead of a hard replace ("LUT nyakitin mata" fix) - see
        // LUT_STRENGTH in common.glsl.
        color = mix(original, graded, clamp(float(LUT_STRENGTH) * 0.01, 0.0, 1.0));
    }

#endif
