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


		#include "core/resource_geometry_generator.h"
		#include "core/geometry.h"
		#include "core/material_to_shader_tree.h"

		using namespace GS;
		using namespace GS::Core;


//------------------------------------------------------------------------------
bool				GeometryGenerator::IsGenerated(const char *name)
{	return String(name).StartsWith("@sys/gen/");	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
static	bool		GenerateSphere(const char *name, Geometry &g)
{
	g.Free();

	// Extract dimensions.
	float r = 0.5f;

	// Generate vertices.
	const int s_count = 6, c_count = 16;

	if	(!g.AllocateVertex((s_count + 1) * c_count + 2))
		return false;

	Vector4 *v = g.vtx;

	v->Set(0, r, 0);
	v++;

	for (int s = 0; s < (s_count + 1); ++s)
	{
		float t = float(s + 1) / (s_count + 2);
		float a = t * Units::Deg(180.f);

		float y = Math::Cos(a) * r;
		float s_r = Math::Sin(a) * r;

		for	(int c = 0; c < c_count; ++c)
		{
			const float c_a = c * Units::Deg(360.f) / c_count;

			v->Set(Math::Cos(c_a) * s_r, y, Math::Sin(c_a) * s_r);
			v++;
		}
	}

	v->Set(0, -r, 0);

	// Build polygons.
	if	(!g.AllocatePolygon((s_count + 2) * c_count))
		return false;

	Polygon *p = g.pol;

	for	(int c = 0; c < c_count; ++c)
	{
		p->vtx_count = 3;
		p->material = 0;
		p++;
	}
	for (int s = 0; s < s_count; ++s)
		for	(int c = 0; c < c_count; ++c)
		{
			p->vtx_count = 4;
			p->material = 0;
			p++;
		}
	for	(int c = 0; c < c_count; ++c)
	{
		p->vtx_count = 3;
		p->material = 0;
		p++;
	}

	if	(!g.AllocatePolygonBinding())
		return false;

	p = g.pol;

	for	(int c = 0; c < c_count; ++c)
	{
		p->binding[0] = 0; p->binding[2] = c + 1; p->binding[1] = Types::Wrap(c + 2, 1, c_count);
		p++;
	}
	for	(int s = 0; s < s_count; ++s)
	{
		int i = 1 + c_count * s;
		for	(int c = 0; c < c_count; ++c)
		{
			p->binding[0] = i + c; p->binding[1] = Types::Wrap(i + c + 1, i, i + c_count - 1);
			p->binding[3] = i + c + c_count; p->binding[2] = Types::Wrap(i + c + c_count + 1, i + c_count, i + c_count * 2 - 1);
			p++;
		}
	}

	int i = 1 + c_count * s_count;
	for	(int c = 0; c < c_count; ++c)
	{
		p->binding[0] = i + c; p->binding[1] = Types::Wrap(i + c + 1, i, i + c_count - 1);
		p->binding[2] = i + c_count;
		p++;
	}
	return true;
}
static	bool		GenerateCube(const char *name, Geometry &g)
{
	g.Free();

	// Extract dimensions.
	Vector4	d(1, 1, 1);
	d *= 0.5f;

	// Generate vertices.
	if	(!g.AllocateVertex(8))
		return false;

	g.vtx[0].Set(-d.x,  d.y,  d.z);
	g.vtx[1].Set( d.x,  d.y,  d.z);
	g.vtx[2].Set( d.x,  d.y, -d.z);
	g.vtx[3].Set(-d.x,  d.y, -d.z);
	g.vtx[4].Set(-d.x, -d.y,  d.z);
	g.vtx[5].Set( d.x, -d.y,  d.z);
	g.vtx[6].Set( d.x, -d.y, -d.z);
	g.vtx[7].Set(-d.x, -d.y, -d.z);

	// Build polygons.
	if	(!g.AllocatePolygon(6))
		return false;

	for	(uint n = 0; n < 6; ++n)
	{
		g.pol[n].vtx_count = 4;
		g.pol[n].material = 0;
	}

	if	(!g.AllocatePolygonBinding())
		return false;

	g.pol[0].binding[0] = 0; g.pol[0].binding[1] = 1; g.pol[0].binding[2] = 2; g.pol[0].binding[3] = 3;
	g.pol[1].binding[0] = 3; g.pol[1].binding[1] = 2; g.pol[1].binding[2] = 6; g.pol[1].binding[3] = 7;
	g.pol[2].binding[0] = 7; g.pol[2].binding[1] = 6; g.pol[2].binding[2] = 5; g.pol[2].binding[3] = 4;
	g.pol[3].binding[0] = 4; g.pol[3].binding[1] = 5; g.pol[3].binding[2] = 1; g.pol[3].binding[3] = 0;
	g.pol[4].binding[0] = 2; g.pol[4].binding[1] = 1; g.pol[4].binding[2] = 5; g.pol[4].binding[3] = 6;
	g.pol[5].binding[0] = 0; g.pol[5].binding[1] = 3; g.pol[5].binding[2] = 7; g.pol[5].binding[3] = 4;

	return true;
}
static	bool		GeneratePlane(const char *name, Geometry &g)
{
	g.Free();

	// Extract dimensions.
	Vector4	d(1, 1, 1);

	// Generate vertices.
	if	(!g.AllocateVertex(4))
		return false;

	g.vtx[0].Set(-d.x,  0,  d.z);
	g.vtx[1].Set( d.x,  0,  d.z);
	g.vtx[2].Set( d.x,  0, -d.z);
	g.vtx[3].Set(-d.x,  0, -d.z);

	// Build polygons.
	if	(!g.AllocatePolygon(1))
		return false;

	g.pol[0].vtx_count = 4;
	g.pol[0].material = 0;

	if	(!g.AllocatePolygonBinding())
		return false;

	g.pol[0].binding[0] = 0; g.pol[0].binding[1] = 1; g.pol[0].binding[2] = 2; g.pol[0].binding[3] = 3;
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				GeometryGenerator::Generate(const char *name, Geometry &g)
{
	String _name(name);

	bool r = false;

	if	(_name.StartsWith("@sys/gen/plane"))
		r = GeneratePlane(name, g);
	else	if	(_name.StartsWith("@sys/gen/cube"))
		r = GenerateCube(name, g);
	else	if	(_name.StartsWith("@sys/gen/sphere"))
		r = GenerateSphere(name, g);

	if	(r)
	{
		// Compute extra data.
		g.ComputeVertexNormal(Units::Deg(45.f));

		// Load materials.
		if	(g.material_table.Allocate(1))
			g.material_table[0].name = "@core/builtin/material/default.nmm";
	}
	return r;
}
//------------------------------------------------------------------------------
