#if defined NORMAL_MAPS
mat3 computeTBNMatrix(mat4 gbufferModelViewInverse, mat3 NormalMatrix, vec4 at_tangent, vec3 Normal)
{

    mat3 tbnMatrix;
    tbnMatrix[0] = mat3(gbufferModelViewInverse) * normalize(NormalMatrix * at_tangent.xyz);
    tbnMatrix[2] = mat3(gbufferModelViewInverse) * (normalize(NormalMatrix * Normal));
    tbnMatrix[1] = cross(tbnMatrix[0], tbnMatrix[2]) * (at_tangent.w < 0.0 ? -1.0 : 1.0);

    return tbnMatrix;
}
#endif
vec4 getMaterialProperties(int materialIndex)
{
    // Reflectance, Smoothness, Subsurface amounts, and Porosity
    switch (materialIndex)
    {
    case 1: // Wood
        return vec4(0.04, 0.25, 0.0, 0.04);
    case 2: // Polished Stone
        return vec4(0.2, 0.7, 0.0, 0.01);
    case 3: // Stone
        return vec4(0.05, 0.4, 0.0, 0.03);
    case 4: // Fabric
        return vec4(0.02, 0.1, 0.25, 0.0);
    case 6: // Terracotta
        return vec4(0.04, 0.35, 0.0, 0.03);
    case 7: // Crystals
        return vec4(0.7, 0.8, 0.5, 0.0);
    case 8: // Water
        return vec4(0.0, 0.0, 0.0, 0.0);
    case 9: // Ice
        return vec4(0.05, 0.7, 0.01, 0.0);
    case 10: // Glass
        return vec4(0.05, 0.7, 0.0, 0.0);
    case 11: // Dirt & Sand
        return vec4(0.04, 0.4, 0.0, 0.25);
    case 12: // Bricks
        return vec4(0.04, 0.3, 0.0, 0.07);
    case 13: // Miscellaneous
        return vec4(0.05, 0.6, 0.0, 0.01);
    case 14: // Iron
        return vec4(0.901961, 0.9, 0.0, 0.0);
    case 15: // Gold
        return vec4(0.905882, 0.9, 0.0, 0.0);
    case 16: // Copper
        return vec4(0.917647, 0.8, 0.0, 0.0);
    case 2003: // Foliage (Leaves)
    case 2013: // Foliage (Modded & Fallback)
        return vec4(0.03, 0.2, 0.5, 0.0);
    case 2001: // Foliage (Tall Flowers)
    case 2002: // Foliage (Tall Flowers)
    case 2004: // Foliage (Tall Flowers)
    case 2005: // Foliage (Other Plants)
    case 2011: // Foliage (Modded & Fallback)
        return vec4(0.03, 0.2, 0.75, 0.0);
    case 10010: // Banners
        return vec4(0.03, 0.2, 0.254, 0.0);
    default:
        return vec4(0.04, 0.3, 0.0, 0.07);
        // Default value if index is out of range
    }
}

float hash(vec2 p)
{
    p = fract(p * vec2(0.3183099, 0.3678794) + vec2(0.71, 0.113));
    return fract(p.x * p.y * (p.x + p.y));
}

float noise(vec2 p)
{
    vec2 i = floor(p);
    vec2 f = fract(p);

    vec2 u = f * f * (3.0 - 2.0 * f);

    return mix(mix(hash(i + vec2(0.0, 0.0)),
                   hash(i + vec2(1.0, 0.0)), u.x),
               mix(hash(i + vec2(0.0, 1.0)),
                   hash(i + vec2(1.0, 1.0)), u.x),
               u.y);
}

vec3 wavingLeaves(vec3 viewPos)
{
    const float k_pi2wtMultiplier = 0.37699111843;
    float pi2wt = k_pi2wtMultiplier * frameTimeCounter;
    float t = 24.0;

#ifndef TIMEFREEZE
    t = pi2wt;
#endif

    float t_scaled = t * 2.2;
    float test = 0.0;

    vec2 viewPos_xz = viewPos.xz;

    test += noise(viewPos_xz * 0.01 + t) * 0.0001;
    test += noise(viewPos_xz * 0.05 + t) * 0.0001;
    test += noise(viewPos_xz * 0.40 + t) * 0.0003;
    test += noise(viewPos_xz * 0.80 + t) * 0.0002;

    return vec3(1.0) * test;
}

bool isFlower(int entityType, bool midc1)
{
    return (entityType == 2001 && midc1) || (entityType == 2011 && midc1) || (entityType == 2004 && midc1);
}

bool isLeaves(int entityType, bool mid2)
{
    return (entityType == 2003 && mid2) || (entityType == 2013 && mid2) || (entityType == 2002 && mid2);
}

vec3 computeWaveOffset(vec3 position, vec3 cameraPosition, vec4 vertex, vec4 MultiTexCoord, mat4 gbufferModelView)
{
    vec3 offsetWorldPos = vertex.xyz + cameraPosition;
    vec3 waveOffset = wavingLeaves(offsetWorldPos) * MultiTexCoord.y;
    vec3 worldPos = offsetWorldPos + waveOffset - cameraPosition;
    return mat3(gbufferModelView) * worldPos + gbufferModelView[3].xyz;
}
vec3 computeWaveOffsetShadow(vec3 position, vec3 cameraPosition, vec4 vertex, vec4 MultiTexCoord, mat4 gbufferModelView)
{
    vec3 offsetWorldPos = vertex.xyz + cameraPosition;
    vec3 waveOffset = wavingLeaves(offsetWorldPos) * MultiTexCoord.y;
    vec3 worldPos = offsetWorldPos + waveOffset - cameraPosition;

    return mat3(shadowModelView) * worldPos + shadowModelView[3].xyz;
}

float computeTranslucent(int entityType)
{
    float translucent = 0.0;
    switch (entityType)
    {
    case 2001:
    case 2002:
    case 2004:
    case 2011:
        translucent = 1.0;
        break;
    case 2003:
    case 2005:
    case 2013:
    case 10010:
        translucent = 0.5;
        break;
    }
    return translucent;
}

