/* Refrax — program/solid.fsh.glsl */

#include "/lib/settings.glsl"
#include "/lib/common.glsl"
#include "/lib/blockid.glsl"
#include "/lib/labpbr.glsl"
#ifdef BLOCK_ENTITY
#include "/lib/endportal.glsl"
#endif

uniform sampler2D gtexture;
uniform sampler2D normals;
uniform sampler2D specular;
uniform float alphaTestRef;
#ifdef POM
uniform mat4 gbufferModelViewInverse;
uniform vec3 shadowLightPosition;
#endif
#ifdef ENTITY
uniform vec4 entityColor;
uniform int entityId;
#endif
#ifdef BLOCK_ENTITY
uniform int blockEntityId;
uniform vec3 cameraPosition;
uniform float frameTimeCounter;
#endif

in vec2 uv;
in vec2 lmcoord;
in vec4 vcolor;
in vec3 normalW;
in vec3 tangentW;
in float tangentSign;
in vec3 scenePos;
flat in int blockId;
flat in vec2 tileBase;
flat in vec2 tileSize;
#ifdef TERRAIN
flat in vec2 tileAlphaRange;
#endif

#ifdef HAND
/* RENDERTARGETS: 1,2,3,10,0 */
#else
/* RENDERTARGETS: 1,2,3,10 */
#endif
layout(location = 0) out vec4 outAlbedo;
layout(location = 1) out vec4 outNormal;
layout(location = 2) out vec4 outMaterial;
layout(location = 3) out vec4 outExtra;
#ifdef HAND
layout(location = 4) out vec4 outForwardClear;
#endif

void main() {
#ifdef HAND
    outForwardClear = vec4(0.0, 0.0, 0.0, 1.0);
#endif
    vec2 uvDx = dFdx(uv);
    vec2 uvDy = dFdy(uv);
    vec2 texcoord = uv;
    vec3 N = normalize(normalW);

#ifdef BLOCK_ENTITY
    if (blockEntityId == 10030) {
#if MC_VERSION < 12100
        N = normalize(cross(dFdx(scenePos), dFdy(scenePos)));
        if (dot(N, scenePos) > 0.0) N = -N;
#endif
        vec3 portal = endPortalColor(scenePos, cameraPosition, N, frameTimeCounter);
        outAlbedo = vec4(linearToSrgb(portal), 1.0);
        outNormal = packSurfaceData(N, 0.55, SURF_STATIC);
        outMaterial = vec4(lmcoord, 1.0, 0.0);
        outExtra = vec4(0.0);
        return;
    }
#endif

    float alphaRef = alphaTestRef;
#if defined ENTITY || defined HAND || defined EMISSIVE_FULL || defined BLOCK_ENTITY
    alphaRef = max(alphaRef, 0.5);
#endif
    float baseAlpha = textureGrad(gtexture, uv, uvDx, uvDy).a * vcolor.a;
    bool terrainFoliage = false;
    bool alphaCutoutTile = false;
#ifdef TERRAIN
    float tileMinAlpha = tileAlphaRange.x * vcolor.a;
    float tileMaxAlpha = tileAlphaRange.y * vcolor.a;
    alphaCutoutTile = tileMinAlpha < 0.05 && tileMaxAlpha > 0.95;
    terrainFoliage = isFoliage(blockId) || alphaCutoutTile;
    if (alphaCutoutTile) alphaRef = max(alphaRef, 0.5);
#endif
    if (baseAlpha < alphaRef) discard;

    float texAO = 1.0;
    float pomShadow = 1.0;
    Material mat;
    mat.roughness = 0.9;
    mat.f0 = 0.04;
    mat.emission = 0.0;
    mat.sss = 0.0;
    mat.porosity = 0.0;
    mat.anisotropy = 0.0;
    mat.clearcoat = 0.0;
    mat.thinFilm = 0.0;

#ifdef PBR_MATERIALS
  #ifdef TERRAIN
    bool foliage = terrainFoliage;
  #else
    const bool foliage = false;
  #endif
    if (!foliage && dot(tangentW, tangentW) > 1e-6) {
        mat3 TBN = makeTBN(N, tangentW, tangentSign);
      #ifdef POM
        vec3 viewDirT = normalize(transpose(TBN) * -normalize(scenePos));
        vec3 lightDirW = normalize(mat3(gbufferModelViewInverse) * shadowLightPosition);
        vec3 lightDirT = normalize(transpose(TBN) * lightDirW);
        float pomHeight;
        vec3 pomSlopeN;
        float pomSlopeW;
        float pomFade = smoothstep(64.0, 96.0, length(scenePos));
        texcoord = pomOffset(normals, texcoord, tileBase, tileSize, viewDirT, uvDx, uvDy, pomFade, pomHeight, pomSlopeN, pomSlopeW);
        if (lmcoord.y > 0.03)
            pomShadow = pomDirectShadow(normals, texcoord, tileBase, tileSize, lightDirT, uvDx, uvDy, pomHeight, pomFade);
      #if POM_DEBUG > 0
        {
            float span = pomPixelSpan(normals, tileSize, viewDirT, uvDx, uvDy, pomFade);
            float keep = pomSpanKeep(span);
        #if POM_DEBUG == 1
            float t = float(pomLayerCount(span * keep)) / float(POM_SAMPLES * 3);
            outAlbedo = vec4(t, 1.0 - t, keep <= 0.0 ? 1.0 : 0.0, 1.0);
        #else
            outAlbedo = vec4(keep <= 0.0 ? vec3(1.0, 0.0, 0.0) : mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), keep), 1.0);
        #endif
            outNormal = packSurfaceData(normalW, 1.0, SURF_STATIC);
            outMaterial = vec4(lmcoord, 1.0, 0.0);
            outExtra = vec4(0.0);
            return;
        }
      #endif
      #endif
        vec4 nTex = textureGrad(normals, texcoord, uvDx, uvDy);
        if (nTex.r + nTex.g > 0.0005) {
            vec3 tanN = filteredNormalTex(nTex, uvDx, uvDy, vec2(textureSize(normals, 0)));
      #ifdef POM
            if (pomSlopeW > 0.0) tanN = normalize(mix(tanN, pomSlopeN, pomSlopeW));
      #endif
            N = normalize(TBN * tanN);
            texAO = decodeTexAO(nTex);
        }
    }
    if (!foliage) mat = decodeSpecular(textureGrad(specular, texcoord, uvDx, uvDy));
#endif

    vec4 albedo = textureGrad(gtexture, texcoord, uvDx, uvDy) * vcolor;
#ifdef ENTITY
    albedo.rgb = mix(albedo.rgb, entityColor.rgb, entityColor.a);
#endif
    if (albedo.a < alphaRef) discard;
    albedo.rgb *= mix(texAO, texAO * texAO, PBR_AO_DEPTH);

    float emission = mat.emission;
#ifdef EMISSIVE_FULL
    emission = 1.0;
#endif
#ifdef ENTITY
    float entityGlow = entityEmissionStrength(entityId);
    if (entityGlow > 0.0) {
        float glow = smoothstep(0.18, 0.75, luminance(albedo.rgb));
        emission = max(emission, glow * entityGlow);
    }
  #ifdef ENTITY_GLOWING
    emission = max(emission, 0.30);
  #endif
#endif
#ifdef TERRAIN
    if (terrainFoliage) {
        mat.roughness = 1.0;
        mat.f0 = MATTE_FOLIAGE_F0;
    }
    if (isFoliage(blockId) && mat.sss <= 0.0) mat.sss = FOLIAGE_SSS;
    if (emission <= 0.0 && isEmitter(blockId))
        emission = emitterEmission(blockId, luminance(albedo.rgb));
#ifdef EMISSIVE_ORES
    if (emission <= 0.0 && isEmissiveOre(blockId))
        emission = oreEmission(albedo.rgb);
#endif
#ifdef EMISSIVE_FOLIAGE
    if (emission <= 0.0 && isEmissiveFoliage(blockId))
        emission = foliageEmission(albedo.rgb);
#endif
    if (emission <= 0.0 && blockId == 0)
        emission = inferredEmission(albedo.rgb, lmcoord.x);
    applyFallbackMaterial(blockId, mat);
#endif

#ifdef DEBUG_PBR
#ifdef PBR_MATERIALS
    vec4 dbgN = textureGrad(normals, texcoord, uvDx, uvDy);
    vec4 dbgS = textureGrad(specular, texcoord, uvDx, uvDy);
    outAlbedo = vec4(fract(scenePos.x / 8.0) < 0.5 ? dbgN.rgb : dbgS.rgb, 1.0);
    outNormal = packSurfaceData(normalW, 1.0, SURF_STATIC);
    outMaterial = vec4(lmcoord, 1.0, 0.0);
    outExtra = vec4(mat.sss, mat.porosity, 0.0, 0.0);
    return;
#endif
#endif

#ifdef ENTITY
    const float dynamicSurface = 1.0;
#else
    const float dynamicSurface = 0.0;
#endif

    outAlbedo = vec4(albedo.rgb, pomShadow);
    outNormal = packSurfaceData(N, emission, dynamicSurface);
    outMaterial = vec4(lmcoord, mat.roughness, mat.f0);
    outExtra = vec4(mat.sss, mat.porosity, mat.anisotropy, packCoating(mat.clearcoat, mat.thinFilm));
}
