/** 
 * @file exopostprocess.cpp
 *
 * @brief This implements the Exodus post processing chain.
 *
 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
 * Copyright (C) 2011 Geenz Spad
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "exopostprocess.h"
#include "llviewershadermgr.h"
#include "pipeline.h"
#include "llviewercontrol.h"

static LLStaticHashedString sScreen_Res("screen_res");

LLVector3	exoPostProcess::sExodusRenderVignette;

exoPostProcess::exoPostProcess()
{
	initVB();
	sExodusRenderVignette = LLVector3(0.f, 0.f, 0.f);
}

exoPostProcess::~exoPostProcess()
{
	destroyVB();
}

void exoPostProcess::ExodusRenderPostStack(LLRenderTarget *src, LLRenderTarget *dst)
{
	if (mShaderLevel > 0)
	{
		if (sExodusRenderVignette.mV[0] > 0.f && LLPipeline::sRenderDeferred)
			ExodusRenderVignette(src, dst); // Don't render vignette here in non-deferred. Do it in the glow combine shader.
	}
}
void exoPostProcess::ExodusRenderPostSettingsUpdate()
{
	mShaderLevel = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_AVATAR);
	sExodusRenderVignette = gSavedSettings.getVector3("FSRenderVignette");
}
void exoPostProcess::ExodusRenderPostUpdate()
{
	etc1.setVec(0.f, 0.f);
	etc2.setVec((F32) gPipeline.mScreen.getWidth(),
				(F32) gPipeline.mScreen.getHeight());
	initVB();
}

void exoPostProcess::initVB()
{
	destroyVB();
	mExoPostBuffer = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1, 0);

	if (!gPipeline.sRenderDeferred)
	{
		if (mExoPostBuffer->allocateBuffer(3, 0, true))
		{
			LLStrider<LLVector3> v;
			LLStrider<LLVector2> uv1;
			LLStrider<LLVector2> uv2;

			mExoPostBuffer->getVertexStrider(v);
			mExoPostBuffer->getTexCoord0Strider(uv1);
			mExoPostBuffer->getTexCoord1Strider(uv2);
		
			uv1[0] = LLVector2(0.f, 0.f);
			uv1[1] = LLVector2(0.f, 2.f);
			uv1[2] = LLVector2(2.f, 0.f);
		
			uv2[0] = LLVector2(0.f, 0.f);
			uv2[1] = LLVector2(0.f, etc2.mV[1] * 2.f);
			uv2[2] = LLVector2(etc2.mV[0] * 2.f, 0.f);
		
			v[0] = LLVector3(-1.f, -1.f, 0.f);
			v[1] = LLVector3(-1.f, 3.f, 0.f);
			v[2] = LLVector3(3.f, -1.f, 0.f);

			mExoPostBuffer->flush();
		}
		else
		{
			LL_WARNS() << "Failed to allocate Vertex Buffer for exoPostProcessing" << LL_ENDL;
			destroyVB();
		}
	}
	else
	{
		if (mExoPostBuffer->allocateBuffer(8, 0, true))
		{
			LLStrider<LLVector3> vert;
			mExoPostBuffer->getVertexStrider(vert);
			LLStrider<LLVector2> tc0;
			LLStrider<LLVector2> tc1;
			mExoPostBuffer->getTexCoord0Strider(tc0);
			mExoPostBuffer->getTexCoord1Strider(tc1);

			vert[0].set(-1.f, 1.f, 0.f);
			vert[1].set(-1.f, -3.f, 0.f);
			vert[2].set(3.f, 1.f, 0.f);
		}
		else
		{
			LL_WARNS() << "Failed to allocate Vertex Buffer for exoPostProcessing" << LL_ENDL;
			destroyVB();
		}
	}
}

void exoPostProcess::destroyVB()
{
	mExoPostBuffer = NULL;
}

void exoPostProcess::ExodusRenderPost(LLRenderTarget* src, LLRenderTarget* dst, S32 type)
{
	if (type == EXODUS_RENDER_VIGNETTE_POST)
	{
		ExodusRenderVignette(src, dst);
	}
}

void exoPostProcess::ExodusRenderVignette(LLRenderTarget* src, LLRenderTarget* dst)
{
	if (mExoPostBuffer)
	{
		dst->bindTarget();
		LLGLSLShader *shader = &gPostVignetteProgram;
		shader->bind();

		mExoPostBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX);

		exoShader::BindRenderTarget(dst, shader, LLShaderMgr::EXO_RENDER_SCREEN);

		shader->uniform3fv(LLShaderMgr::EXO_RENDER_VIGNETTE, 1, sExodusRenderVignette.mV);
		mExoPostBuffer->drawArrays(LLRender::TRIANGLES, 0, 3);
		stop_glerror();

		shader->unbind();
		dst->flush();
	}
}

void exoShader::BindTex2D(LLTexture *tex2D, LLGLSLShader *shader, S32 uniform, S32 unit, LLTexUnit::eTextureType mode, LLTexUnit::eTextureAddressMode addressMode, LLTexUnit::eTextureFilterOptions filterMode)
{
	if (gPipeline.sRenderDeferred)
	{
		S32 channel = 0;
		channel = shader->enableTexture(uniform);
		if (channel > -1)
		{
			gGL.getTexUnit(channel)->bind(tex2D);
			gGL.getTexUnit(channel)->setTextureFilteringOption(filterMode);
			gGL.getTexUnit(channel)->setTextureAddressMode(addressMode);
		}
	}
	else
	{
		gGL.getTexUnit(unit)->bind(tex2D);
	}
}

void exoShader::BindRenderTarget(LLRenderTarget* tgt, LLGLSLShader* shader, S32 uniform, S32 unit, LLTexUnit::eTextureType mode)
{
	if (gPipeline.sRenderDeferred)
	{
		S32 channel = 0;
		channel = shader->enableTexture(uniform, tgt->getUsage());
		if (channel > -1)
		{
			tgt->bindTexture(0,channel);
			gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT);
		}
	}
	else
	{
		S32 reftex = shader->enableTexture(uniform, tgt->getUsage());
		if (reftex > -1)
		{
			gGL.getTexUnit(reftex)->activate();
			gGL.getTexUnit(reftex)->bind(tgt);
			gGL.getTexUnit(0)->activate();
		}
	}
	shader->uniform2f(sScreen_Res, tgt->getWidth(), tgt->getHeight());
}

