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

local AirJet = {}
AirJet.__index = AirJet

local jetShader,jitShader
local tex --//screencopy
local timerUniform, timer2Uniform

local lastTexture1,lastTexture2

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

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

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

    --// gfx requirement
    fbo       = true,
    shader    = true,
    distortion= true,
    ms        = -1,
    intel     = -1,
  }
end


AirJet.Default = {
  --// visibility check
  los            = true,
  airLos         = true,
  radar          = false,
  
  layer = 4,
  life  = math.huge,
  repeatEffect  = true,

  emitVector    = {0,0,-1},
  pos           = {0,0,0}, --// not used
  width         = 4,
  length        = 50,
  color         = {0, 0, 0.5},
  distortion    = 0.02,
  jitterWidthScale  = 3,
  jitterLengthScale = 3,
  animSpeed     = 1,

  texture1      = "bitmaps/GPL/Lups/perlin_noise.jpg", --// noise texture
  texture2      = ":c:bitmaps/GPL/Lups/jet.bmp",       --// shape
  texture3      = ":c:bitmaps/GPL/Lups/jet.bmp",       --// jitter shape
}

-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
local spGetUnitViewPosition = Spring.GetUnitViewPosition
local spGetPositionLosState = Spring.GetPositionLosState
local spGetUnitLosState     = Spring.GetUnitLosState
local spIsSphereInView      = Spring.IsSphereInView
local spGetUnitRadius       = Spring.GetUnitRadius

local IsPosInLos    = Spring.IsPosInLos
local IsPosInAirLos = Spring.IsPosInAirLos
local IsPosInRadar  = Spring.IsPosInRadar

local spGetGameSeconds = Spring.GetGameSeconds
local glUseShader = gl.UseShader
local glUniform   = gl.Uniform
local glBlending  = gl.Blending
local glTexture   = gl.Texture
local glCallList  = gl.CallList
local GL_ONE_MINUS_SRC_ALPHA = GL.ONE_MINUS_SRC_ALPHA
local GL_SRC_ALPHA           = GL.SRC_ALPHA
local GL_ONE                 = GL.ONE

function AirJet:BeginDraw()
  glUseShader(jetShader)
    glUniform(timerUniform, spGetGameSeconds())
  glBlending(GL_ONE,GL_ONE)
end

function AirJet:EndDraw()
  glUseShader(0)
  glTexture(1,false)
  glTexture(2,false)
  glBlending(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)
  lastTexture1,lastTexture2 = "",""
end

function AirJet:Draw()
  if (lastTexture1~=self.texture1) then
    glTexture(1,self.texture1)
    lastTexture1=self.texture1
  end
  if (lastTexture2~=self.texture2) then
    glTexture(2,self.texture2)
    lastTexture2=self.texture2
  end

  glCallList(self.dList)
end



function AirJet:BeginDrawDistortion()
  glUseShader(jitShader)
    glUniform(timer2Uniform, spGetGameSeconds())
end

function AirJet:EndDrawDistortion()
  glUseShader(0)
  glTexture(1,false)
  glTexture(2,false)
  lastTexture1,lastTexture2 = "",""
end

function AirJet:DrawDistortion()
  if (lastTexture1~=self.texture1) then
    glTexture(1,self.texture1)
    lastTexture1=self.texture1
  end
  if (lastTexture2~=self.texture3) then
    glTexture(2,self.texture3)
    lastTexture2=self.texture3
  end

  glCallList(self.dList)
end

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

-- used if repeatEffect=true;
function AirJet:ReInitialize()
  self.dieGameFrame = self.dieGameFrame + self.life
end

function AirJet.Initialize()

  jetShader = gl.CreateShader({
    vertex = [[
      uniform float timer;

      varying float distortion;
      varying vec4 texCoords;

      const vec4 centerPos = vec4(0.0,0.0,0.0,1.0);

      #define WIDTH  gl_Vertex.x
      #define LENGTH gl_Vertex.y
      #define TEXCOORD gl_Vertex.zw
      // gl_MultiTexCoord0.xy := jitter width/length scale (i.e jitter quad length = gl_vertex.x * gl_MultiTexCoord0.x)
      // gl_MultiTexCoord0.z  := (quad_width) / (quad_length) (used to normalize the texcoord dimensions)
      #define DISTORTION_STRENGTH gl_MultiTexCoord0.w
      #define EMITDIR gl_MultiTexCoord1
      #define COLOR gl_MultiTexCoord2.rgb
      #define ANIMATION_SPEED gl_MultiTexCoord2.w

      void main()
      {
        texCoords.st = TEXCOORD;
        texCoords.pq = TEXCOORD;
        texCoords.q += timer * ANIMATION_SPEED;

        gl_Position = gl_ModelViewMatrix * centerPos ;
        vec3 dir3   = vec3(gl_ModelViewMatrix * EMITDIR) - gl_Position.xyz;
        vec3 v = normalize( dir3 );
        vec3 w = normalize( -vec3(gl_Position) );
        vec3 u = normalize( cross(w,v) );
        gl_Position.xyz += WIDTH*v + LENGTH*u;
        gl_Position      = gl_ProjectionMatrix * gl_Position;

        gl_FrontColor.rgb = COLOR;

        distortion = DISTORTION_STRENGTH;
      }
    ]],
    fragment = [[
      uniform sampler2D noiseMap;
      uniform sampler2D mask;

      varying float distortion;
      varying vec4 texCoords;

      void main(void)
      {
          vec2 displacement = texCoords.pq;

          vec2 txCoord = texCoords.st;
          txCoord.s += (texture2D(noiseMap, displacement * distortion * 20.0).y - 0.5) * 40.0 * distortion;
          txCoord.t +=  texture2D(noiseMap, displacement).x * (1.0-texCoords.t)        * 15.0 * distortion;
          float opac = texture2D(mask,txCoord.st).r;

          gl_FragColor.rgb  = opac * gl_Color.rgb; //color
          gl_FragColor.rgb += pow(opac, 5.0 );     //white flame
          gl_FragColor.a    = opac*1.5;
      }

    ]],
    uniformInt = {
      noiseMap = 1,
      mask = 2,
    },
    uniform = {
      timer = 0,
    }
  })

  if (jetShader == nil) then
    print(PRIO_MAJOR,"LUPS->airjet: (color-)shader error: "..gl.GetShaderLog())
    return false
  end

  jitShader = gl.CreateShader({
    vertex = [[
      uniform float timer;

      varying float distortion;
      varying vec4 texCoords;

      const vec4 centerPos = vec4(0.0,0.0,0.0,1.0);

      #define WIDTH  gl_Vertex.x
      #define LENGTH gl_Vertex.y
      #define TEXCOORD gl_Vertex.zw
      // gl_MultiTexCoord0.xy := jitter width/length scale (i.e jitter quad length = gl_vertex.x * gl_MultiTexCoord0.x)
      // gl_MultiTexCoord0.z  := (quad_width) / (quad_length) (used to normalize the texcoord dimensions)
      #define DISTORTION_STRENGTH gl_MultiTexCoord0.w
      #define EMITDIR gl_MultiTexCoord1
      #define COLOR gl_MultiTexCoord2.rgb
      #define ANIMATION_SPEED gl_MultiTexCoord2.w

      void main()
      {
        texCoords.st  = TEXCOORD;
        texCoords.pq  = TEXCOORD*0.8;
        texCoords.p  *= gl_MultiTexCoord0.z;
        texCoords.pq += 0.2*timer*ANIMATION_SPEED;

        gl_Position = gl_ModelViewMatrix * centerPos;
        vec3 dir3   = vec3(gl_ModelViewMatrix * EMITDIR) - gl_Position.xyz;
        vec3 v = normalize( dir3 );
        vec3 w = normalize( -vec3(gl_Position) );
        vec3 u = normalize( cross(w,v) );
        float length = LENGTH * gl_MultiTexCoord0.x;
        float width  = WIDTH * gl_MultiTexCoord0.y;
        gl_Position.xyz += width*v + length*u;
        gl_Position      = gl_ProjectionMatrix * gl_Position;

        distortion = DISTORTION_STRENGTH;
      }
    ]],
    fragment = [[
      uniform sampler2D noiseMap;
      uniform sampler2D mask;

      varying float distortion;
      varying vec4 texCoords;

      void main(void)
      {
          float opac    = texture2D(mask,texCoords.st).r;
          vec2 noiseVec = (texture2D(noiseMap, texCoords.pq).st - 0.5) * distortion * opac;
          gl_FragColor  = vec4(noiseVec.xy,0.0,gl_FragCoord.z);
      }

    ]],
    uniformInt = {
      noiseMap = 1,
      mask = 2,
    },
    uniform = {
      timer = 0,
    }
  })


  if (jitShader == nil) then
    print(PRIO_MAJOR,"LUPS->airjet: (jitter-)shader error: "..gl.GetShaderLog())
    return false
  end

  timerUniform  = gl.GetUniformLocation(jetShader, 'timer')
  timer2Uniform = gl.GetUniformLocation(jitShader, 'timer')
end

function AirJet:Finalize()
  if (gl.DeleteShader) then
    gl.DeleteShader(jetShader)
    gl.DeleteShader(jitShader)
  end
end

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

local glMultiTexCoord = gl.MultiTexCoord
local glVertex        = gl.Vertex
local glCreateList    = gl.CreateList
local glDeleteList    = gl.DeleteList
local glBeginEnd      = gl.BeginEnd
local GL_QUADS        = GL.QUADS

local function BeginEndDrawList(self)
  local color = self.color
  local ev    = self.emitVector 
  glMultiTexCoord(0,self.jitterWidthScale,self.jitterLengthScale,self.width/self.length,self.distortion)
  glMultiTexCoord(1,ev[1],ev[2],ev[3],1)
  glMultiTexCoord(2,color[1],color[2],color[3],self.animSpeed)

  --// xy = width/length ; zw = texcoord
  local w = self.width
  local l = self.length
  glVertex(-l,-w, 1,0)
  glVertex(0, -w, 1,1)
  glVertex(0,  w, 0,1)
  glVertex(-l, w, 0,0)
end


function AirJet:CreateParticle()
  self.dList = glCreateList(glBeginEnd,GL_QUADS,
                            BeginEndDrawList,self)

  --// used for visibility check
  self.radius = self.length*self.jitterLengthScale

  self.dieGameFrame  = thisGameFrame + self.life
end

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

local MergeTable   = MergeTable
local setmetatable = setmetatable

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

function AirJet:Destroy()
  --gl.DeleteTexture(self.texture1)
  --gl.DeleteTexture(self.texture2)
  glDeleteList(self.dList)
end

function AirJet:Visible()
  local radius = self.length
  local posX,posY,posZ = self.pos[1],self.pos[2],self.pos[3]
  local losState
  if (self.unit and not self.worldspace) then
    losState = GetUnitLosState(self.unit)
    local ux,uy,uz = spGetUnitViewPosition(self.unit)
	if ux then
      posX,posY,posZ = posX+ux,posY+uy,posZ+uz
      radius = radius + (spGetUnitRadius(self.unit) or 30)
	end
  end
  if (losState==nil) then
    if (self.radar) then
      losState = IsPosInRadar(posX,posY,posZ)
    end
    if ((not losState) and self.airLos) then
      losState = IsPosInAirLos(posX,posY,posZ)
    end
    if ((not losState) and self.los) then
      losState = IsPosInLos(posX,posY,posZ)
    end
  end
  return (losState)and(spIsSphereInView(posX,posY,posZ,radius))
end
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------

return AirJet