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

#include "stdafx.h"
#include "MathUtils.h"
#include "CryBoneDesc.h"
#include "StringUtils.h"

using namespace CryStringUtils;

CryBoneDesc::CryBoneDesc()
{
	m_PhysInfo[0].pPhysGeom = m_PhysInfo[1].pPhysGeom = 0;
	m_matInvDefGlobal.SetIdentity();; // allows to get difference to def pose matrices 
}

CryBoneDesc::~CryBoneDesc()
{
}

//////////////////////////////////////////////////////////////////////////
// loads the bone from a raw chunk data (no header)
// PARAMETERS:
//   pEntity - the chunk data to load the bone info from
// RETURNS:
//   false if the bone couldn't be loaded
bool CryBoneDesc::LoadRaw (const BONE_ENTITY* pEntity)
{
  //read  bone info
	assert(pEntity->nChildren<200);

	// update the lod 0 physics of this bone
	UpdatePhysics (*pEntity, 0);

	//get bone info
	m_nControllerID = pEntity->ControllerID;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// updates this bone physics, from the given entity descriptor, and of the given lod
void CryBoneDesc::UpdatePhysics (const BONE_ENTITY& entity, int nLod)
{
	assert (nLod >= 0 && nLod < SIZEOF_ARRAY(m_PhysInfo));
	CopyPhysInfo(m_PhysInfo[nLod], entity.phys);

	int nFlags = 0;
	if (entity.prop[0])
	{
		nFlags = joint_no_gravity|joint_isolated_accelerations;
	}
	else
	{
		if (!strnstr(entity.prop,"gravity", sizeof(entity.prop)))
			nFlags |= joint_no_gravity;
		
		if (!strnstr(entity.prop,"physical",sizeof(entity.prop)))
			nFlags |= joint_isolated_accelerations;
	}

	m_PhysInfo[nLod].flags |= nFlags;
}



//! Performs post-initialization. This step is requred to initialize the pPhysGeom of the bones
//! After the bone has been loaded but before it is first used. When the bone is first loaded, pPhysGeom
//!   is set to the value equal to the chunk id in the file where the physical geometry (BoneMesh) chunk is kept.
//! After those chunks are loaded, and chunk ids are mapped to the registered physical geometry objects,
//!   call this function to replace pPhysGeom chunk ids with the actual physical geometry object pointers.
//! RETURNS:
//    true if the corresponding physical geometry object has been found
//!	NOTE:
//!	The entries of the map that were used are deleted
bool CryBoneDesc::PostInitPhysGeom (ChunkIdToPhysGeomMap& mapChunkIdToPhysGeom, int nLodLevel)
{
	phys_geometry*& pPhysGeom = m_PhysInfo[nLodLevel].pPhysGeom;
	ChunkIdToPhysGeomMap::iterator it = mapChunkIdToPhysGeom.find ((INT_PTR)pPhysGeom);
	if (it != mapChunkIdToPhysGeom.end()) 
	{
		// remap the chunk id to the actual pointer to the geometry
		pPhysGeom = it->second;
		mapChunkIdToPhysGeom.erase (it);
		return true;
	} 
	else
	{
		pPhysGeom = NULL;
		return false;
	}
}

// sets the name of the bone out of the given buffer of the given max size
void CryBoneDesc::setName (const char* szName)
{
	m_strName.assign (szName);

	static const char *g_arrLimbNames[4] = { "L UpperArm","R UpperArm","L Thigh","R Thigh" };
	m_nLimbId = -1;
	for (int j=0; j < SIZEOF_ARRAY(g_arrLimbNames) ;j++)
		if (strstr(szName,g_arrLimbNames[j]))
		{
			m_nLimbId = j;
			break;
		}
}

// returns the bone name, if available
const char* CryBoneDesc::getNameCStr()const
{
	return m_strName.c_str();
}

const string& CryBoneDesc::getName()const
{
	return m_strName;
}

// compares two bone descriptions and returns true if they're the same bone
// (the same name and the same position in the hierarchy)
bool CryBoneDesc::isEqual (const CryBoneDesc& desc)const
{
	return m_strName == desc.m_strName
		&& m_nControllerID == desc.m_nControllerID
		&& m_nOffsetParent == desc.m_nOffsetParent
		&& m_numChildren == desc.m_numChildren
		&& m_nOffsetChildren == desc.m_nOffsetChildren;
}


inline size_t align4 (size_t x)
{
	return (x + 3)&~3;
}

// Serializes the description:
// returns the number of required bytes for serialization, if the data pointer is NULL
// returns 0 (if the buffer size is not enough) or the number of bytes written, if the data pointer is given
unsigned CryBoneDesc::Serialize (bool bSave, void *pStream, unsigned nSize)
{
	if (bSave)
	{
		unsigned nSizeRequired = (unsigned)(sizeof(CryBoneDescData_Comp) + align4 (m_strName.length()+1));
		if (!pStream)
			return nSizeRequired;
		if (nSize < nSizeRequired)
			return 0;

		CopyCryBone(*(CryBoneDescData_Comp*)pStream, *this);
		memcpy ((CryBoneDescData_Comp*)pStream+1,m_strName.c_str(),m_strName.length()+1);
		return nSizeRequired;
	}
	else
	{
		if (!pStream)
			return 0;
		if (nSize < sizeof(CryBoneDescData_Comp)+1)
			return 0;
		if (nSize & 3)
			return 0; // alignment error
		CopyCryBone(*this, *(CryBoneDescData_Comp*)pStream);
		// end of the stream
		const char* pEnd = (const char*)pStream + nSize;
		// the start byte of the bone name
		const char* pName = (const char*)((CryBoneDescData_Comp*)pStream+1);

		// scan until the end of the name (\0 char) or the stream (pEnd address)
		const char *pNameEnd;
		for (pNameEnd = pName; pNameEnd < pEnd && *pNameEnd; ++pNameEnd);
		m_strName.assign (pName, pNameEnd);
		// return aligned size of the chunk including the string 0 terminator, but
		// no more than the declared size of the stream
		return min(nSize, (unsigned)align4 (pNameEnd+1-(const char*)pStream));
	}
}

void CryBoneDesc::setDefaultGlobal(const Matrix44& matDefault)
{
	m_matInvDefGlobal=OrthoUniformGetInverted (matDefault);
}


// scales the bone with the given multiplier
void CryBoneDesc::scale (float fScale)
{
	m_matInvDefGlobal.SetTranslationOLD(m_matInvDefGlobal.GetTranslationOLD()*fScale);
}
