precision highp float;
precision highp int;

uniform vec3 highlightColors[3];

uniform mat4 worldViewProjection_custom;
uniform vec3 cameraPositionW;
uniform vec3 groundColor;
uniform vec4 materialDiffuseColor;
uniform vec3 ambientColor;
uniform vec3 hemisphericLightColor;
uniform float hemisphericLightStrength;
uniform vec3 hemisphericLightWorldSpace;
uniform vec3 directionalLightColor;
uniform vec3 directionalLightWorldSpaceDirection;
uniform float directionalLightStrength;
uniform sampler2D diffuseSampler;
uniform sampler2D propertiesSampler;
uniform float packSize;
uniform mat4 worldMatrix;
uniform vec3 size;

uniform bool isPerspective;
uniform float farPlane;
uniform float nearPlane;
uniform float orthoSize;
uniform float ditherDistanceFade;

uniform float directionalLightLambertStrength;
uniform float directionalLightSpecularStrength;

// #typescript options?.clipPlaneUniforms()

in float vColorPixelIndex;
in vec3 vPositionW;

// #include DitherUniforms.glsl

vec3 computeHemisphericLightingDiffuseTerm(vec3 normalW, vec3 hemisphericLightDirectionW, vec3 diffuseColor, vec3 groundColor) {
	float ndl = dot(normalW, hemisphericLightDirectionW) * 0.5 + 0.5;
	vec3 diffuse = mix(groundColor, diffuseColor, ndl);
	
	return diffuse;
}

vec3 computeDirectionalLight(vec3 worldE, vec3 worldN) {
    vec3 normalReflectedLightDirection = reflect(normalize(directionalLightWorldSpaceDirection), worldN);

    // float shininess = 128.0; // Hardcoded to 128, since we don't have shininess in the properties texture.
    // float specular = pow(max(0.0, dot(normalReflectedLightDirection, worldE)), shininess) * directionalLightSpecularStrength;
    // float specular = specRaw * directionalLightSpecularStrength;
    // pow is expensive so allegedly hardcoding multiplication is faster. Since we know its 128 we can hardcode it.
    float specular = max(0.0, dot(normalReflectedLightDirection, worldE));
    specular = specular * specular;
    specular = specular * specular;
    specular = specular * specular;
    specular = specular * specular;
    specular = specular * specular;
    specular = specular * specular;
    specular = specular * specular * directionalLightSpecularStrength;

    float diffuse = max(dot(worldN, -normalize(directionalLightWorldSpaceDirection)), 0.0) * directionalLightLambertStrength;
    return max(directionalLightColor * (diffuse + specular), vec3(0.0));
}

// #include HSVMapping.glsl

// #include LinearizeDepth.glsl

void main(void) {    

    // NOTE: Normal calculation MUST happen BEFORE any discard operations (like dithering) OR if branches
    // because dFdx/dFdy derivatives become undefined when neighboring fragments are discarded.
    // This causes broken normals on some drivers (especially Linux).
    vec3 fdx = dFdx(vPositionW);
    vec3 fdy = dFdy(vPositionW);


    

    // used by ColorPickingIndex.glsl below
    ivec2 textureSize = textureSize(diffuseSampler, 0);

    // Calculates pixelIndex used by PropertiesPixel.glsl and to get fragment color further down
    // #include ColorPickingIndex.glsl

    // This include sets (among other things) the visible variable to true or false.
    // #include PropertiesPixel.glsl

    if (!visible) {
        // Good for debugging if a texture fails for some reason
        // if (int(colorPixelIndex) % textureSize.x == 0 && diffuseColor == vec4(0, 0, 0, 0)) {
        //     gl_FragColor = vec4(1, 0, 0, 1);
        //     return;
        // }
        discard;
    }


    


    
    vec4 diffuseColor = texelFetch(diffuseSampler, ivec2(pixelIndex), 0);
    
    if (diffuseColor.a == 0.0) {
        // Good for debugging if a texture fails for some reason
        // if (int(colorPixelIndex) % textureSize.x == 0 && diffuseColor == vec4(0, 0, 0, 0)) {
        //     gl_FragColor = vec4(1, 0, 0, 1);
        //     return;
        // }
        discard;
    }

    
    vec3 worldSpaceVertex = vPositionW + cameraPositionW;

    

    // #typescript options?.clipPlaneDiscarding()

    vec3 worldE = normalize(cameraPositionW - vPositionW);
    vec3 normal = normalize(cross(fdy, fdx));
    vec3 hemisphericLighting = computeHemisphericLightingDiffuseTerm(normal, hemisphericLightWorldSpace, hemisphericLightColor, groundColor);
    vec3 directionalLighting = computeDirectionalLight(worldE, normal);

    vec3 finalDiffuse = clamp((directionalLighting * directionalLightStrength + hemisphericLighting * hemisphericLightStrength) * materialDiffuseColor.rgb + ambientColor, 0.0, 1.0) * diffuseColor.rgb;
    float opaqueness = 0.0;
    #ifdef dithering
        opaqueness = min(diffuseColor.a, 1.0);
        float distanceImpact = 0.25;
        // #include DitherDiscard.glsl
    #endif
    
    // #include HSVColorChanger.glsl



    if (highlighted) {
        finalDiffuse = highlightColors[highlightIndex - 1];
    }
    
    
    // #typescript options?.clipPlaneColorBlending()
    
    gl_FragColor = vec4(finalDiffuse, opaqueness);
}