
#include "stdafx.h"
#include "model.h"
#include "node.h"
#include "../manager/resourcemanager.h"
#include "mesh.h"
#include "bonemgr.h"


using namespace graphic;



cModel::cModel(const int id) :
	cNode(id, "model" )
,	m_bone(NULL)
,	m_isRenderMesh(true)
,	m_isRenderBone(false)
,	m_isRenderBoundingBox(false)
,	m_isRenderBoneBoundingBox(false)
,	m_isRenderShadow(false)
,	m_type(MODEL_TYPE::RIGID)
,	m_curAni(NULL)
{
	m_nodeType = MODEL;
	
}

cModel::~cModel()
{
	Clear();
}


bool cModel::Create(cRenderer &renderer, const StrPath &modelName, MODEL_TYPE::TYPE type
	,const bool isLoadShader)
	// type = MODEL_TYPE::AUTO
{
	sRawMeshGroup *rawMeshes = cResourceManager::Get()->LoadRawMesh(modelName);
	RETV(!rawMeshes, false);

	Clear();

	m_fileName = modelName;
	const bool isSkinnedMesh = !rawMeshes->bones.empty();

	// ½ºÅ°´× ¾Ö´Ï¸ÞÀÌ¼ÇÀÌ¸é BoneÀ» »ý¼ºÇÑ´Ù.
	switch (type)
	{
	case MODEL_TYPE::RIGID:
		m_type = MODEL_TYPE::RIGID;
		break;

	case MODEL_TYPE::AUTO:
	case MODEL_TYPE::SKIN:
		if (isSkinnedMesh)
		{
			m_bone = new cBoneMgr(renderer, 0, *rawMeshes);
			m_type = MODEL_TYPE::SKIN;
		}
		else
		{
			m_type = MODEL_TYPE::RIGID;
		}
		break;
	}

	// ¸Þ½¬ »ý¼º.
	int id = 0;
	for each (auto &mesh in rawMeshes->meshes)
	{
		cMesh *p = NULL;
		if (MODEL_TYPE::SKIN == m_type)
		{
			p = new cSkinnedMesh(renderer, id++, &m_bone->GetPalette(), mesh);
		}
		else
		{
			p = new cRigidMesh(renderer, id++, mesh);
		}

		if (p)
			m_meshes.push_back(p);
	}
	
	if (isLoadShader)
	{
		if (MODEL_TYPE::RIGID == m_type)
			SetShader( cResourceManager::Get()->LoadShader(renderer, "hlsl_rigid_phong.fx") );
		else
		{

		}
			//SetShader(cResourceManager::Get()->LoadShader(renderer, "hlsl_skinning_using_texcoord_sc2.fx"));
			//SetShader(cResourceManager::Get()->LoadShader(renderer, "hlsl_skinning_using_texcoord_unlit.fx"));
	}

	// ¸ðµ¨ Ãæµ¹ ¹Ú½º¸¦ »ý¼ºÇÑ´Ù.
	GetCollisionBox(renderer);

	return true;
}


// ¾Ö´Ï¸ÞÀÌ¼Ç ½ÃÀÛ.
bool cModel::SetAnimation( const StrId &aniFileName, 
	const int nAniFrame, // = 0
	const bool isLoop, // = false
	const bool isBlend // = true
	)
{
	sRawAniGroup *rawAnies = cResourceManager::Get()->LoadAnimation(aniFileName);
	RETV(!rawAnies, false);

	m_curAni = rawAnies;

	if (MODEL_TYPE::SKIN == m_type)
	{
		m_bone->SetAnimation(*rawAnies, nAniFrame, isLoop, isBlend);
	}
	else
	{
		for (u_int i=0; i < m_meshes.size(); ++i)
		{
			((cRigidMesh*)m_meshes[ i])->LoadAnimation(rawAnies->anies[i]);
		}
	}

	return true;
}


bool cModel::Update(const float deltaSeconds)
{
	for each (auto node in m_meshes)
		node->Update(deltaSeconds);

	if (m_bone)
		m_bone->Update(deltaSeconds);

	return true;
}


void cModel::Render(cRenderer &renderer, const Matrix44 &tm)
{
	__super::Render(renderer, m_TM * tm);

	// ¼ÎÀÌ´õ°¡ ¼³Á¤µÇÁö ¾Ê¾Ò´Ù¸é ¾÷µ¥ÀÌÆ® ÇÑ´Ù.
	if (m_shader &&  !m_meshes.empty()) // check shader setting
	{
		if (m_meshes[ 0]->GetShader() == NULL)
		{
			for each (auto node in m_meshes)
				node->SetShader(m_shader);
		}		
	}

	renderer.GetDevice()->SetTransform(D3DTS_WORLD, (D3DXMATRIX*)&Matrix44::Identity);

	if (m_isRenderMesh)
	{
		for each (auto node in m_meshes)
			node->Render(renderer, m_TM * tm);
	}

	if (m_isRenderBone && m_bone)
		m_bone->Render(renderer, m_TM * tm);

	if (m_isRenderBoundingBox)
		m_renderBoundingBox.Render(renderer, m_TM * tm);
	if (m_isRenderBoneBoundingBox && m_bone)
		m_bone->RenderBoundingBox(renderer, m_TM * tm);
}


// ±×¸²ÀÚ Ãâ·Â.
void cModel::RenderShadow(cRenderer &renderer, const Matrix44 &viewProj,
	const Vector3 &lightPos, const Vector3 &lightDir, const Matrix44 &parentTm)
{
	renderer.GetDevice()->SetTransform(D3DTS_WORLD, (D3DXMATRIX*)&Matrix44::Identity);

	if (m_isRenderMesh)
	{
		const Matrix44 tm = m_TM * parentTm;
		for each (auto node in m_meshes)
			node->RenderShadow(renderer, viewProj, lightPos, lightDir, tm);
	}
}


// ±×¸²ÀÚ ¾÷µ¥ÀÌÆ®.
// GetDevice()->BeginScene(); ÇÔ¼ö°¡ È£ÃâµÇ±â Àü¿¡ 
// UpdateShadow() ÇÔ¼ö¸¦ È£ÃâÇØ¾ß ÇÑ´Ù.
void cModel::UpdateShadow(cRenderer &renderer)
{	
	if (m_isRenderShadow)
	{
		// ±×¸²ÀÚ¸Ê »ý¼º.
		if (!m_shadow.IsLoaded())
			m_shadow.Create(renderer, 256, 256);

		m_shadow.UpdateShadow(renderer, *this);
	}
}


// remove all data
void cModel::Clear()
{
	for each (auto mesh in m_meshes)
	{
		SAFE_DELETE(mesh);
	}
	m_meshes.clear();
	m_curAni = NULL;

	SAFE_DELETE(m_bone);
}


// ¸Þ½¬¸¦ Ã£¾Æ¼­ ¸®ÅÏÇÑ´Ù.
cMesh* cModel::FindMesh(const StrId &meshName)
{
	for each (auto &mesh in m_meshes)
	{
		if (mesh->GetName() == meshName)
			return (cMesh*)mesh;
	}
	return NULL;
}


// Çà·ÄÆÄ·¹Æ®¸¦ µ¿±âÈ­ ÇÑ´Ù.
bool cModel::SharePalette(vector<Matrix44> *palette)
{
	RETV(m_type != MODEL_TYPE::SKIN, false);

	for each (auto &mesh in m_meshes)
	{
		if (cSkinnedMesh *skinMesh = dynamic_cast<cSkinnedMesh*>(mesh))
		{
			skinMesh->SetPalette(palette);
		}
	}

	return true;
}


int cModel::GetCollisionId() 
{
	return GetId();
}

bool cModel::IsTest( int testNum )
{
	return true;
}


void cModel::UpdateCollisionBox()
{
	m_boundingBox.SetTransform(m_TM);
	m_renderBoundingBox.SetTransform(m_TM);
}


cBoundingBox* cModel::GetCollisionBox(cRenderer &renderer)
{
	sMinMax mm;
	for each (auto &mesh in m_meshes)
	{
		if (const cBoundingBox *box = mesh->GetBoundingBox())
		{
			mm.Update(box->m_min);
			mm.Update(box->m_max);
		}
	}

	m_boundingBox.SetBoundingBox(mm._min, mm._max);
	if (m_boundingBox.Length() >= FLT_MAX)
		m_boundingBox.SetBoundingBox(Vector3(0,0,0), Vector3(0,0,0));

	m_renderBoundingBox.SetCube(renderer, m_boundingBox.m_min, m_boundingBox.m_max);
	m_renderBoundingBox.SetColor(0x0000ff00);
	return &m_boundingBox;
}


void cModel::Collision( int testNum, ICollisionable *obj )
{

}


// ¸ðµ¨À» º¹»çÇØ¼­ ¸®ÅÏÇÑ´Ù.
cModel* cModel::Clone(cRenderer &renderer) const
{
	cModel *clone = new cModel(GenerateId());
	clone->Create(renderer, m_fileName, m_type);

	clone->SetTransform(m_TM);
	clone->SetShader(m_shader);
	clone->SetRenderMesh(m_isRenderMesh);
	clone->SetRenderBone(m_isRenderBone);
	clone->SetRenderBoundingBox(m_isRenderBoundingBox);

	return clone;
}


// ½ºÅ©¸° ÁÂÇ¥ x,y ·Î ¸ðµ¨ÀÌ ¼±ÅÃÀÌ µÇ¾ú´ÂÁö ÆÇ´ÜÇÑ´Ù. ÇÇÅ·µÇ¾ú´Ù¸é true¸¦ ¸®ÅÏÇÑ´Ù.
bool cModel::Pick(const Vector3 &orig, const Vector3 &dir)
{
	m_boundingBox.SetTransform(m_TM);
	return m_boundingBox.Pick(orig, dir);
}


cMesh* cModel::GetMesh(const StrId &name)
{
	for each (auto &mesh in m_meshes)
	{
		if (mesh->GetName() == name)
			return mesh;
	}
	return NULL;	
}


cMesh* cModel::GetMesh(const int idx)
{
	if ((int)m_meshes.size() > idx)
		return m_meshes[ idx];
	return NULL;
}


void cModel::SetShader(cShader *shader)
{
	__super::SetShader(shader);

	for each (auto node in m_meshes)
		node->SetShader(shader);
}


void cModel::SetToolTransform(const sTransform& tm) 
{
	m_transform = tm; 

	Matrix44 R, S, T;
	T.SetTranslate(tm.pos);
	Quaternion q;
	q.Euler(tm.rot);
	R = q.GetMatrix();
	S.SetScale(tm.scale);

	SetTransform(S * R * T);
}
