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

local UnitSmoke = {}
UnitSmoke.__index = UnitSmoke

local UnitSmokeShader
local widthLoc, timeLoc, headingLoc
local trailDirsUniform = {}

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

function UnitSmoke.GetInfo()
	return {
		name      = "UnitSmoke",
		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       = false,
		shader    = true,
		rtt       = false,
		ctt       = false,
	}
end

UnitSmoke.Default = {
	layer = 1,

	life     = math.huge,
	pos      = {0,0,0},
	size     = 20,
	width    = 5,
	quads    = 10, --//max 16

	repeatEffect = false,
	dieGameFrame = math.huge
}

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

local glUniform   = gl.Uniform
local glUseShader = gl.UseShader

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

function UnitSmoke:BeginDraw()
	glUseShader(UnitSmokeShader)
	glUniform(timeLoc, thisGameFrame * 0.01 )
	gl.Texture(1,"bitmaps/GPL/Lups/mynoise.png")
	gl.Texture(0,":c:bitmaps/GPL/Lups/smoketrail.png")
	--gl.Texture(0,":c:bitmaps/GPL/Lups/flametrail.png")
	--gl.Texture(0,":c:bitmaps/GPL/Lups/firetrail.png")
	--gl.Texture(false)
	--gl.Blending(GL.SRC_ALPHA,GL.ONE)

	local modelview = {gl.GetMatrixData("camera")}
	gl.MatrixMode(GL.TEXTURE)
	gl.PushMatrix()
	gl.LoadMatrix(modelview) --//FIXME: gl.MultMatrix("camera")
	gl.MatrixMode(GL.MODELVIEW)
	gl.PushMatrix()
	gl.LoadIdentity()

	--gl.PolygonMode(GL.FRONT_AND_BACK, GL.LINE)
end

function UnitSmoke:EndDraw()
	glUseShader(0)
	--gl.Blending(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA)
	gl.Texture(0,false)
	gl.Texture(1,false)

	gl.MatrixMode(GL.TEXTURE)
	gl.PopMatrix()
	gl.MatrixMode(GL.MODELVIEW)
	gl.PopMatrix()

	gl.PolygonMode(GL.FRONT_AND_BACK, GL.FILL)
end


function UnitSmoke:Draw()
	local pos = self.pos
	glUniform(widthLoc, self.width )
	glUniform(headingLoc, Spring.GetUnitHeading(self.unit)/32000 );

	local quads = self.quads-1 --// "-1" cause glsl is 0-indexed and lua 1-indexed

	for i=1,quads+1 do
		local dir = self.trailDirs[i]
		glUniform( trailDirsUniform[i] , dir[1], dir[2] + self.size*(i-1)/quads , dir[3] )
	end

	gl.PushMatrix()
		gl.Translate(pos[1],pos[2],pos[3])
		gl.BeginEnd(GL.QUADS,function()
			for i=0,quads-1 do
				--local tex_t      = 1 - self.trailTexCoords[i]   / self.totalLength
				--local tex_t_next = 1 - self.trailTexCoords[i+1] / self.totalLength

				local tex_t      = 1 - i/quads
				local tex_t_next = 1 - (i+1)/quads

				gl.TexCoord(-1,tex_t_next,0,i+1)
				gl.Vertex(0,0,0)
				gl.TexCoord(1,tex_t_next,1,i+1)
				gl.Vertex(0,0,0)
				gl.TexCoord(1,tex_t,1,i)
				gl.Vertex(0,0,0)
				gl.TexCoord(-1,tex_t,0,i)
				gl.Vertex(0,0,0)
			end
		end)
	gl.PopMatrix()
end

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

function UnitSmoke.Initialize()
	UnitSmokeShader = gl.CreateShader({
		vertex = [[
			uniform float width;
			uniform float time;
			uniform float heading;

			uniform vec3 traildirs[16];

			void main()
			{
				 vec3 updir,right;

				 gl_Position = (gl_ModelViewMatrix * gl_Vertex);

				 updir = traildirs[int(gl_MultiTexCoord0.w)];
				 gl_Position.xyz += updir;

				 //gl_TexCoord[0].pq = updir.xy*vec2(0.1) + vec2(heading, -time*3.0);

				 gl_Position = gl_TextureMatrix[0] * gl_Position;

				 updir = ( gl_TextureMatrix[0] * vec4(updir,0.0) ).xyz - gl_Position.xyz;
				 right = normalize( cross(updir,gl_Position.xyz) );

				 //gl_Position.x += gl_MultiTexCoord0.x * size * 0.35;
				 gl_Position.xyz += right * gl_MultiTexCoord0.x * width;

				 gl_Position    = gl_ProjectionMatrix * gl_Position;

				 //gl_FrontColor = vec4(1.0,1.0,1.0,1.0);
				 gl_TexCoord[0].st = gl_MultiTexCoord0.pt;
				 //gl_TexCoord[0].st = gl_Position.xy;
				 gl_TexCoord[0].pq = gl_TexCoord[0].st + vec2(heading, time*3.0);
			}
		]],
		fragment = [[
			uniform sampler2D SmokeTex;
			uniform sampler2D noiseMap;

			void main()
			{
				 vec4 noise = texture2D(noiseMap, gl_TexCoord[0].pq*0.8); //vec4(0.0);

				 gl_FragColor = texture2D(SmokeTex, gl_TexCoord[0].st + vec2( (noise.r-0.5) ,0.0) );
				 //gl_FragColor.a *= (gl_FragColor.r+gl_FragColor.g+gl_FragColor.b)*0.333;
				 gl_FragColor.a *= 0.9;
			}
		]],
		uniformInt={
			SmokeTex = 0,
			noiseMap = 1,
		},
	})

	if (UnitSmokeShader == nil) then
		print(PRIO_MAJOR,"LUPS->UnitSmoke: critical shader error: "..gl.GetShaderLog())
		return false
	end

	widthLoc = gl.GetUniformLocation(UnitSmokeShader, 'width')
	timeLoc  = gl.GetUniformLocation(UnitSmokeShader, 'time')
	headingLoc = gl.GetUniformLocation(UnitSmokeShader, 'heading')

	for i=1,16 do
		trailDirsUniform[i] = gl.GetUniformLocation(UnitSmokeShader,"traildirs["..(i-1).."]")
	end
end

function UnitSmoke.Finalize()
	if (gl.DeleteShader) then
		gl.DeleteShader(UnitSmokeShader)
	end
end

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

function UnitSmoke:Update()
	local vel = {Spring.GetUnitVelocity(self.unit)}
	vel = Vmul( -1 , vel )

	local x,y,z = Spring.GetWind()
	local wind  = Vmul( 0.125 , {z,y,-x} )

	self.trailDirs[2] = Vadd( Vadd(vel,wind), {0,2,0})

	for i=self.quads,3,-1 do
		self.trailDirs[i] = Vadd( self.trailDirs[i-1], Vadd(vel,wind) )
	end

--[[
	local tlength = 0
	for i=1,16 do
		tlength = tlength + Vlength( self.trailDirs[i] )
		self.trailTexCoords[i] = tlength
	end
	self.totalLength = tlength
--]]
end

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

function UnitSmoke:CreateParticle()
--[[
	self.trailTexCoords = { [0] = 0 }
	self.totalLength = 0
--]]

	if (self.quads>16) then self.quads=16
	elseif (self.quads<5) then self.quads=5 end

	self.trailDirs = {}
	for i=1,self.quads do
		self.trailDirs[i] = {0,0.05,0}
	end

	self.startGameFrame = thisGameFrame
	self.dieGameFrame   = self.startGameFrame + self.life
end

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

local MergeTable   = MergeTable
local setmetatable = setmetatable

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

function UnitSmoke:Destroy()
end

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

return UnitSmoke
