// -*- c++ -*-
/**
  @file ShadowMappedLightPass.pix
  @author Morgan McGuire morgan@cs.williams.edu

  For use with G3D::SuperShader.
  @edited 2009-07-30
 */

#include "SS_Globals.pix"
#include "SS_Light.pix"
#include "SS_BumpMap.pix"

#ifdef G3D_ATI
   // On ATI cards, use regular 2D texturing since
   // depth textures provide no advantage.
   uniform sampler2D shadowMap;
#else
   uniform sampler2DShadow shadowMap;
#endif

/** Coordinate to use in the shadow map */
varying vec4        shadowCoord;

void main(void) {

#include "SS_LambertianComponent.pix"
    gl_FragColor.a = 
#       if defined(LAMBERTIANMAP) || defined(LAMBERTIANCONSTANT)
            lambertianColor.a
#       else
            1.0
#       endif
        ;

    float shadow = 0.0;
    
    // Test whether we are within a spotlight's cone. 
    vec3 wsL = normalize(lightPosition0.xyz - wsPosition.xyz * lightPosition0.w);
    if (-dot(lightDirection0, wsL) >= lightAttenuation0.w) {

        // Compute projected shadow coord.
        vec4 projShadowCoord = shadowCoord / shadowCoord.w;

        // "side" and "diagonal" offset variables used to produce vectors to the 
        // 8-neighbors, which leads to a smoother shadow result (0.71 is sqrt(2)/2).
        vec4 s = vec4(g3d_sampler2DInvSize(shadowMap), -g3d_sampler2DInvSize(shadowMap).x, 0.0);
        vec4 d = s * 0.71;

#       ifdef G3D_ATI
            // On ATI, percentage closer filtering is not available.
            // Perform nearest neighbor sampling with explicit depth
            // comparisons.  We use dot products to perform four
            // compares at once and sum their results; this saves four
            // instructions.  We could also (but do not in this
            // implementation) perform our own bilinear to smooth as
            // well as blur the results.

            shadow = 
                (float(texture2D(shadowMap, projShadowCoord.xy).r > projShadowCoord.z) +
                  
                  dot(vec4(1,1,1,1),
                      vec4(greaterThan(vec4(
                        texture2D(shadowMap, projShadowCoord.xy + s.xw).r,
                        texture2D(shadowMap, projShadowCoord.xy - s.xw).r,
                        texture2D(shadowMap, projShadowCoord.xy + s.wy).r,
                        texture2D(shadowMap, projShadowCoord.xy - s.wy).r), projShadowCoord.zzzz))) +
                  
                  dot(vec4(1,1,1,1),
                      vec4(greaterThan(vec4(
                        texture2D(shadowMap, projShadowCoord.xy + s.xy).r,
                        texture2D(shadowMap, projShadowCoord.xy - s.xy).r,
                        texture2D(shadowMap, projShadowCoord.xy + s.zy).r,
                        texture2D(shadowMap, projShadowCoord.xy - s.zy).r), projShadowCoord.zzzz)))) / 9.0; 
        
#       else
            shadow = 
                (shadow2D(shadowMap, projShadowCoord.xyz).r +
                 
                 shadow2D(shadowMap, projShadowCoord.xyz + s.xww).r +
                 shadow2D(shadowMap, projShadowCoord.xyz - s.xww).r +
                 shadow2D(shadowMap, projShadowCoord.xyz + s.wyw).r +
                 shadow2D(shadowMap, projShadowCoord.xyz - s.wyw).r +
	         
                 shadow2D(shadowMap, projShadowCoord.xyz + d.xyw).r +
                 shadow2D(shadowMap, projShadowCoord.xyz - d.zyw).r +
                 shadow2D(shadowMap, projShadowCoord.xyz + d.zyw).r +
                 shadow2D(shadowMap, projShadowCoord.xyz - d.xyw).r) / 9.0;
#       endif

        // Clamp to zero beyond border.  The texture clamping mode is supposed
        // to do this but is not working.  We clamp 1 pixel beyond in order to
        // get correct blended values near the border.
        shadow *= 
            float(all(bvec4(greaterThanEqual(projShadowCoord.xy, -s.xy),
                            lessThanEqual(projShadowCoord.xy, vec2(1.0, 1.0) + s.xy))));

    }

    if (shadow == 0.0) {
        discard;
    }

#include "SS_Components.pix"

    float shininess = 0.0;

#   if defined(SPECULARCONSTANT) || defined(SPECULARMAP)
        vec3 F0;
        vec3 specularColor;
        float cos_i = dot(wsN, wsE);
        {        
            vec4 temp =
#           ifdef SPECULARCONSTANT
                specularConstant
#	            ifdef SPECULARMAP
                    * texture2D(specularMap, offsetTexCoord)
#	            endif
#	        else
                texture2D(specularMap, offsetTexCoord)
#           endif
            ;
    				   
	       F0 = temp.rgb;
	       specularColor = computeF(F0, max(0.0, cos_i));
           shininess = unpackSpecularExponent(temp.a);
        }
#       if defined(LAMBERTIANCONSTANT) || defined(LAMBERTIANMAP)
           // modulate the lambertian color by the specular; this
           // ensures energy conservation at glancing angles under Fresnel effects.
           lambertianColor.rgb *= vec3(1.0) - specularColor;
#       endif
#   else
       shininess = 0.0;
#   endif

    vec3 I_lambertian = BLACK;
    vec3 I_specular = BLACK;

    addLightContribution(wsN, wsE, wsPosition, shininess, lightPosition0, lightAttenuation0,
                         lightDirection0, lightColor0, I_lambertian, I_specular, wsL);
        
    gl_FragColor.rgb =
        shadow * (BLACK
#       if defined(LAMBERTIANCONSTANT) || defined(LAMBERTIANMAP)
           + I_lambertian * lambertianColor.rgb 
#       endif

#       if defined(SPECULARCONSTANT) || defined(SPECULARMAP)
           + I_specular * specularColor.rgb
#       endif
        );
}
