#include "StdAfx.h"
#include "ColladaGeom.h"

#include "ColladaFileLoader.h"


namespace lib_geo
{


bool ColladaGeom::LoadColladaFile(const char* filename)
{
	ColladaFileLoader loader;
	if( !loader.Load( filename , *this ) )
		return false;

	for( size_t i = 0 ; i < m_Geoms.size() ; ++i )
	{
		m_Geoms[i].CreateAttrib();
	}

	CreateNodeMap();

	SetNodeToGeometryReference();

	SetNodeAndBoneReference();

	InitializeTransform();

	return true;
}

void ColladaGeom::CreateNodeMap(void)
{
	for( size_t i = 0 ; i < m_Nodes.size() ; ++i )
	{
		AddNodeMap( &m_Nodes[i] );
	}
}

void ColladaGeom::AddNodeMap(ColladaNode* n)
{
	m_NodeMap[ n->m_Name ] = n;

	for( size_t i = 0 ; i < n->m_Childs.size() ; ++i )
	{
		AddNodeMap( &n->m_Childs[i] );
	}
}

// ノードにジオメトリを割り当てる
void ColladaGeom::SetNodeToGeometryReference(void)
{
	for( size_t i = 0 ; i < m_Nodes.size() ; ++i )
	{
		for( size_t j = 0 ; j < m_Geoms.size() ; ++j )
		{
			if( m_Nodes[i].m_BindTargetName == m_Geoms[j].m_Name )
			{
				m_Nodes[i].m_BindMesh = &m_Geoms[j];
				break;
			}
		}
	}

}

// ノードとボーンの相互参照を設定する
void ColladaGeom::SetNodeAndBoneReference(void)
{
	for( size_t i = 0 ; i < m_Bones.size() ; ++i )
	{
		ColladaBone& bone = m_Bones[i];
		for( size_t j = 0 ; j < bone.m_Nodes.size() ; ++j )
		{
			std::map<std::string, ColladaNode*>::iterator it;
			it = m_NodeMap.find( bone.m_Nodes[j].m_Name );
			if( it != m_NodeMap.end() )
			{
				bone.m_Nodes[j].m_Node = it->second;
			}
		}
	}

	for( size_t i = 0 ; i < m_Bones.size() ; ++i )
	{
		for( std::map<std::string, ColladaNode*>::iterator it = m_NodeMap.begin() ; it != m_NodeMap.end() ; ++it )
		{
			if( it->second->m_BindTargetName == m_Bones[i].m_Name )
				it->second->m_BindBone = &m_Bones[i];
		}
	}

}

// 変換行列を初期化する
void ColladaGeom::InitializeTransform(void)
{
	UpdateNodeTransmat();

	for( size_t i = 0 ; i < m_Nodes.size() ; ++i )
	{
		m_Nodes[i].InitializeInvmat();
	}

	UpdateVertexFromNodeTransform();

	UpdateVertexFromBoneTransform();
}


void ColladaGeom::UpdateVertexFromNodeTransform(void)
{
	for( size_t i = 0 ; i < m_Nodes.size() ; ++i )
	{
		ColladaNode& n = m_Nodes[i];
		if( n.m_BindMesh != NULL )
		{
			ColladaMesh& m = *n.m_BindMesh;
			for( size_t j = 0 ; j < m.m_Verts.size() ; ++j )
			{
				lm::vec3f& v = m.m_Verts[j];

				v = m.m_VertAttr[j].m_InitialPos;
				n.ApplyTransMat(v);
			}

			m.UpdateNormal();
		}
	}
}

void ColladaGeom::UpdateVertexFromBoneTransform(void)
{
	for( size_t i = 0 ; i < m_Bones.size() ; ++i )
	{
		ColladaBone& bone = m_Bones[i];
		ColladaMesh& part = *bone.m_SrcMesh;

		for( size_t j = 0 ; j < part.m_Verts.size() ; ++j )
		{
			lm::vec3f& v = part.m_Verts[j];
			lm::vec3f& vs = part.m_VertAttr[j].m_InitialPos;
			v = bone.m_BindShapeMat * vs;
		}

		part.UpdateNormal();
	}
}

void ColladaGeom::UpdateVertexFromBoneWeighSkinning(void)
{
	for( size_t i = 0 ; i < m_Bones.size() ; ++i )
	{
		ColladaBone& bone = m_Bones[i];
		ColladaMesh& part = *bone.m_SrcMesh;

		for( size_t j = 0 ; j < part.m_Verts.size() ; ++j )
		{
			lm::vec3f& v = part.m_Verts[j];
			v *= 0.0f;

			lm::vec3f& va = part.m_VertAttr[j].m_InitialPos;

			lm::vec3f vat = bone.m_BindShapeMat * va;

			float weight_sum = 0.0f;
			int weight_count = 0;
			lm::vec3f avg_sum;
			lm::vec3f count_sum;

			VertBondBinds& binds = bone.m_Binds[j];
			for( size_t k = 0 ; k < binds.m_Binds.size() ; ++k )
			{
				BoneBind& bind = binds.m_Binds[k];
				ColladaNode* bind_node = bone.m_Nodes[ bind.m_JointIdx ].m_Node;
				if( bind_node == NULL )
					continue;

				lm::matrix4f& mi = bind_node->m_TransMatIniInv;
				lm::matrix4f& mt = bind_node->m_TransMat;
				lm::vec3f pv = vat * mi * mt;
				avg_sum += pv * bind.m_Weight;

				count_sum += pv;

				weight_sum += bind.m_Weight;
				++weight_count;
			}

			if( weight_sum != 0.0f )
			{
				v = avg_sum / weight_sum;
			}
			else if( weight_count != 0 )
			{
				v = count_sum / (float)weight_count;
			}
			else
			{
				v = vat;
			}
		}
	}

	for( size_t i = 0 ; i < m_Geoms.size() ; ++i )
		m_Geoms[i].UpdateNormal();
}

void ColladaGeom::UpdateNodeTransmat(void)
{
	for( size_t i = 0 ; i < m_Nodes.size() ; ++i )
	{
		m_Nodes[i].UpdateTransmat();
	}
}


}
