
uniform bool isPicking;

#ifdef ANAGLYPH
uniform mat3 matL;
uniform float gamma;
#endif

#ifdef NO_ORDER_TRANSP

#ifdef ONE_DRAW_BUFFER
uniform float which_pass;  // 0. - first, 1. - second
#else
#ifdef PURE_OPENGL_ES_2
#extension GL_EXT_draw_buffers : require
#endif
#endif

#define gl_FragColor gl_FragData[0]

float get_weight(float depth, float alpha){
      return alpha * max(1e-2, 3e3 * pow(1. - depth, 3.));  // eq #10
}

/*
 * Set data for order independant transparency
 *
 * With multiple buffers:
 * gl_FragData[0]: weighted color
 * gl_FragData[1]: weight
 *
 * With one buffer and multi-pass:
 * gl_FragData[0]: weighted color in pass 1
 * gl_FragData[0]: weight         in pass 2
 *
 */
void setFragDataForNoOrderTransp(float depth) {
  float wi = get_weight(depth, gl_FragColor.a);

  gl_FragData[0] = vec4(gl_FragColor.rgb * wi, gl_FragColor.a);

#ifdef ONE_DRAW_BUFFER
  // for using one draw buffer, switch on which_pass between color and weights
  gl_FragData[0] = mix(gl_FragData[0], vec4(wi, wi, 0., 1.), which_pass);
#else
#ifdef ANAGLYPH
  // need to set two components because anaglyph masks out colors for each eye
  gl_FragData[1].rg = vec2(wi);
#else
  gl_FragData[1].r = wi;
#endif // ANAGLYPH
#endif // ONE_DRAW_BUFFER
}

#endif // NO_ORDER_TRANSP

/*
 * Apply post-lighting effects directly to output variables like gl_FragColor
 */
void PostLightingEffectsNoOIT() {
  if (isPicking)
    return;
#ifdef ray_trace_mode_3
  // cel shading
  gl_FragColor.rgb = floor(gl_FragColor.rgb * 3.999) / 3.0;
#endif

#ifdef ANAGLYPH
  gl_FragColor.rgb = matL * pow(gl_FragColor.rgb, vec3(gamma));
#endif
}

vec3 ComputeChromadepth(float depth);

/*
 * ... including OIT
 */
void PostLightingEffects(float depth) {
  if (isPicking)
    return;
  PostLightingEffectsNoOIT();

#ifdef chromadepth_postlighting
  vec3 luma_coeff = vec3(.30, .59, .11);
  gl_FragColor.rgb = ComputeChromadepth(depth) * dot(luma_coeff, gl_FragColor.rgb);
#endif

#ifdef transparency_mode_3
#ifndef NO_ORDER_TRANSP
  // if transparent, discard in opaque pass
  if (gl_FragColor.a < .95)
    discard;
#else
  // if almost opaque, discard in transparent pass
  if (gl_FragColor.a > .95)
    discard;

  setFragDataForNoOrderTransp(depth);
#endif // NO_ORDER_TRANSP
#endif // transparency_mode_3
}

/*
 * Default argument overload
 */
void PostLightingEffects() {
  PostLightingEffects(gl_FragCoord.z);
}

#ifdef chromadepth
#ifndef ortho
/*
 * "unproject" the depth value if not in orthoscopic view, to get
 * a depth gradient which is linear in eye space.
 */
uniform vec2 clippingplanes;
float orthoDepth(float depth) {
  float front = clippingplanes.x;
  float back = clippingplanes.y;
  float front_inv = 1. / front;
  float back_inv  = 1. / back;

  // from proj normalized dev coords to eye space
  float depth_eye = 1. / (depth * (back_inv - front_inv) + front_inv);

  // from eye space to ortho normalized dev coords
  return (depth_eye - front) / (back - front);
}
#endif

/*
 * Chromadepth - Calculates a color by depth.
 * Uses a rainbow from red (near) to blue (far).
 *
 * http://chromatek.com/art-and-design/design-guide/
 */
vec3 ComputeChromadepth(float depth) {
#ifndef ortho
  depth = orthoDepth(depth);
#endif

  const float margin = 1. / 4.;
  float hue6 = 6. * 240. / 360. *
    clamp(depth * (1. + 2. * margin) - margin, 0., 1.);

  return clamp(vec3(
        abs(hue6 - 3.) - 1.,
        2. - abs(hue6 - 2.),
        2. - abs(hue6 - 4.)),
      0., 1.);
}
#endif

/*
 * Applies pre-lighting and pre-fog color alteration effects.
 */
vec4 ApplyColorEffects(vec4 color, float depth) {
#ifdef chromadepth
#ifndef chromadepth_postlighting
  if (!isPicking)
    color.rgb = ComputeChromadepth(depth);
#endif
#endif
  return color;
}
