//=========================================================================
//
//  Program:   Visualization Toolkit
//  Module:    vtkLightingHelper_s.glsl
//
//  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
//  All rights reserved.
//  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
//
//     This software is distributed WITHOUT ANY WARRANTY; without even
//     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//     PURPOSE.  See the above copyright notice for more information.
//
//=========================================================================

#version 110

// Filename: vtkLightingHelper_s.glsl
// Filename is useful when using gldb-gui

// This file defines some lighting functions.
// They can be used either in a vertex or fragment shader.

// It is intented to be used in conjunction with vtkLightsSwitches class.

// Those functions expect uniform variables about the status of lights
// 1. In fixed-mode pipeline (glUseProgram(0)),
// 2. get the values with GLboolean lightSwitch[i]=glIsEnabled(GL_LIGHTi);
// 3. Switch to programmable pipeline (glUseProgram(prog))
// 4. Send boolean as uniform: var=glGetUniformLocation(prog,"lightSwitch[i]");
// 5. glUniform1i(var,lightSwitch[i]);

// vtkLightsSwitches class can do that for you.


// Example in vertex shader:
// Reminder: two-sided/one-sided is controlled by GL_VERTEX_PROGRAM_TWO_SIDE
//
// vec4 eyeCoords=gl_ModelViewMatrix*gl_Vertex;
// vec4 n=gl_Normalmatrix*gl_Normal;
// n=normalize(n);
// separateSpecularColor(gl_FrontMaterial,eyeCoords,n,gl_FrontColor,gl_FrontSecondaryColor);
 // If two-sided.
// separateSpecularColor(gl_BackMaterial,eyeCoords,n,gl_BackColor,gl_BackSecondaryColor);

 // Typical:
// gl_FrontColor=singleColor(gl_FrontMaterial,eyeCoords,n);

// This is convenience method to use in shader but you better do
// this computation on the CPU and send the result as a uniform.

// True if any enabled light is a positional one.
bool needSurfacePositionInEyeCoordinates()
{
  bool result=false;
  for (int i=0; !result && (i < gl_MaxLights); i++)
    {
    result = (gl_LightSource[i].diffuse.w != 0.0) && 
      (gl_LightSource[i].position.w != 0.0);
    }

  return result;
}

// Lighting computation based on a material m,
// a position on the surface expressed in eye coordinate (typically a vertex
//  position in a vertex shader, something interpolated in a fragment shader),
// a unit normal `n' to the surface in eye coordinates.
// Most of the components are in cpri (primary color), the specular
// component is in csec (secondary color).
// Useful for blending color and textures.
void separateSpecularColor(gl_MaterialParameters m,
                           vec3 surfacePosEyeCoords,
                           vec3 n,
                           out vec4 cpri,
                           out vec4 csec)
{
  cpri = m.emission + m.ambient * gl_LightModel.ambient; // ecm+acm*acs
  csec = vec4(0.0,0.0,0.0,1.0);
  vec3 wReverseRayDir = surfacePosEyeCoords;

  // For each light,
  for (int i=0; i < gl_MaxLights; i++)
    {
    // Trick.
    bool lightEnabled = (gl_LightSource[i].diffuse.w != 0.0);
    if (!lightEnabled)
      {
      continue;
      }
    
    vec3 ldir;
    vec3 h;
    float att;
    float spot;
    float shininessFactor;

    if (gl_LightSource[i].position.w != 0.0)
      {
      // ldir=light direction
      vec3 lightPos=gl_LightSource[i].position.xyz/
        gl_LightSource[i].position.w;
      ldir = lightPos - surfacePosEyeCoords;
      float sqrDistance = dot(ldir,ldir);
      ldir = normalize(ldir);
      h = normalize(ldir + wReverseRayDir);
      att = 1.0 / (gl_LightSource[i].constantAttenuation + gl_LightSource[i].linearAttenuation * 
        sqrt(sqrDistance) + gl_LightSource[i].quadraticAttenuation * sqrDistance);
      }
    else
      {
      att = 1.0;
      ldir = gl_LightSource[i].position.xyz;
      ldir = normalize(ldir);
      h = normalize(ldir + wReverseRayDir);
      }

    if (att>0.0)
      {
      if (gl_LightSource[i].spotCutoff == 180.0)
        {
        spot = 1.0;
        }
      else
        {
        float coef=-dot(ldir,gl_LightSource[i].spotDirection);
        if (coef>=gl_LightSource[i].spotCosCutoff)
          {
          spot=pow(coef,gl_LightSource[i].spotExponent);
          }
        else
          {
          spot=0.0;
          }
        }
      if (spot>0.0)
        {
        // LIT operation...
        float nDotL=dot(n,ldir);
        float nDotH=dot(n,h);

        // separate nDotL and nDotH for two-sided shading, otherwise we
        // get black spots.

        if (nDotL<0.0) // two-sided shading
          {
          nDotL=-nDotL;
          }

        if (nDotH<0.0) // two-sided shading
          {
          nDotH=-nDotH;
          }
        // ambient term for this light
        vec4 cpril=m.ambient*gl_LightSource[i].ambient;// acm*adi

        // diffuse term for this light
        if (nDotL>0.0)
          {
          cpril+=m.diffuse*gl_LightSource[i].diffuse*nDotL; // dcm*dcli
          }

        // specular term for this light
        shininessFactor=pow(nDotH,m.shininess); // srm

        cpri+=att*spot*cpril;

        // scm*scli
        csec+=att*spot*
          m.specular*gl_LightSource[i].specular*shininessFactor;

        }
      }
    }
}


// Lighting computation based on a material m,
// a position on the surface expressed in eye coordinate (typically a vertex
//  position in a vertex shader, something interpolated in a fragment shader),
// a unit normal to the surface in eye coordinates.
// The result includes the specular component.
vec4 singleColor(gl_MaterialParameters m,
                 vec3 surfacePosEyeCoords,
                 vec3 n)
{
 vec4 cpri;
 vec4 csec;
 separateSpecularColor(m,surfacePosEyeCoords,n,cpri,csec);
 return cpri+csec;
}
