/****************************************************************************
**
** Copyright (C) 2014 NVIDIA Corporation.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt 3D Studio.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef SAMPLE_PROBE_GLSLLIB
#define SAMPLE_PROBE_GLSLLIB 1

#ifdef QQ3D_SHADER_META
/*{
    "uniforms": [
        { "type": "samplerCube", "name": "qt_lightProbe" },
        { "type": "vec4", "name": "qt_lightProbeProperties" },
        { "type": "mat4", "name": "qt_lightProbeOrientation", "condition": "QSSG_ENABLE_IBL_ORIENTATION" }
    ]
}*/
#endif // QQ3D_SHADER_META

vec3 qt_textureProbe(samplerCube lightProbe, vec3 dir, float lod)
{
#ifdef QSSG_ENABLE_RGBE_LIGHT_PROBE
    vec4 ret = textureLod(lightProbe, dir, lod);
    return ret.rgb * pow(2.0, ret.a * 255.0 - 128.0);
#else
    return textureLod(lightProbe, dir, lod).rgb;
#endif
}

vec4 qt_sampleDiffuse(vec3 normal)
{
    // Note: qt_lightProbeProperties.w = Exposure
    if (qt_lightProbeProperties.w < 0.005)
        return vec4(0.0);
    vec3 smpDir = normal;
    #if QSSG_ENABLE_IBL_ORIENTATION
        smpDir = mat3(qt_lightProbeOrientation) * smpDir;
    #endif
    float baseLevel = qt_lightProbeProperties.y;
    // Irradiance map is contained in mip at baseLevel
    vec3 val = qt_textureProbe(qt_lightProbe, smpDir, baseLevel);

    if (qt_lightProbeProperties.z > -1.0) {
        float ctr = 0.5 + 0.5 * qt_lightProbeProperties.z;
        float vertWt = smoothstep(ctr * 0.25, ctr + 0.25, smpDir.y);
        float wtScaled = mix(1.0, vertWt, qt_lightProbeProperties.z + 1.0);
        val *= wtScaled;
    }

    return vec4(qt_lightProbeProperties.w * val, 1.0);
}

// This method is used by DefaultMaterial for the specular term
vec4 qt_sampleGlossy(vec3 normal, vec3 viewDir, float rough)
{
    // Note: qt_lightProbeProperties.w == Exposure
    if (qt_lightProbeProperties.w < 0.005)
        return vec4(0.0);

    float sigma = smoothstep(0.0, 1.0, clamp(rough, 0.0001, 1.0));
    vec3 ret = vec3(0, 0, 0);
    vec3 smpDir = reflect(-viewDir, normal);
#if QSSG_ENABLE_IBL_ORIENTATION
    smpDir = mat3(qt_lightProbeOrientation) * smpDir;
#endif

    // Compute the Geometric occlusion/self-shadowing term
    float NdotL = clamp(dot(smpDir, normal), 0.0, 0.999995);
    float k = sigma * 0.31830988618;    // roughness / pi
    float Gl = clamp((NdotL / (NdotL*(1.0-k) + k) + (1.0 - k*k)) * 0.5, 0.0, 1.0 );
    float levels = qt_lightProbeProperties.y - 1.0;

    vec3 outColor = qt_textureProbe(qt_lightProbe, smpDir, sigma * levels);
    if (qt_lightProbeProperties.z > -1.0) {
        float ctr = 0.5 + 0.5 * qt_lightProbeProperties.z;
        float vertWt = smoothstep(ctr * 0.25, ctr + 0.25, smpDir.y);
        float wtScaled = mix(1.0, vertWt, qt_lightProbeProperties.z + 1.0);
        outColor *= wtScaled;
    }

    return vec4(qt_lightProbeProperties.w * Gl * outColor, 1.0);
}

// An alternative to using a LUT is to use this function
// see: https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
vec2 qt_brdfApproximation(in vec3 N, in vec3 viewDir, in float roughness)
{
    float nDotV = clamp(dot(N, viewDir), 0.0, 1.0);
    const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
    const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
    vec4 r = roughness * c0 + c1;
    float a004 = min(r.x * r.x, exp2(-9.28 * nDotV)) * r.x + r.y;
    return vec2(-1.04, 1.04) * a004 + r.zw;
}

// This method is used by the PrincipledMaterial for the IBL specular term.
vec4 qt_sampleGlossyPrincipled(vec3 normal, vec3 viewDir, vec3 F, float roughness)
{
    if (qt_lightProbeProperties.w < 0.005)
        return vec4(0.0);

    // pre-filtered maps for each roughnesses level are stored in the mips
    // higher than the value of qt_lightProbeOffset.w
    float levels = qt_lightProbeProperties.y - 1.0;

    float lod = clamp((roughness * 5), 0.0, levels);
    vec3 smpDir = normalize(reflect(-viewDir, normal));
#if QSSG_ENABLE_IBL_ORIENTATION
    smpDir = mat3(qt_lightProbeOrientation) * smpDir;
#endif

    vec3 specularSample = qt_textureProbe(qt_lightProbe, smpDir, lod);
    vec2 brdf = qt_brdfApproximation(normal, viewDir, roughness);
    if (qt_lightProbeProperties.z > -1.0) {
        float ctr = 0.5 + 0.5 * qt_lightProbeProperties.z;
        float vertWt = smoothstep(ctr * 0.25, ctr + 0.25, smpDir.y);
        float wtScaled = mix(1.0, vertWt, qt_lightProbeProperties.z + 1.0);
        specularSample *= wtScaled;
    }

    return vec4(qt_lightProbeProperties.w * (specularSample * (F * brdf.x + brdf.y)), 1.0);
}

#endif
