
//////////////////////////////////////////////////////////////////////
//
//	Crytek Source code 
//	Copyright (c) Crytek 2001-2004
// 
//	File: 
//
//  Description:  
//
//	History:
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CrySkinMorphBuilder.h"
#include "CrySkinMorph.h"

CrySkinMorphBuilder::CrySkinMorphBuilder(const ICrySkinSource* pGeometry, const Matrix44* pMatInvDef, unsigned numBones):
	CrySkinBuilderBase (pGeometry),
	m_pMatInvDef(pMatInvDef),
	m_numBones(numBones)
{
}


//////////////////////////////////////////////////////////////////////////
// initializes the given skin out of the given morph target
void CrySkinMorphBuilder::initSkinMorph (const SMeshMorphTargetVertex* pMorphVerts, unsigned numMorphVerts, class CrySkinMorph* pSkin)
{
	m_pMorphVerts = pMorphVerts;
	m_numMorphVerts = numMorphVerts;
	m_pSkinTarget = pSkin;
	pSkin->m_numDests = m_pGeometry->numVertices();

	if (!numMorphVerts)
	{
		pSkin->clear();
		return;
	}

	findAffectingBoneRange();

	makeMorphBoneVertexArray();

	calculateNumMorphLinks();

	validate();

	// for aux ints, we need, for each bone:
	// 2 ints for group headers
	// for each smooth link,
	// 1 int for index of the target vertex
	unsigned numAuxInts = 2 * (m_numAffectingBones - m_nFirstAffectingBone) + m_numMorphSmoothLinks;

	pSkin->init (m_numMorphRigidLinks + m_numMorphSmoothLinks,
		numAuxInts,
		m_nFirstAffectingBone,
		m_numAffectingBones);

	CrySkinStreams stream, streamBegin, streamEnd;
	streamBegin.pAux  = pSkin->m_arrAux.begin();
	streamBegin.pVert = pSkin->m_arrVertices.begin();
	stream = streamBegin;
	streamEnd.pAux  = streamBegin.pAux  + numAuxInts;
	streamEnd.pVert = streamBegin.pVert	+ m_numMorphRigidLinks + m_numMorphSmoothLinks;

	for (unsigned nBone = m_nFirstAffectingBone; nBone < m_numAffectingBones; ++nBone)
	{
		// for each bone, fill the three groups
		// we start from the rigid vertices
		fillRigidGroup (stream, nBone);
		assert (stream.pAux <= streamEnd.pAux);
		assert (stream.pVert <= streamEnd.pVert);
		fillSmoothGroup (stream, nBone);
		// only when we processed the last bone, we should have the pAux pointing to the end
		assert (stream.pAux <= streamEnd.pAux);
		assert (stream.pVert <= streamEnd.pVert);
	}
}


//////////////////////////////////////////////////////////////////////////
// for the given morph target(source) finds and initializes the
// m_nFirstAffectingBone and m_numAffectingBones
void CrySkinMorphBuilder::findAffectingBoneRange()
{
	// init - find the first vertex binding info
	const CryVertexBinding* pLinks = &m_pGeometry->getLink(m_pMorphVerts[0].nVertexId);
	m_nFirstAffectingBone = pLinks->minBoneID();
	m_numAffectingBones = pLinks->maxBoneID()+1;

	// find all the other vertex binding info and init the bone range
	for (unsigned nMorphVert = 1; nMorphVert < m_numMorphVerts; ++nMorphVert)
	{
		unsigned nGeomVert = m_pMorphVerts[nMorphVert].nVertexId;
		pLinks = &m_pGeometry->getLink(nGeomVert);
		m_nFirstAffectingBone = min (m_nFirstAffectingBone, pLinks->minBoneID());
		m_numAffectingBones = max(m_numAffectingBones, pLinks->maxBoneID()+1);
	}

	assert (m_nFirstAffectingBone < m_numAffectingBones);
	assert (m_numAffectingBones <= m_numBones);
}


//////////////////////////////////////////////////////////////////////////
// calculates the number of rigid and smooth links
// m_numMorphRigidLinks and m_numMorphSmoothLinks
void CrySkinMorphBuilder::calculateNumMorphLinks()
{
	m_numMorphRigidLinks  = 0;
	m_numMorphSmoothLinks = 0;
	for (unsigned nMorphVert = 0; nMorphVert < m_numMorphVerts; ++nMorphVert)
	{
		unsigned nGeomVert = m_pMorphVerts[nMorphVert].nVertexId;
		const CryVertexBinding* pLinks = &m_pGeometry->getLink(nGeomVert);

		if (pLinks->size() == 1)
			++m_numMorphRigidLinks;
		else
			m_numMorphSmoothLinks += (unsigned)pLinks->size();
	}
}


//////////////////////////////////////////////////////////////////////////
// calculates the vertex list of each bone, taking only those vertices 
// present in the morph vertex array
void CrySkinMorphBuilder::makeMorphBoneVertexArray()
{
	m_arrBoneVerts.clear();
	m_arrBoneVerts.resize (m_numAffectingBones);
	for (unsigned nMorphVert = 0; nMorphVert < m_numMorphVerts; ++nMorphVert)
	{
		unsigned nGeomVert = m_pMorphVerts[nMorphVert].nVertexId;
		const CryVertexBinding* pLinks = &m_pGeometry->getLink(nGeomVert);
		// the morph vertex target in the object coordinates
		Vec3d ptMorphOffset = m_pMorphVerts[nMorphVert].ptVertex - m_pGeometry->getVertex(nGeomVert);
		
		if (pLinks->size() == 1)
		{
			unsigned nBone = (*pLinks)[0].BoneID;
			CrySkinRigidVertex v;
			v.nDest = nGeomVert;
			// transform the point into the bone coordinates

			//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
			v.pt = m_pMatInvDef[nBone].TransformVectorOLD (ptMorphOffset);
			//v.pt = GetTransposed44(m_pMatInvDef[nBone])*(ptMorphOffset);

			m_arrBoneVerts[nBone].arrRigid.push_back(v);
		}
		else
		{
			for (unsigned nLink = 0; nLink < pLinks->size(); ++nLink)
			{
				unsigned nBone = (*pLinks)[nLink].BoneID;
				CrySkinSmoothVertex v;
				v.fWeight = (*pLinks)[nLink].Blending;
				v.nDest = nGeomVert;

				v.pt = m_pMatInvDef[nBone].TransformVectorOLD (ptMorphOffset);

				m_arrBoneVerts[nBone].arrSmooth.push_back(v);
			}
		}
	}
}


void CrySkinMorphBuilder::validate()
{
#ifdef _DEBUG
	unsigned nBone;
	for (nBone = 0; nBone < m_nFirstAffectingBone;++nBone)
		assert (m_arrBoneVerts[nBone].empty());
	assert (!m_arrBoneVerts[m_nFirstAffectingBone].empty());
#endif
}


// fills in the group of aux ints for the given bone (the smooth vertex group)
// returns the pointer to the next available auxint after the group
void CrySkinMorphBuilder::fillSmoothGroup (CrySkinStreams& streams, unsigned nBone)
{
	CrySkinSmoothVertexArray& arrSmooth = m_arrBoneVerts[nBone].arrSmooth;

	// the group starts with the number of rigid vertices belonging to this bone
	*streams.pAux++ = (CrySkinAuxInt)arrSmooth.size();

	CrySkinSmoothVertexArray::const_iterator it = arrSmooth.begin(), itEnd = it + arrSmooth.size();

  for (; it != itEnd; ++it)
	{
		it->build (*streams.pVert++);
		*streams.pAux++ = it->nDest;
	}
}
