
#pragma BLENDER_REQUIRE(effect_dof_lib.glsl)

uniform vec2 targetTexelSize;
uniform int spritePerRow;
uniform vec2 bokehAnisotropy;

uniform sampler2D colorBuffer;
uniform sampler2D cocBuffer;

/* Scatter pass, calculate a triangle covering the CoC.
 * We render to a half resolution target with double width so we can
 * separate near and far fields. We also generate only one triangle per group of 4 pixels
 * to limit overdraw. */

flat out vec4 color1;
flat out vec4 color2;
flat out vec4 color3;
flat out vec4 color4;
flat out vec4 weights;
flat out vec4 cocs;
flat out vec2 spritepos;
flat out float spritesize;

/* Load 4 Circle of confusion values. texel_co is centered around the 4 taps. */
vec4 fetch_cocs(vec2 texel_co)
{
  /* TODO(@fclem): The `textureGather(sampler, co, comp)` variant isn't here on some
   * implementations.
   */
#if 0  // GPU_ARB_texture_gather
  vec2 uvs = texel_co / vec2(textureSize(cocBuffer, 0));
  /* Reminder: Samples order is CW starting from top left. */
  cocs = textureGather(cocBuffer, uvs, isForegroundPass ? 0 : 1);
#else
  ivec2 texel = ivec2(texel_co - 0.5);
  vec4 cocs;
  cocs.x = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 1)).r;
  cocs.y = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 1)).r;
  cocs.z = texelFetchOffset(cocBuffer, texel, 0, ivec2(1, 0)).r;
  cocs.w = texelFetchOffset(cocBuffer, texel, 0, ivec2(0, 0)).r;
#endif

#ifdef DOF_FOREGROUND_PASS
  cocs *= -1.0;
#endif

  cocs = max(vec4(0.0), cocs);
  /* We are scattering at half resolution, so divide CoC by 2. */
  return cocs * 0.5;
}

void vertex_discard()
{
  /* Don't produce any fragments */
  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}

void main()
{
  ivec2 tex_size = textureSize(cocBuffer, 0);

  int t_id = gl_VertexID / 3; /* Triangle Id */

  /* Some math to get the target pixel. */
  ivec2 texelco = ivec2(t_id % spritePerRow, t_id / spritePerRow) * 2;

  /* Center sprite around the 4 texture taps. */
  spritepos = vec2(texelco) + 1.0;

  cocs = fetch_cocs(spritepos);

  /* Early out from local CoC radius. */
  if (all(lessThan(cocs, vec4(0.5)))) {
    vertex_discard();
    return;
  }

  vec2 input_texel_size = 1.0 / vec2(tex_size);
  vec2 quad_center = spritepos * input_texel_size;
  vec4 colors[4];
  bool no_color = true;
  for (int i = 0; i < 4; i++) {
    vec2 sample_uv = quad_center + quad_offsets[i] * input_texel_size;

    colors[i] = dof_load_scatter_color(colorBuffer, sample_uv, 0.0);
    no_color = no_color && all(equal(colors[i].rgb, vec3(0.0)));
  }

  /* Early out from no color to scatter. */
  if (no_color) {
    vertex_discard();
    return;
  }

  weights = dof_layer_weight(cocs) * dof_sample_weight(cocs);
  /* Filter NaNs. */
  weights = select(weights, vec4(0.0), equal(cocs, vec4(0.0)));

  color1 = colors[0] * weights[0];
  color2 = colors[1] * weights[1];
  color3 = colors[2] * weights[2];
  color4 = colors[3] * weights[3];

  /* Extend to cover at least the unit circle */
  const float extend = (cos(M_PI_4) + 1.0) * 2.0;
  /* Crappy diagram
   * ex 1
   *    | \
   *    |   \
   *  1 |     \
   *    |       \
   *    |         \
   *  0 |     x     \
   *    |   Circle    \
   *    |   Origin      \
   * -1 0 --------------- 2
   *   -1     0     1     ex
   */

  /* Generate Triangle : less memory fetches from a VBO */
  int v_id = gl_VertexID % 3;                     /* Vertex Id */
  gl_Position.x = float(v_id / 2) * extend - 1.0; /* int divisor round down */
  gl_Position.y = float(v_id % 2) * extend - 1.0;
  gl_Position.z = 0.0;
  gl_Position.w = 1.0;

  spritesize = max_v4(cocs);

  /* Add 2.5 to max_coc because the max_coc may not be centered on the sprite origin
   * and because we smooth the bokeh shape a bit in the pixel shader. */
  gl_Position.xy *= spritesize * bokehAnisotropy + 2.5;
  /* Position the sprite. */
  gl_Position.xy += spritepos;
  /* NDC range [-1..1]. */
  gl_Position.xy = gl_Position.xy * targetTexelSize * 2.0 - 1.0;

  /* Add 2.5 for the same reason but without the ratio. */
  spritesize += 2.5;
}
