/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "gpu/gpu_renderer.h"
		#include "core/camera.h"
		#include "core/light.h"

		using namespace GS;
		using namespace GS::GPU;


//------------------------------------------------------------------------------
void				Renderer::RenderFullscreenLight(Shader &p, Core::Light &l)
{
	SetDepthFunc(DepthAlways);

	const float h = 1.f, k = 100.f;
	const float vtx[] = { -k, -k, h, k, -k, h, k, k, h, -k, k, h };

	const size_t stride = sizeof(float) * 3;
	helper_vtx_vbo->Update(vtx, 0, stride * 4);

	SetShaderProgram(&p);
	SetIndexSource(helper_idx_vbo);
	SetVertexSource(helper_vtx_vbo, stride);

	ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
	if	(vtx_parm)
		p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);

	p.SetTransformInputs(Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
	p.SetLightInputs(*this, *view_item, l);
	p.SetRendererInputs(*this);
	p.CommitInputs();

	DrawElements(Types::PrimitiveTriangle, 3 * 2, Types::ValueUShort);

	if	(vtx_parm)
		p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);

	SetDepthFunc(DepthLess);
}
void				Renderer::RenderSpotLight(Core::Light &l, const DrawContext &dc)
{
	if	(!view_item)
		return;

	Shader &p = (l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *spotlight_shadow_program : *spotlight_program;

	if	(!l.volume_range)
		RenderFullscreenLight(p, l);

	else
	{
		const Vector4 *vtx = l.frustum.GetVertices();

		const size_t stride = sizeof(float) * 4;
		helper_vtx_vbo->Update(vtx, 0, stride * 8);

		SetShaderProgram(&p);
		SetIndexSource(box_idx_vbo);
		SetVertexSource(helper_vtx_vbo, stride);

		ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
		if	(vtx_parm)
			p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);

		p.SetTransformInputs(m_projection, m_view, m_iview, &Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
		p.SetLightInputs(*this, *view_item, l);
		p.SetRendererInputs(*this);
		p.CommitInputs();

		// TODO Boolean operation on light frustum and clipping planes.
		Vector4	row = view_item->GetMatrix().GetRow(3);
		Frustum::Visibility viscode = l.frustum.ClassifySet(1, &row, Units::Cm(50.f));

		if	(viscode != Frustum::Outside)
		{
			SetDepthFunc(DepthGreater);
			SetCullFunc(CullBack);
		}

		DrawElements(Types::PrimitiveTriangle, 3 * 2 * 6, Types::ValueUShort);

		if	(vtx_parm)
			p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);

		if	(viscode != Frustum::Outside)
		{
			SetDepthFunc(DepthLess);
			SetCullFunc(CullFront);
		}
	}
}
void				Renderer::RenderPointLight(Core::Light &l, const DrawContext &dc)
{
	if	(!view_item)
		return;

	Shader &p = (l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *pointlight_shadow_program : *pointlight_program;

	if	(!l.volume_range)
		RenderFullscreenLight(p, l);

	else
	{
		float k = l.volume_range;
		float pointlight_vtx[] =
		{
			-k,  k, -k,		 k,  k, -k,		 k, -k, -k,		-k, -k, -k,
			-k,  k,  k,		 k,  k,  k,		 k, -k,  k,		-k, -k,  k
		};

		const size_t stride = sizeof(float) * 3;
		helper_vtx_vbo->Update(pointlight_vtx, 0, stride * 8);

		SetShaderProgram(&p);
		SetIndexSource(box_idx_vbo);
		SetVertexSource(helper_vtx_vbo, stride);

		ShaderInput *vtx_parm = p.GetInput(Core::ShaderInput::Position);
		if	(vtx_parm)
			p.Set(*vtx_parm->location, 3, Types::ValueFloat, false, stride, 0);

		p.SetTransformInputs(m_projection, m_view, m_iview, &l.GetMatrix(), &l.GetInverseMatrix());
		p.SetLightInputs(*this, *view_item, l);
		p.SetRendererInputs(*this);
		p.CommitInputs();

		//
		MinMax vminmax;
		vminmax.SetFromPositionSize(l.GetMatrix().GetRow(3), Vector4(l.volume_range, l.volume_range, l.volume_range) * 2.1f);

		bool inside = vminmax.IsInside(view_item->GetMatrix().GetRow(3));

		if	(inside)
		{
			SetDepthFunc(DepthGreater);
			SetCullFunc(CullBack);
		}
		else
		{
			SetDepthFunc(DepthLess);
			SetCullFunc(CullFront);
		}

		DrawElements(Types::PrimitiveTriangle, 3 * 12, Types::ValueUShort);

		if	(vtx_parm)
			p.Set(*vtx_parm->location, 0, Types::ValueLast, false, 0, NULL);

		SetDepthFunc(DepthLess);
		SetCullFunc(CullFront);
	}
}
void				Renderer::RenderLinearLight(Core::Light &l, const DrawContext &dc)
{
	RenderFullscreenQuad((l.shadow == Core::Light::Shadow_Map) && gpu_config.enable_shadow ? *linearlight_shadow_program : *linearlight_program, viewport.GetWidth() / (float)dimensions.x, viewport.GetHeight() / (float)dimensions.y, NULL, &l);
}
//------------------------------------------------------------------------------