-- $Id: ShockWave.lua 3171 2008-11-06 09:06:29Z det $
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

local ShockWave = {}
ShockWave.__index = ShockWave

local warpShader
local screenLoc
local dlist

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function ShockWave.GetInfo()
  return {
    name      = "ShockWave",
    backup    = "", --// backup class, if this class doesn't work (old cards,ati's,etc.)
    desc      = "",

    layer     = 1, --// extreme simply z-ordering :x

    --// gfx requirement
    fbo       = true,
    shader    = true,
    distortion= true,
    atiseries = 2,
    intel     = 0,
  }
end

ShockWave.Default = {
  layer = 1,
  worldspace = true,

  life   = 23,
  pos    = {0,0,0},
  growth = 4.5,

  repeatEffect = false,
  dieGameFrame = math.huge
}

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

local glUniform   = gl.Uniform
local glUseShader = gl.UseShader
local glCallList  = gl.CallList
local glMultiTexCoord = gl.MultiTexCoord
local spWorldToScreenCoords = Spring.WorldToScreenCoords

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function ShockWave:BeginDrawDistortion()
  glUseShader(warpShader)
  --glUniform(screenLoc, 1/vsx,1/vsy )
end

function ShockWave:EndDrawDistortion()
  glUseShader(0)
end


function ShockWave:DrawDistortion()
  local pos   = self.pos
  local x,y,z = pos[1],pos[2],pos[3]
  local cx,cy = spWorldToScreenCoords(x,y,z)

  glMultiTexCoord(0,x,y,z,1)
  glMultiTexCoord(1,cx,cy,self.radius,self.life2)

  glCallList(dlist)
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function ShockWave.Initialize()
  warpShader = gl.CreateShader({
    vertex = [[
      uniform float radius;

	varying vec2 texCoordFoo;
      varying vec2  center;
      varying float life;

      void main()
      {
         center = gl_MultiTexCoord1.xy;
         life   = gl_MultiTexCoord1.w;

         gl_Position     = gl_ModelViewMatrix * gl_MultiTexCoord0;
         gl_Position.xy += gl_Vertex.xy * gl_MultiTexCoord1.z;
         gl_Position     = gl_ProjectionMatrix * gl_Position;
         texCoordFoo.st = gl_Vertex.st;
      }
    ]],
    fragment = [[
      uniform vec2  screenInverse;

varying vec2 texCoordFoo;
      varying vec2  center;
      varying float life;

      float p1 = gl_ProjectionMatrix[2][2];
      float p2 = gl_ProjectionMatrix[2][3];

      float ConvertZtoEye(float z)
      {
          return p2/(z*2.0-1.0+p1);
      }

      float ConvertEyeToZ(float d)
      {
          return 0.5-0.5*p1+(1.0/(2.0*d))*p2;
      }

      void main(void)
      {
          float dist = (length(texCoordFoo.st)-0.6)*2.5;
          if (dist>1.0) {
            discard;
          }else{
            float eyeDepth = ConvertZtoEye(gl_FragCoord.z);
            eyeDepth -= cos(asin(dist))*30.0;
            gl_FragDepth = ConvertEyeToZ(eyeDepth);

            vec2 d = gl_FragCoord.xy - center;
            float distortion = exp( -0.5*( pow(-dist*8.0+4.0,2.0) ) )*0.15;
            vec2 noiseVec    = (d/dist)*screenInverse*distortion*life;
            gl_FragColor.xyw = vec3(noiseVec,gl_FragCoord.z);

            //float distortion = pow(dist, 1.0/4.0)-dist;
            //float distortion = smoothstep(1.0,0.0,dist)*0.25;
            //float distortion = tanh(dist*3.0)-dist;
            //float distortion = smoothstep(0.0,1.0,dist)-dist;
          }
      }
    ]],
    uniform = {
      screenInverse = {1/1280,1/1024},
      life = 1,
    }
  })

  if (warpShader == nil) then
    print(PRIO_MAJOR,"LUPS->ShockWave: criticle shader error: "..gl.GetShaderLog())
    return false
  end

  screenLoc = gl.GetUniformLocation(warpShader, 'screenInverse')

  dlist = gl.CreateList(gl.BeginEnd,GL.QUADS,function()
    gl.Vertex(-1,1)
    gl.Vertex(1,1)
    gl.Vertex(1,-1)
    gl.Vertex(-1,-1)
  end)
end

function ShockWave.Finalize()
  if (gl.DeleteShader) then
    gl.DeleteShader(warpShader)
  end
  gl.DeleteList(dlist)
end

function ShockWave.ViewResize()
  gl.UseShader(warpShader)
    gl.Uniform(screenLoc,1/vsx,1/vsy)
  gl.UseShader(0)
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function ShockWave:Update()
  self.life2  = self.life2 - self.life_inc
  self.radius = self.radius + self.growth
end

-- used if repeatEffect=true;
function ShockWave:ReInitialize()
  self.life2  = 1
  self.radius = 0
  self.startGameFrame = thisGameFrame
  self.dieGameFrame = self.startGameFrame + self.life
end

function ShockWave:CreateParticle()
  self.startGameFrame = thisGameFrame
  self.dieGameFrame   = self.startGameFrame + self.life

  self.life2 = 1
  self.life_inc = 1/(self.dieGameFrame - self.startGameFrame)
  self.radius = 0
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

function ShockWave.Create(Options)
  local newObject = MergeTable(Options, ShockWave.Default)
  setmetatable(newObject,ShockWave)  -- make handle lookup
  newObject:CreateParticle()
  return newObject
end

function ShockWave:Destroy()
end

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

return ShockWave