#include "stdafx.h"
#include "GeomObject.h"
#include "SkinWeight.h"

#include <C2/graph/MaterialSamples.h>

using namespace std;


namespace geom
{


GeomObject::GeomObject(void)
{
	m_Visible        = true;
	m_VertexOnlyMode = false;

	m_ObjectIndex = -1;

	m_FileFormat = GeomFileFormat::None;
}

void GeomObject::ClearObject(void)
{
	for (MeshBuf& mbuf : m_MeshAry)
	{
		mbuf.ClearAllBuf();
	}

	m_TextureLib.Clear();

	m_Nodes.Clear();
}

const lm::range3f GeomObject::GetGeomBBox(bool for_ini) const
{
	lm::range3f r;
	for (const MeshBuf& m : m_MeshAry)
	{
		r.expand(for_ini ? m.GetBBoxIni() : m.GetBBox());
	}

	return r;
}

void GeomObject::InitializeBufferCommon(void)
{
	for (MeshBuf& mbuf : m_MeshAry)
	{
		mbuf.InitializeBuffer();

		// 面がなければ頂点モードを標準にする
		if (mbuf.m_Mesh.m_Faces.empty() && mbuf.m_Mesh.m_Polylines.empty())
			m_VertexOnlyMode = true;
	}
}


//! 形状に1対1で対応するファイルがある場合にtrueを返す.
bool GeomObject::IsFileObject(void) const
{
	if (m_FilePath.empty())
		return false;

	if (m_FileFormat == GeomFileFormat::None)
		return false;

	return true;
}


MeshBuf* GeomObject::CreateNewMeshBuf(void)
{
	MeshBuf* m = new MeshBuf(this);
	m->m_MBufIdx = (int)m_MeshAry.size();
	m_MeshAry.push_back(m);
	return m;
}


size_t GeomObject::GetNumTotalVerts(void) const
{
	size_t num = 0;
	for (const MeshBuf& mbuf : m_MeshAry)
	{
		num += mbuf.GetNumVerts();
	}
	return num;
}

size_t GeomObject::GetNumTotalFaces(void) const
{
	size_t num = 0;
	for (const MeshBuf& mbuf : m_MeshAry)
	{
		num += mbuf.GetNumFaces();
	}
	return num;
}

bool GeomObject::IsInChild(const MeshBuf* mbuf) const
{
	for (const MeshBuf& m : m_MeshAry)
	{
		if (&m == mbuf)
			return true;
	}

	return false;
}


void GeomObject::SetFrame(int frame)
{
	int a_frame = frame - 1;

	for (MeshBuf& m : m_MeshAry)
	{
		if (m.m_Bones.m_SrcVertPos.empty())
			continue;

		m.m_Mesh.m_Verts = m.m_Bones.m_SrcVertPos;
	}

	for (SceneNode& n : m_Nodes.m_RootNodes)
	{
		ApplyTrans(n, n.GetFrameTransform(a_frame), a_frame);
	}
	
	for (MeshBuf& m : m_MeshAry)
	{
		std::vector<SkinWeight> VWeights(m.m_Mesh.m_Verts.size());

		for (Bone& b : m.m_Bones.m_Bones)
		{
			for (BoneWeight& bw : b.m_Weights)
			{
				if (b.m_RefNode == NULL)
					continue;

				int vid = bw.m_Vid;
				float weight = bw.m_Weight;

				lm::vec3f v = m.m_Bones.m_SrcVertPos[vid];

				v = b.VertGolbalToLocal(v) * b.m_RefNode->m_WorldTrans;

				VWeights[vid].Add(weight, v);
			}
		}

		for (size_t j = 0; j < m.m_Mesh.m_Verts.size(); ++j)
		{
			SkinWeight& vw = VWeights[j];
			if (vw.Val > 0.0f)
				m.m_Mesh.m_Verts[j] = vw.GetAvg();
		}

		m.UpdateBBox();
		m.m_Mesh.UpdateNormal();
	}
}

void GeomObject::ApplyTrans(SceneNode& n, const lm::matrix4f& trans, int a_frame)
{
	n.m_WorldTrans = trans;

	for (int mid : n.m_MeshIDs)
	{
		MeshBuf& mb = m_MeshAry[mid];
		if (!mb.m_Bones.m_Bones.empty())
			continue;

		mb.m_Mesh.ApplyTransform(trans);
	}

	for (SceneNode& cn : n.m_Children)
	{
		ApplyTrans(cn, cn.GetFrameTransform(a_frame) * trans, a_frame);
	}
}

void GeomObject::ResetSmoothNormal(void)
{
	for (MeshBuf& m : m_MeshAry)
	{
		m.m_Mesh.CreateNormalsEachVerts();
		m.m_Mesh.UpdateNormal();
	}
}

bool GeomObject::HasMeshes(void) const
{
	return !m_MeshAry.empty();
}


}
