/********************************************************************************/
/*                                                                              */
/*    IterationVX-Style Water Waves (procedural noise-texture ocean waves)     */
/*    Ported from IterationVX 2.1 - Lib/IndividualFounctions/WaterWaves.glsl   */
/*    Base: IterationT 3.2.0 by Tahnass, an edit of Chocapic13's shaders.      */
/*    See NOTICE_IVX_WATER_PORT.txt for attribution details.                   */
/*                                                                              */
/*    Technique: 6-octave domain-warped FBM sampled from noisetex, animated    */
/*    over time and fed back into itself (each octave offsets the sample      */
/*    position of the next), instead of Gerstner's closed-form sine-wave      */
/*    math. Reads as broad, organic rolling-ocean swell - a different "look"  */
/*    than Gerstner's sharper, more directional wave crests, useful as an     */
/*    alternate style rather than a strict upgrade.                           */
/*                                                                              */
/*    Adapted to Unbound's water.glsl plug interface: takes a world-space     */
/*    position (playerPos + cameraPosition, matching Unbound's convention)    */
/*    and returns a TANGENT-SPACE pseudo normal-map sample (x, y, z) plus the */
/*    raw slope, the same shape gerstnerWaterNormalTangentSpace() and         */
/*    MakeupWaterWaveNormal() already return - so it drops straight into the  */
/*    existing normalMap.xy / normalMed / normalSmall handling in             */
/*    translucents/water.glsl with no other changes needed.                   */
/*                                                                              */
/********************************************************************************/
#ifdef IVX_WATER_WAVES

// Smoothed (bicubic-ish) noise texture fetch: snaps to the noise texture's
// texel grid and eases the fractional part with a smoothstep curve before
// sampling, so the low-res noisetex doesn't read as blocky when stretched
// across large waves. Renamed from the original `textureSmooth` / `curve`
// to avoid clashing with anything already in Unbound's namespace.
vec4 IvxTextureSmooth(sampler2D texSampler, vec2 coord) {
    coord *= 64.0;
    coord += 0.5;

    vec2 whole = floor(coord);
    vec2 part  = coord - whole;
    part = part * part * (3.0 - 2.0 * part); // smoothstep ease ("curve()" in the original)

    coord = whole + part;
    coord -= 0.5;
    coord *= 0.015625;

    return texture2DLod(texSampler, coord, 0.0);
}

// Flattens a value's extremes toward the middle while keeping it in [0,1] -
// used to soften the sharpest wave-crest octaves so they don't spike.
float IvxAlmostIdentity(float x, float m, float n) {
    x = abs(x * 2.0 - 1.0);

    if (x <= m) {
        float a = 2.0 * n - m;
        float b = 2.0 * m - 3.0 * n;
        float t = x / m;
        x = (a * t + b) * t * t + n;
    }
    return 1.0 - x;
}

// 6-octave domain-warped FBM height field, sampled from noisetex and
// animated by frameTimeCounter. Each octave scales/offsets the sample
// position using the PREVIOUS octave's own coordinate (the `p.x *= ...`
// feedback), which is what gives this technique its organic, non-repeating
// rolling look rather than a flat sum of independent noise layers.
float IvxGetWaves(vec3 position) {
    float wavesTime = frameTimeCounter * 0.0035;

    position *= IVX_WAVE_SCALE;
    vec2 p = position.xz - position.y;
    p += -3.875 * wavesTime;
    p.x = -p.x;

    vec2 waves = vec2(0.0);

    waves += vec2(IvxTextureSmooth(noisetex, p * vec2(2.0, 1.2) + vec2(0.0, p.x * 2.1)).w, 1.0);

    p = p * 0.475;
    p += vec2(-5.16, -7.75) * wavesTime;
    waves += vec2(IvxTextureSmooth(noisetex, p * vec2(2.0, 1.4) + vec2(0.0, -p.x * 2.1)).w * 4.0, 4.0);

    p *= 0.67;
    p.x += 7.75 * wavesTime;
    waves += vec2(IvxTextureSmooth(noisetex, p + vec2(0.0, p.x * 1.1)).z * 8.0, 8.0);

    p *= 0.7;
    p.x += -2.82 * wavesTime;
    waves += vec2(IvxTextureSmooth(noisetex, p * vec2(1.0, 0.75) + vec2(0.0, -p.x * 1.7)).w * 16.0, 16.0);

    p *= 0.45;
    p.x += wavesTime;
    waves += vec2(IvxAlmostIdentity(IvxTextureSmooth(noisetex, p * vec2(1.0, 0.8) + vec2(0.0, -p.x * 1.7)).z, 0.2, 0.1) * 30.0, 30.0);

    p *= 0.5;
    p.x += wavesTime;
    waves += vec2(IvxAlmostIdentity(IvxTextureSmooth(noisetex, p * vec2(1.0, 0.8) + vec2(0.0, p.x * 1.7)).w, 0.2, 0.1) * 20.0, 20.0);

    return waves.x / waves.y;
}

#define IVX_SAMPLE_DIST 12.0

// Returns a TANGENT-SPACE pseudo normal-map sample (x, y, z) built by
// finite-differencing IvxGetWaves in the X and Z directions (central-ish
// difference, same as Unbound's Gerstner path returning a slope) - a
// drop-in replacement for gerstnerWaterNormalTangentSpace()/
// MakeupWaterWaveNormal(). `worldPos` should be playerPos + cameraPosition
// (Unbound's usual world-space convention); `waveHeight` matches the
// original's per-call height scale (water.glsl passes something derived
// from NdotU there, same role as Gerstner's `strength` parameter).
vec3 ivxWaterNormalTangentSpace(vec3 worldPos, float waveHeight, out vec2 waveDerivativeOut) {
    vec3 position = worldPos;
    position.xz -= 0.005 * IVX_SAMPLE_DIST;

    float wavesCenter = IvxGetWaves(position);
    float wavesLeft = IvxGetWaves(position + vec3(0.01 * IVX_SAMPLE_DIST, 0.0, 0.0));
    float wavesUp   = IvxGetWaves(position + vec3(0.0, 0.0, 0.01 * IVX_SAMPLE_DIST));

    vec2 slope = vec2(wavesCenter - wavesLeft, wavesCenter - wavesUp)
                 * (waveHeight * (IVX_WAVE_HEIGHT / IVX_SAMPLE_DIST));
    waveDerivativeOut = slope;

    vec3 normalMapM;
    normalMapM.xy = slope;
    normalMapM.z  = sqrt(max0(1.0 - dot(normalMapM.xy, normalMapM.xy)));
    return normalize(normalMapM);
}

#endif // IVX_WATER_WAVES
