#if defined LAB_PBR || defined POM
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.05, 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.3, 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);
#ifndef VOXY
    case 9: // Ice
        return vec4(0.05, 0.7, 0.25, 0.0);
#else
    case 9: // Ice
        return vec4(0.05, 0.7, 0.0, 0.0);

#endif
    case 10: // Glass
        return vec4(0.05, 0.7, 0.0, 0.0);
    case 11: // Dirt & Grass
        return vec4(0.05, 0.2, 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.25, 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.03, 0.1, 0.0, 0.07);
        // Default value if index is out of range
    }
}

float Noise3D(in vec3 p)
{
    const vec3 s = vec3(7.0, 157.0, 113.0);
    vec3 ip = floor(p); // Unique unit cell ID.
    vec4 h = vec4(0.0, s.yz, s.y + s.z) + dot(ip, s);
    vec3 fp = fract(p); // Cell's fractional component.
    fp = fp * fp * (3.0 - 2.0 * fp);
    h = mix(fract(sin(h) * 43758.5453), fract(sin(h + s.x) * 43758.5453), fp.x);
    h.xy = mix(h.xz, h.yw, fp.y);
    return mix(h.x, h.y, fp.z); // Range: [0, 1].
}

// Wind function to output wind direction and strength
vec3 Wind(vec3 p)
{
    // Generate directional noise components with a higher frequency for smoother transitions
    float noiseX = Noise3D(p + vec3(1.0, 0.0, 0.0));
    float noiseY = Noise3D(p + vec3(0.0, 1.0, 0.0));
    float noiseZ = Noise3D(p + vec3(0.0, 0.0, 1.0));

    // Combine noise components into a direction vector
    vec3 direction = normalize(vec3(noiseX, noiseY, noiseZ));

    // Calculate wind strength based on Noise3D with a different scale for variation
    float strength = Noise3D(p * 0.3) * 0.5 + 0.5; // Normalize strength to [0, 1]

    // Return wind vector with direction and strength
    return direction * strength * 2.0 - 1.0;
}

vec3 wavingLeaves(vec3 viewPos)
{
    viewPos *= 0.075;
    float pi2wt = 0.5 * frameTimeCounter;
    float t = 24.0;

#ifndef TIMEFREEZE
    t = pi2wt;
#endif
    vec3 wind = Wind(viewPos + t);
    return wind * 0.25 * 0.5;
}

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 / 255.0);
    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 / 255.0);
    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;
}
