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


		#include "core/terrain_shader_generator.h"
		#include "core/shader.h"

		using namespace GS::Core;


//------------------------------------------------------------------------------
static void			DeclareStaticTexture(Shader &shader, const char *var, const char *name)
{
	if	(name)
		if	(ShaderInput *input = shader.DeclareInput(var, ShaderInput::DataTexture2D, ShaderInput::Texture2D, ShaderInput::Uniform, ShaderInput::Pixel))
			input->parm_t = name;
}
bool				TerrainShaderGenerator::GenerateShader(Shader &shader, const char *t_blend, const Terrain::Layer layers[4])
{
	shader.Clear();

	// Get the number of layers.
	uint layer_count = 0;
	for (uint n = 0; n < 4; ++n)
		if	(layers[n].enabled)
			++layer_count;

	if	(layer_count == 0)
		return false;

	// Declare default inputs.
	shader.DeclareInput("a_position", ShaderInput::Vector3, ShaderInput::Position, ShaderInput::Attribute, ShaderInput::Vertex);
	shader.DeclareInput("a_normal", ShaderInput::Vector3, ShaderInput::Normal, ShaderInput::Attribute, ShaderInput::Vertex);
	shader.DeclareInput("a_uv", ShaderInput::Vector2, ShaderInput::UV0, ShaderInput::Attribute, ShaderInput::Vertex);

	shader.DeclareVarying("v_normal", "vec3");
	shader.vertex << "v_normal = a_normal;\n";

	// If using more than one layer, declare the blend map.
	if	(layer_count > 1)
	{
		DeclareStaticTexture(shader, "t_blend", t_blend);
		shader.DeclareVarying("v_uv", "vec2");
		shader.vertex << "v_uv = a_uv;\n";
		shader.pixel << "vec4 blend = texture2D(t_blend, v_uv);\n";
	}

	// Declare texture inputs.
	bool use_tangent = false;
	for (uint n = 0; n < 4; ++n)
	{
		if	(!layers[n].enabled)
			continue;

		if	(!layers[n].normal.IsEmpty())
		{
			DeclareStaticTexture(shader, String::Format("t_norm_%d", n), layers[n].normal);
			use_tangent = true;
		}

		DeclareStaticTexture(shader, String::Format("t_diff_%d", n), layers[n].diffuse);
		DeclareStaticTexture(shader, String::Format("t_spec_%d", n), layers[n].specular);
		DeclareStaticTexture(shader, String::Format("t_self_%d", n), layers[n].self);
	}

	// Build tangent matrix.
	if	(use_tangent)
	{
		shader.DeclareVarying("v_tangent", "vec3");
		shader.DeclareVarying("v_bitangent", "vec3");

		// Compute tangent & bitangent.
		shader.vertex <<
		"\
		v_tangent = normalize(cross(a_normal, vec3(0, 0, 1)));\n\
		v_bitangent = normalize(cross(v_tangent, a_normal));\n\
		";

		// Build tangent frame.
		shader.pixel << "mat3 tangent_frame = _build_mat3(v_tangent, v_bitangent, v_normal);\n";
	}

	// Declare layers.
	for (uint n = 0; n < 4; ++n)
		if	(layers[n].enabled)
		{
			// Vertex
			ShaderVarying *uv_varying = shader.DeclareVarying(String("v_uv_layer_") << n, "vec2");
			shader.vertex << String::Format("%s = a_uv * %.2f;\n", uv_varying->name.c_str(), layers[n].tiling);

			// Pixel
			if	(layers[n].normal.IsEmpty())
				shader.pixel << String::Format("vec3 norm_%d = v_normal;\n", n);
			else
			{
				shader.pixel << String::Format("vec3 normal_map_%d = texture2D(t_norm_%d, %s).xyz;\n", n, n, uv_varying->name.c_str());
				shader.pixel << String::Format("normal_map_%d = normalize(vec3(normal_map_%d.xy * 2.0 - 1.0, normal_map_%d.z));\n", n, n, n);
				shader.pixel << String::Format("vec3 norm_%d = n_mtx_mul(tangent_frame, normal_map_%d);\n", n, n);
			}

			if	(layers[n].diffuse.IsEmpty())
					shader.pixel << String::Format("vec4 diff_%d = vec4(1.0, 1.0, 1.0, 1.0);\n", n);
			else	shader.pixel << String::Format("vec4 diff_%d = texture2D(t_diff_%d, %s);\n", n, n, uv_varying->name.c_str());

			if	(layers[n].specular.IsEmpty())
					shader.pixel << String::Format("vec4 spec_%d = vec4(1.0, 1.0, 1.0, 1.0);\n", n);
			else	shader.pixel << String::Format("vec4 spec_%d = texture2D(t_spec_%d, %s);\n", n, n, uv_varying->name.c_str());

			if	(layers[n].self.IsEmpty())
					shader.pixel << String::Format("vec4 self_%d = vec4(0.0, 0.0, 0.0, 1.0);\n", n);
			else	shader.pixel << String::Format("vec4 self_%d = texture2D(t_self_%d, %s);\n", n, n, uv_varying->name.c_str());
		}

	// Build the layer mixing code.
	if	(layer_count == 1)
	{
		uint n = 0;
		for ( ; n < 4; ++n)
			if	(layers[n].enabled)
				break;

		shader.pixel << "%normal% = " << String::Format("norm_%d", n) << ";\n";
		shader.pixel << "%diffuse% = " << String::Format("diff_%d", n) << ";\n";
		shader.pixel << "%specular% = " << String::Format("diff_%d", n) << ";\n";
		shader.pixel << "%constant% = " << String::Format("self_%d", n) << ";\n";
	}
	else
	{
		static const char *layer_comp[4] = { "x", "y", "z", "w" };

		String norm, diff, spec, self;

		bool first_assign = true;
		for (uint n = 0; n < 4; ++n)
		{
			if	(!layers[n].enabled)
				continue;

			if	(first_assign)
			{
				norm << "%normal% = " << String::Format("norm_%d * blend.%s", n, layer_comp[n]);
				diff << "%diffuse% = " << String::Format("diff_%d * blend.%s", n, layer_comp[n]);
				spec << "%specular% = " << String::Format("spec_%d * blend.%s", n, layer_comp[n]);
				self << "%constant% = " << String::Format("self_%d * blend.%s", n, layer_comp[n]);

				first_assign = false;
			}
			else
			{
				norm << String::Format(" + norm_%d * blend.%s", n, layer_comp[n]);
				diff << String::Format(" + diff_%d * blend.%s", n, layer_comp[n]);
				spec << String::Format(" + spec_%d * blend.%s", n, layer_comp[n]);
				self << String::Format(" + self_%d * blend.%s", n, layer_comp[n]);
			}
		}

		shader.pixel << norm << ";\n";
		shader.pixel << diff << ";\n";
		shader.pixel << spec << ";\n";
		shader.pixel << self << ";\n";
	}
	return true;
}
//------------------------------------------------------------------------------
