//
// Asura_Hierarchy_System.cpp
//
// Created by Richard Rice - 17/02/2003
// 
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Animation/Asura_Hierarchy.h"

// Engine independent.
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Animation_Posable.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_NameTree.h"
#include "Core/Asura_Timers.h"
#include "Core/Asura_Mempool.h"
#include "Core/Memory/Asura_Mem.h"
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_DebugText.h"
#include "Mesh/Asura_CollisionMesh.h"
#ifdef ASURA_TOOLS
#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#endif
#include "Rendering/Asura_Module.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_SortedRender.h"
#ifdef ASURA_USE_INDEPENDENT_SHADOW_SYSTEM
#include "Shadows/Asura_Shadow_System.h"
#endif

/**************/
/* Prototypes */
/**************/

/************************/
/* Static & Global Data */
/************************/


bool Asura_Hierarchy_System::s_bJustRenderForCurCam = false;
bool Asura_Hierarchy_System::s_bNoCulling = false;
bool Asura_Hierarchy_System::s_bBoneUpdatesEnabled = true;

#if defined(ASURA_TOOLS)
Asura_Hierarchy_Skin*		Asura_Hierarchy_System::s_pxSkin;
Asura_Mesh*					Asura_Hierarchy_System::s_pxMesh;
#else
const Asura_Hierarchy_Skin*	Asura_Hierarchy_System::s_pxSkin;
#endif	//defined(ASURA_TOOLS)
u_int						Asura_Hierarchy_System::s_uSkinIndex;
Asura_Hierarchy_Tween*		Asura_Hierarchy_System::s_pxTweenStorage;
bool						Asura_Hierarchy_System::s_bBonesInWorldspace;

#ifdef ASURA_HIERARCHY_QUAD
	Asura_AMatrix4x4	Asura_Hierarchy_System::s_axBoneTransforms[ ASURA_MAX_NO_OF_BONES_PER_MODEL ];
	Asura_AMatrix4x4	Asura_Hierarchy_System::s_xBaseTransform;
	Asura_AMatrix4x4	Asura_Hierarchy_System::s_xInverseBaseTransform;
	Asura_Vector_3		Asura_Hierarchy_System::s_xOrigPosition;
	Asura_Matrix_3x3	Asura_Hierarchy_System::s_xOrigOrient;
#else
	const Asura_Matrix_3x3* Asura_Hierarchy_System::s_pxOri;
	const Asura_Vector_3*   Asura_Hierarchy_System::s_pxPos;

	Asura_Matrix_3x3 		Asura_Hierarchy_System::s_xAnimOrient;
	Asura_Vector_3 			Asura_Hierarchy_System::s_xAnimPos;

	Asura_Vector_3          Asura_Hierarchy_System::s_axBonePos[ASURA_MAX_NO_OF_BONES_PER_MODEL];
	Asura_Matrix_3x3        Asura_Hierarchy_System::s_axBoneOri[ASURA_MAX_NO_OF_BONES_PER_MODEL];
#endif

/*************/
/* Functions */
/*************/

#if ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES
#define ASURA_USE_FAST_COMPRESSED_KEYFRAME_ACCESS 0
#else
#define ASURA_USE_FAST_COMPRESSED_KEYFRAME_ACCESS 1
#endif

// macro to tidy up the code below
#if ASURA_USE_FAST_COMPRESSED_KEYFRAME_ACCESS 
#define Asura_GetKeyFrameTime(pxAnim, iBone, iFrame) Asura_Hierarchy_Anim::FastGetCompressedKeyFrameTime(usCompressedTimeKeyFrames, uBoneFirstKeyframeIndex, iFrame)
#else
#define Asura_GetKeyFrameTime(pxAnim, iBone, iFrame) pxAnim->GetKeyFrameTime(iBone, iFrame)
#endif

bool Asura_Hierarchy_System::BonesToWorldspaceFromTweenStorage()
{
	const Asura_Hierarchy_Skin* pxSkin = Asura_Hierarchy_System::s_pxSkin;
	Asura_Assert( NULL != pxSkin, "A Skin would be nice");
	if( NULL == pxSkin)
	{
		return( false);
	}
	
	Asura_Hierarchy_Tween* pxTweenStorage = Asura_Hierarchy_System::s_pxTweenStorage;
	Asura_Assertf( NULL != pxTweenStorage, ("Tween Storage would be nice. Skin '%s'", pxSkin->Name.GetString()));
	if( NULL == pxTweenStorage)
	{
		return( false);
	}
	
    const int                             iNumBones	= pxSkin->NumberOfBones;
	Asura_Assertf( iNumBones <= ASURA_MAX_NO_OF_BONES_PER_MODEL, ("Too many bones in skin '%s'", pxSkin->Name.GetString()));
	
	if(!pxTweenStorage->HasBoneData())
	{
		Asura_Assertf( false, ("No bones in! Skin '%s'", pxSkin->Name.GetString()));
		return( false);
	}
	
#ifdef ASURA_HIERARCHY_QUAD
	// RJT TODO: If platform specific the VFPU write-back cache could be used here for PSP
	if( pxTweenStorage->IsPositionDataInWorldspace() )
	{
		for( int i = 0; i < iNumBones; ++i )
		{
			pxTweenStorage->GetBoneData(i).GetTransform(s_axBoneTransforms[i]);
		}
	}
	else if( iNumBones > 0 )
	{
		const int*	piBoneParents = pxSkin->ParentBone;
		Asura_Assertf( NULL != piBoneParents, ("No bones parent indices! Skin '%s'", pxSkin->Name.GetString()));

		pxTweenStorage->GetBoneData(0).GetTransform(s_axBoneTransforms[0]);

		//Combine the bone 0 pos/orient into the animation world pos/orient. RAR 09/10/2007
		s_xBaseTransform.Apply(s_axBoneTransforms[0], s_xBaseTransform);
		s_xBaseTransform.InvertOrthogonal(s_xInverseBaseTransform);
		s_axBoneTransforms[0].Identity();


		for( int i = 1; i < iNumBones; ++i )
		{
			const int			iParentIndex	= piBoneParents[ i ];
			Asura_AMatrix4x4	xTemp;

			Asura_Assert( ( 0 <= iParentIndex ) && ( iParentIndex < i ), "" );

			pxTweenStorage->GetBoneData(i).GetTransform(xTemp);
			s_axBoneTransforms[iParentIndex].Apply(xTemp, s_axBoneTransforms[i]);
		}
	}

	s_bBonesInWorldspace = pxTweenStorage->IsPositionDataInWorldspace();
#else	// ASURA_HIERARCHY_QUAD
	Asura_Vector_3*    pxPosOut	= Asura_Hierarchy_System::s_axBonePos;
	Asura_Matrix_3x3*  pxOriOut	= Asura_Hierarchy_System::s_axBoneOri;

	if( pxTweenStorage->IsPositionDataInWorldspace())
	{
		for(int iBone = 0; iBone < iNumBones; ++iBone)
		{
			const Asura_Hierarchy_TweenStore_BoneData&	xBoneData	= pxTweenStorage->GetBoneData(iBone);

			xBoneData.GetPosition(pxPosOut[iBone]);
			xBoneData.GetOrientation(pxOriOut[iBone]);
		}


		Asura_Hierarchy_System::s_bBonesInWorldspace = true;
	}
	else
	{
		const int*	piBoneParents = pxSkin->ParentBone;
		Asura_Assertf( NULL != piBoneParents, ("No bones parent indices! Skin '%s'", pxSkin->Name.GetString()));

		for( int iBone = 0; iBone < iNumBones; ++iBone )
		{
			const Asura_Hierarchy_TweenStore_BoneData&	xBoneData	= pxTweenStorage->GetBoneData(iBone);

			Asura_Vector_3								xPos;
			Asura_Matrix_3x3							xOriMat;

			xBoneData.GetOrientation(xOriMat);
			xBoneData.GetPosition(xPos);

			if( iBone )
			{
				const int iParentIndex = piBoneParents[iBone];
				Asura_Assert( 0 <= iParentIndex && iParentIndex < iBone, "" );

				pxPosOut[iBone] = xPos * pxOriOut[iParentIndex] + pxPosOut[iParentIndex];
				pxOriOut[iBone] = pxOriOut[iParentIndex] * xOriMat;
			}
			else
			{
				//Combine the bone 0 pos/orient into the animation world pos/orient. RAR 09/10/2007
				s_xAnimPos = xPos * s_xAnimOrient + s_xAnimPos;
				s_xAnimOrient = s_xAnimOrient * xOriMat;
				
				pxPosOut[iBone] = Asura_Maths::ZeroVector3;
				pxOriOut[iBone] = Asura_Maths::IdentityMatrix3x3;
			}
		}

		Asura_Hierarchy_System::s_bBonesInWorldspace = false;
	}
#endif	// ASURA_HIERARCHY_QUAD
	
	return( true);
}

#ifdef ASURA_USE_KEYFRAME_CACHING
void Asura_Hierarchy_Anim::GetRelativeBonePosition(int iBone,float fTimer,Asura_Vector_3* pxOutputPos,Asura_Quat* pxOutputQuat, Asura_Cached_KeyFrame* pxCachedKeyFrame) const
#else//def ASURA_USE_KEYFRAME_CACHING
void Asura_Hierarchy_Anim::GetRelativeBonePosition(int iBone,float fTimer,Asura_Vector_3* pxOutputPos,Asura_Quat* pxOutputQuat) const
#endif//def ASURA_USE_KEYFRAME_CACHING
{
	Asura_Assert(iBone>=0 && iBone<GetBoneArraySize(),"Invalid bone index");
	
	const int iNumberOfKeyFrames = GetNumberOfKeyFrames(iBone);

	if (iNumberOfKeyFrames == 1)
	{
		// (MH - 30.07.2003) compressing the quats causes problems with single-frame posed anims
		// which require an accurate representation of the rotation.
		// This slower version gets the uncompressed quat if it's available.
		//anim->GetKeyFrameQuat(q, bone, 0);
		if(pxOutputQuat)
		{
			SlowGetKeyFrameQuat(*pxOutputQuat, iBone, 0);
		}
		if(pxOutputPos)
		{
			*pxOutputPos = GetKeyFramePosition(iBone,0);
		}
		return;
	}

#if ASURA_USE_FAST_COMPRESSED_KEYFRAME_ACCESS
	const u_short* usCompressedTimeKeyFrames = GetCompressedTimeKeyFrames();
	const u_int uBoneFirstKeyframeIndex = GetBoneFirstKeyframeIndex( iBone );
#endif

#ifdef ASURA_USE_KEYFRAME_CACHING
	if(pxCachedKeyFrame)
	{
		// Pre-fetch this key-frame AND the next one...
		Asura_Mem::PreFetchData(pxCachedKeyFrame, 2 * sizeof(*pxCachedKeyFrame));

		// Are we still between the same keyframes?
		bool bCacheIsValid = false;
		if(pxCachedKeyFrame->m_fTime1 <= fTimer && fTimer <= pxCachedKeyFrame->m_fTime2)
		{
			// Yep - nothing to do.
			bCacheIsValid = true;
		}
		else if(pxCachedKeyFrame->m_fTime2 <= fTimer && fTimer <= pxCachedKeyFrame->m_fTime3)
		{
			// Between next two keyframes. Update the cache and continue.
			++pxCachedKeyFrame->m_iIndex1;
			pxCachedKeyFrame->m_xQuat1 = pxCachedKeyFrame->m_xQuat2;
			GetKeyFrameQuat(pxCachedKeyFrame->m_xQuat2, iBone, pxCachedKeyFrame->m_iIndex1 + 1);
			pxCachedKeyFrame->m_xPos1 = pxCachedKeyFrame->m_xPos2;
			pxCachedKeyFrame->m_xPos2 = GetKeyFramePosition(iBone, pxCachedKeyFrame->m_iIndex1 + 1);
			pxCachedKeyFrame->m_fTime1 = pxCachedKeyFrame->m_fTime2;
			pxCachedKeyFrame->m_fTime2 = pxCachedKeyFrame->m_fTime3;
			// Deal with the case that these are the last two keyframes.
			if((pxCachedKeyFrame->m_iIndex1 + 2) < iNumberOfKeyFrames)
			{
				pxCachedKeyFrame->m_fTime3 = Asura_GetKeyFrameTime(this, iBone, pxCachedKeyFrame->m_iIndex1 + 2);
			}
			else
			{
				pxCachedKeyFrame->m_fTime3 = 1.f;
			}
			// Now the cache is correct
			bCacheIsValid = true;
		}
		if(bCacheIsValid)
		{
			const float fTimeSlice = Asura_Maths::ClampToRange((fTimer - pxCachedKeyFrame->m_fTime1) / (pxCachedKeyFrame->m_fTime2 - pxCachedKeyFrame->m_fTime1), 0.f, 1.f);
			if(pxOutputQuat)
			{
				pxOutputQuat->Slerp(pxCachedKeyFrame->m_xQuat1, pxCachedKeyFrame->m_xQuat2, fTimeSlice);
			}
			if(pxOutputPos)
			{
				// Lerp between positions
				*pxOutputPos = (pxCachedKeyFrame->m_xPos1) * (1.0f - fTimeSlice) + pxCachedKeyFrame->m_xPos2 * (fTimeSlice);
			}
			// All done.
			return;
		}
		// If cache isn't valid, continue on to do the full search, then fill it out.
	}
#endif//def ASURA_USE_KEYFRAME_CACHING

	int   iFrame;
	int   iNextFrame;
    float fFrameTimeOfQuat0;
    float fFrameTimeOfQuat1;

    // Binary search for correct key frame.
    {
        iFrame            = 0;
        iNextFrame        = iNumberOfKeyFrames - 1;
        fFrameTimeOfQuat0 = 0.0f; // Asura_GetKeyFrameTime( this, iBone, iFrame     );
        fFrameTimeOfQuat1 = 1.0f; // Asura_GetKeyFrameTime( this, iBone, iNextFrame );

        // At each iteration, determine which half of the remaining range
        // contains the desired key frame and recurse into that.
        while( true )
        {
            int iMiddle = (iFrame + iNextFrame) >> 1;

            if( iMiddle == iFrame )
            {
                // iNextFrame == iFrame + 1 and the key frame in question
                // lies somewhere between them so we're done
                break;
            }

            float fMiddleFrameTime = Asura_GetKeyFrameTime( this, iBone, iMiddle );
            if( fMiddleFrameTime > fTimer )
            {
                // Recurse into first half.
                iNextFrame        = iMiddle;
                fFrameTimeOfQuat1 = fMiddleFrameTime;
            }
            else
            {
                // Recurse into second half.
                iFrame            = iMiddle;
                fFrameTimeOfQuat0 = fMiddleFrameTime;
            }
        }
    }
	const float fTimeSlice = Asura_Maths::ClampToRange((fTimer - fFrameTimeOfQuat0) / (fFrameTimeOfQuat1 - fFrameTimeOfQuat0), 0.f, 1.f);

#ifdef ASURA_USE_KEYFRAME_CACHING
	if(pxCachedKeyFrame)
	{
		pxCachedKeyFrame->m_iIndex1 = iFrame;
		// Fill out quats
		GetKeyFrameQuat(pxCachedKeyFrame->m_xQuat1,iBone,iFrame);
		GetKeyFrameQuat(pxCachedKeyFrame->m_xQuat2,iBone,iNextFrame);
		if(pxOutputQuat)
		{
			pxOutputQuat->Slerp(pxCachedKeyFrame->m_xQuat1,pxCachedKeyFrame->m_xQuat2,fTimeSlice);
		}
		// Fill out positions
		pxCachedKeyFrame->m_xPos1 = GetKeyFramePosition(iBone, iFrame);
		pxCachedKeyFrame->m_xPos2 = GetKeyFramePosition(iBone, iNextFrame);
		if(pxOutputPos)
		{
			// Lerp between positions
			*pxOutputPos = (pxCachedKeyFrame->m_xPos1) * (1.0f - fTimeSlice) + pxCachedKeyFrame->m_xPos2 * (fTimeSlice);
		}
		// Fill out times
		pxCachedKeyFrame->m_fTime1 = Asura_GetKeyFrameTime(this, iBone, iFrame);
		pxCachedKeyFrame->m_fTime2 = Asura_GetKeyFrameTime(this, iBone, iNextFrame);
		if(iNextFrame + 1 < iNumberOfKeyFrames)
		{
			pxCachedKeyFrame->m_fTime3 = Asura_GetKeyFrameTime(this, iBone, iNextFrame + 1);
		}
		else
		{
			pxCachedKeyFrame->m_fTime3 = 1.f;
		}
		return;
	}
	
#endif//def ASURA_USE_KEYFRAME_CACHING
	if(pxOutputQuat)
	{
		Asura_Quat xQuat1,xQuat2;

		GetKeyFrameQuat(xQuat1,iBone,iFrame);
		GetKeyFrameQuat(xQuat2,iBone,iNextFrame);

		pxOutputQuat->Slerp(xQuat1,xQuat2,fTimeSlice);
	}

	if(pxOutputPos)
	{
		const Asura_Vector_3& xPos1 = GetKeyFramePosition(iBone, iFrame);
		const Asura_Vector_3& xPos2 = GetKeyFramePosition(iBone, iNextFrame);
		// Lerp between positions
		*pxOutputPos = (xPos1) * (1.0f - fTimeSlice) + xPos2 * (fTimeSlice);
	}

}

bool Asura_Hierarchy_Anim::GetMovementOffset(const Asura_Hierarchy_Skin* pxSkin,float fTimer,Asura_Vector_3* pxOutput,bool bMirrored /*=false*/) const
{
	if(!m_bHasMovementData) return false;
	if(!pxOutput) return false;

	GetRelativeBonePosition(GetMovementBoneIndex(),fTimer,pxOutput,0);

	if(pxOutput && pxSkin)
	{
		//Scale movement according to the relative length of the first bone in the animation and the skin.
		//RAR  23/11/2006
		*pxOutput *= GetMovementScale(pxSkin);
	}

	if(bMirrored)
	{
		//apply mirroring
		if(pxOutput)
		{
			pxOutput->x = -pxOutput->x;
		}
	}

	return true;
}

bool Asura_Hierarchy_Anim::GetMovementOffset(const Asura_Hierarchy_Skin* pxSkin,float fTimer,Asura_Vector_3* pxOutputPos,Asura_Quat* pxOutputQuat, bool bMirrored /*=false*/) const
{
	if(!m_bHasMovementData) return false;

	GetRelativeBonePosition(GetMovementBoneIndex(),fTimer,pxOutputPos,pxOutputQuat);
	if(pxOutputPos && pxSkin)
	{
		//Scale movement according to the relative length of the first bone in the animation and the skin.
		//RAR  23/11/2006
		*pxOutputPos *= GetMovementScale(pxSkin);
	}
	if(bMirrored)
	{
		//apply mirroring
		if(pxOutputPos)
		{
			pxOutputPos->x = -pxOutputPos->x;
		}
		if(pxOutputQuat)
		{
			pxOutputQuat->y = -pxOutputQuat->y;
			pxOutputQuat->z = -pxOutputQuat->z;
		}
	}
	return true;
}


bool Asura_Hierarchy_Anim::GetVelocity(const Asura_Hierarchy_Skin* pxSkin,float fTimer,float fDuration,Asura_Vector_3* pxOutput, bool bMirrored /*=false*/,bool bReversed /*=false*/) const
{
	if(!m_bHasMovementData) return false;
	if(!pxOutput) return false;

	Asura_Assert(fDuration,"Can't get velocity without a duration");
	if(!fDuration) return false;

	Asura_Assert(fTimer>= 0.0f && fTimer<= 1.0f,"");
	if(fTimer<0.0f) fTimer = 0.0f;
	if(fTimer>1.0f) fTimer = 1.0f;

	int iFrame;
	int iNextFrame;
	int iBone = GetMovementBoneIndex();
	int iNumberOfKeyFrames = GetNumberOfKeyFrames(iBone);

	if (iNumberOfKeyFrames == 1)
	{
		pxOutput->Zero();
		return true;
	}

#if ASURA_USE_FAST_COMPRESSED_KEYFRAME_ACCESS
	const u_short* usCompressedTimeKeyFrames = GetCompressedTimeKeyFrames();
	const u_int uBoneFirstKeyframeIndex = GetBoneFirstKeyframeIndex(iBone);
#endif

	float fTime0 = Asura_GetKeyFrameTime(this, iBone, 0);
	float fTime1 = Asura_GetKeyFrameTime(this, iBone, 1);
    for (iFrame = 0, iNextFrame = iFrame + 1; 
		iFrame < iNumberOfKeyFrames - 2; 
		++iFrame, ++iNextFrame, fTime1 = Asura_GetKeyFrameTime(this, iBone, iNextFrame))
	{
		if (fTime1 > fTimer) break;
		fTime0 = fTime1;
	}

	const Asura_Vector_3& p1 = GetKeyFramePosition(iBone, iFrame);
	const Asura_Vector_3& p2 = GetKeyFramePosition(iBone, iNextFrame);
	
	*pxOutput = (p2-p1) / ((fTime1-fTime0)*fDuration);

	if(pxSkin)
	{
		//Scale movement according to the relative length of the first bone in the animation and the skin.
		//RAR  23/11/2006
		*pxOutput *= GetMovementScale(pxSkin);
	}

	if(bReversed)
	{
		//if reversed , then negate the velocity
		*pxOutput = *pxOutput * -1.0f ;
	}
	if(bMirrored)
	{
		//if mirrored, x-flip velocity
		pxOutput->x = -pxOutput->x;
	}


	return true;
}

bool Asura_Hierarchy_Anim::GetAngularVelocity(float fTimer,float fDuration,Asura_Vector_3* pxOutputAxis,Asura_Angle* pfOutputAngle, bool bMirrored /*=false*/,bool bReversed /*=false*/) const
{
	if(!m_bHasMovementData) return false;
	if(!pxOutputAxis || !pfOutputAngle) return false;

	Asura_Assert(fDuration,"Can't get velocity without a duration");
	if(!fDuration) return false;

	Asura_Assert(fTimer>= 0.0f && fTimer<= 1.0f,"");
	if(fTimer<0.0f) fTimer = 0.0f;
	if(fTimer>1.0f) fTimer = 1.0f;

	int iFrame;
	int iNextFrame;
	int iBone = GetMovementBoneIndex();
	int iNumberOfKeyFrames = GetNumberOfKeyFrames(iBone);

	if (iNumberOfKeyFrames == 1)
	{
		pxOutputAxis->x = 0.0f;
		pxOutputAxis->y = 1.0f;
		pxOutputAxis->x = 0.0f;
		*pfOutputAngle = 0.0f;
		return true;
	}

#if ASURA_USE_FAST_COMPRESSED_KEYFRAME_ACCESS
	const u_short* usCompressedTimeKeyFrames = GetCompressedTimeKeyFrames();
	const u_int uBoneFirstKeyframeIndex = GetBoneFirstKeyframeIndex(iBone);
#endif

	float fTime0 = Asura_GetKeyFrameTime(this, iBone, 0);
	float fTime1 = Asura_GetKeyFrameTime(this, iBone, 1);
    for (iFrame = 0, iNextFrame = iFrame + 1; 
		iFrame < iNumberOfKeyFrames - 2; 
		++iFrame, ++iNextFrame, fTime1 = Asura_GetKeyFrameTime(this, iBone, iNextFrame))
	{
		if (fTime1 > fTimer) break;
		fTime0 = fTime1;
	}

	//get the difference in quats between the two frames
	Asura_Quat xQuat1,xQuat2;
	GetKeyFrameQuat(xQuat1,iBone,iFrame);
	GetKeyFrameQuat(xQuat2,iBone,iNextFrame);

	xQuat1.Invert();
	Asura_Quat xQuatDiff = xQuat1 * xQuat2;

	//extract axis and angle
	*pfOutputAngle	 = xQuatDiff.ToRotation(*pxOutputAxis);
	*pfOutputAngle	/= ((fTime1-fTime0)*fDuration);

	if(bReversed)
	{
		//if reversed , then negate the angle
		*pfOutputAngle = -*pfOutputAngle;
	}
	if(bMirrored)
	{
		//if mirrored, x-flip rotation
		pxOutputAxis->y = -pxOutputAxis->y;
		pxOutputAxis->z = -pxOutputAxis->z;
	}


	return true;
}



#ifdef ASURA_TOOLS

static void CalculateSkinNormalData(void)
{
    Asura_Hierarchy_Skin*   pxSkin		= Asura_Hierarchy_System::s_pxSkin;
    Asura_Mesh*             pxMesh				= Asura_Hierarchy_System::s_pxMesh;
#if defined(ASURA_HIERARCHY_QUAD)
	const Asura_AMatrix4x4*	pxBoneTransforms	= pxSkin->m_pxBoneRestPoseTransforms;
#else
    const Asura_Matrix_3x3* pxBoneOriInverse	= pxSkin->BoneRestOrientation;
    const Asura_Vector_3*   pxBonePos			= pxSkin->BoneRestPosition;
#endif	// defined(ASURA_HIERARCHY_QUAD)

	Asura_Assert(pxSkin->NumberOfVertices == pxMesh->NumberOfVertices,"Number of vertices in skin and mesh must match");
	
	if( !pxSkin->NormalData )
	{
		Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
		{
			Asura_ResourceAllocateArray(pxSkin->NormalData, pxMesh->NumberOfPolygons);
		}
		Asura_ResourcePopType();
	}

#if defined(ASURA_HIERARCHY_QUAD)
	pxSkin->CalcRestPoseBone( 0, Asura_Maths::IdentityAMatrix4x4 );
#else
    pxSkin->CalcRestPoseBone( 0, Asura_Maths::IdentityMatrix3x3, Asura_Maths::ZeroVector3 );
#endif	// defined(ASURA_HIERARCHY_QUAD)

	for (int v=0; v<pxSkin->NumberOfVertices; v++)
	{
		pxMesh->VertexDataPtr[v].Position.Zero();

		for (int b=0; b<4; b++)
		{
			float weight = pxSkin->VertexData[v].Weight[b];
			if (weight==-1.0f) break;
			
			int bone = pxSkin->VertexData[v].BoneID[b];

#if defined(ASURA_HIERARCHY_QUAD)
			Asura_Vector_3			vertex;
			const Asura_AMatrix4x4&	xTransform	= pxBoneTransforms[bone];
			const Asura_AVector4	xVertex(pxSkin->VertexData[v].Offset[b], 1.0f);
			Asura_AVector4			xTransformedVertex = xTransform.Apply(xVertex);

			vertex.SetX(xTransformedVertex.GetX());
			vertex.SetY(xTransformedVertex.GetY());
			vertex.SetZ(xTransformedVertex.GetZ());
#else
			const Asura_Matrix_3x3& xInvOri = pxBoneOriInverse[bone];
            const Asura_Vector_3&   p = pxBonePos[bone];

			Asura_Vector_3 vertex = xInvOri.ApplyTranspose(pxSkin->VertexData[v].Offset[b]) + p;
#endif	// defined(ASURA_HIERARCHY_QUAD)
		
			vertex*=weight;
			
			pxMesh->VertexDataPtr[v].Position += vertex;
		}
	//	pxSkin->VertexData[v].Offset[7] = GHMesh->VertexDataPtr[v].Position;
		
	}
	pxMesh->CalculateBoundingBox();

	//Changed so that normals will always be recalculated  , at least for the moment,
	//This is to get round the problem of meshes with incorrect normals saved out as a
	//result of using the PC renderer for more than 28 bones (which overwrites the mesh normals)
	//RAR 15/01/03
	//if (!pxMesh->NormalsDataValid)
	{
		pxMesh->CreateNormals();
	}

	for (int i=0; i<pxMesh->NumberOfPolygons; i++)
	{
		for (int n=0; n<4; n++)
		{
			uint16 vertex = pxMesh->PolygonDataPtr[i].VertexID[n];
			if (vertex!=0xffff)
			{
				pxSkin->NormalData[i].VertexNormal[n]= pxMesh->NormalsDataPtr[i].VertexNormal[n];//*BoneRestOrientation[bone];
			}	 
		}
	}
}


void Asura_Hierarchy_Skin::CalculateSkinNormalData()
{
	if(!IsSkinnedModel()) return;
	Asura_Mesh* pxMesh = Asura_Mesh_System::GetMesh(Name);
	if(!pxMesh)
	{
		Asura_Assert(0,"Failed to find mesh for skin");
		return;
	}
	
	Asura_Hierarchy_System::s_pxSkin = this;
	Asura_Hierarchy_System::s_pxMesh = pxMesh;
    
	::CalculateSkinNormalData();
}

// (MH - 02.10.2002) creation of bounding boxes for skinned and non-skinned hierarchies.
// Non-skinned ones just use the BB from each section.
// Skinned ones are done according to the settings below which can be played with in debug
// builds for testing. I found that to get coverage with no gaps, especially in the chest
// area, it is necessary to count vertices as belonging to a bone if their weight is 1/3 or 
// more. This might end up a per-project setting, or maybe be something tweakable in tools.
DEBUGVAR float fMinWeightToContributeToBoneBB = 0.33f;
DEBUGVAR int dbg_iMinVertsToMakeBB = 2;
DEBUGVAR bool dbg_bUseFirstWeightForBB = false;
bool Asura_Hierarchy_Skin::InitSectionBoundingBoxes()
{
    Asura_Assert(!m_pxSectionBoundingBoxData, "m_pxSectionBoundingBoxData already initialised");
	Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
	{
		Asura_ResourceAllocateArray(m_pxSectionBoundingBoxData, NumberOfBones);
	}
	Asura_ResourcePopType();

	if( !m_pxSectionBoundingBoxData )
    {
        return false;
    }

    if (IsSkinnedModel())
    {
        if (dbg_bUseFirstWeightForBB)
        {
            for (int iVertexID = 0; iVertexID < NumberOfVertices; iVertexID++)
	        {
                // check the weights are sensible
				#if defined ASURA_ASSERT
					float fWeight0 = VertexData[iVertexID].Weight[0];
				#endif

                Asura_Assert(fWeight0 != 0.0f, "invalid first weight");
                Asura_Assert(fWeight0 != -1.0f, "invalid first weight");
		        
                for (int b = 1; b < 4; b++)
		        {
			        float fWeight = VertexData[iVertexID].Weight[b];
			        if (fWeight == -1.0f) break;
			        Asura_Assert(fWeight <= fWeight0, "weights not sorted");
		        }

                // check the bone ID for first weighting is sensible
                int iBone = VertexData[iVertexID].BoneID[0];
                Asura_Assert(iBone < NumberOfBones, "invalid bone ID");

                const Asura_Vector_3& xBoneSpacePos = VertexData[iVertexID].Offset[0];
                m_pxSectionBoundingBoxData[iBone].m_xBB.Set(xBoneSpacePos);
            }

            // check through all the bounding boxes, setting any which have non-zero area as valid.
            // (MP - 14.02.2003) Don't want bone 0 to have a valid BB...
            for (int iBone = 1; iBone < NumberOfBones; iBone++)
            {
                const Asura_Bounding_Box& xBB = m_pxSectionBoundingBoxData[iBone].m_xBB;
                int iNumAxes = 0;
                if (xBB.MinX < xBB.MaxX) iNumAxes++;
                if (xBB.MinY < xBB.MaxY) iNumAxes++;
                if (xBB.MinZ < xBB.MaxZ) iNumAxes++;
                if (iNumAxes >= 2)
                {
                    m_pxSectionBoundingBoxData[iBone].m_uValid = 1;
                }
            }
        }
        else
        {
            // for each bone set the bounding box from all vertices with a weight greater than some threshold
            // (MP - 14.02.2003) Don't want bone 0 to have a valid BB...
            for (int iBone = 1; iBone < NumberOfBones; iBone++)
            {
                int iNumVertsContributing = 0;
	            for (int iVertexID = 0; iVertexID < NumberOfVertices; iVertexID++)
	            {
		            for (int b = 0; b < 4; b++)
		            {
			            float fWeight = VertexData[iVertexID].Weight[b];
			            if (fWeight == -1.0f) break;
			            if (fWeight >= fMinWeightToContributeToBoneBB)
                        {
			                if (VertexData[iVertexID].BoneID[b] == iBone)
                            {
                                const Asura_Vector_3& xBoneSpacePos = VertexData[iVertexID].Offset[b];
                                m_pxSectionBoundingBoxData[iBone].m_xBB.Set(xBoneSpacePos);
                                iNumVertsContributing++;
                            }
                        }
		            }
	            }
                if (iNumVertsContributing >= dbg_iMinVertsToMakeBB)
                {
                    // some bones (e.g. zeroth bone, turret) don't have any vertices.
                    // These will be ignored since their m_uValid will be false.
                    m_pxSectionBoundingBoxData[iBone].m_uValid = 1;
                }
            }
        }
    }
    else// non-skinned hierarchy. May want to add a collision mesh to section data and use that instead.
    {
        for (int iBone = 0; iBone < NumberOfBones; iBone++)
        {
            Asura_Assert(SectionData, "hierarchy has no section data");
            if (!SectionData) continue;
			//RAR 29/11/02 -  Assert removed , since I think there are occasions where having a section without a mesh would be useful...
            //Asura_Assert(SectionData[iBone].Mesh, "hierarchy section has no mesh");
            if (!SectionData[iBone].Mesh) continue;
            if (!SectionData[iBone].Mesh->BoundingBoxDataValid)
            {
                SectionData[iBone].Mesh->CalculateBoundingBox();
            }
            m_pxSectionBoundingBoxData[iBone].m_xBB.Set(SectionData[iBone].Mesh->BoundingBox);
            m_pxSectionBoundingBoxData[iBone].m_uValid = 1;


        }
    }

	//check for any bounding boxes that should be overridden. RAR 16/07/04
	//moved this so it can be used for object hierarchies as well. RAR 01/02/2007
	if(m_pxManualSectionBoundingBoxData)
	{
		for(int iBone = IsSkinnedModel() ? 1 : 0;iBone < NumberOfBones; iBone++)
		{
			if(m_pxManualSectionBoundingBoxData[iBone].m_uValid)
			{
				m_pxSectionBoundingBoxData[iBone].m_xBB = m_pxManualSectionBoundingBoxData[iBone].m_xBB;
				m_pxSectionBoundingBoxData[iBone].m_uValid = m_pxSectionBoundingBoxData[iBone].m_xBB.GetVolume() > 0.0f;
			}
		}
	}


    SetupBB();

    return true;
}

#endif // #ifdef ASURA_TOOLS

void Asura_Hierarchy_System::RenderAnimation( Asura_Animation* pxAnim, 
                                              const Asura_Vector_3& xPos, 
                                              const Asura_Matrix_3x3& xOri,
                                              u_int uRenderFlags )
{
	s_pxSkin		= pxAnim->GetSkin(0);
	s_uSkinIndex	= 0;
    if( !s_pxSkin ) { return; }

    s_pxTweenStorage = pxAnim->GetTweenStore();
	if(!s_pxTweenStorage) return;

#ifdef ASURA_TOOLS
	s_pxMesh = NULL;
#endif

#ifdef ASURA_HIERARCHY_QUAD
	// RJT TODO: Ultimately this transform should be passed in
	s_xBaseTransform = Asura_AMatrix4x4(xOri, xPos);
	s_xBaseTransform.InvertOrthogonal(s_xInverseBaseTransform);
#else
	s_pxOri  = &xOri;
	s_pxPos  = &xPos;
#endif

#ifdef ASURA_TOOLS
    if( !s_pxSkin->m_pxSectionBoundingBoxData )
    {
        s_pxSkin->InitSectionBoundingBoxes();
    }
#else
    Asura_Assert( s_pxSkin->m_pxSectionBoundingBoxData, "" );
#endif

	pxAnim->UpdateCoarseBoundingSphere( xPos, xOri );

	const Asura_Vector_3& xCentre = pxAnim->GetBoundingSphereCentre();
	const float fBSRadius = pxAnim->GetCoarseBoundingSphereIncludingEffectsRadius();

    if( s_bNoCulling ) { uRenderFlags |= ASURA_HIERARCHY_RENDER_NO_FRUSTUM_TEST|ASURA_HIERARCHY_RENDER_NO_MODULELINK_TEST; }

    /// Kevin Lea - 14/03/2002 10:30:50 - check to see if anim is visible in ANY camera view
    unsigned uCamVisFlags = 0;
	for( int iCam = Asura_Camera::GetNumberOfCameras(); iCam--; )
	{
        uCamVisFlags <<= 1;

        if( s_bJustRenderForCurCam )
        {
            if( Asura_Camera::GetCameraIndex() != static_cast<u_int>(iCam) ) { continue; }
        }
        else
        {
			Asura_Camera::SetCameraIndex( iCam );
        }

		if( Asura_Camera::IsCameraActive() && Asura_Camera::GetEntityToIgnore() != pxAnim->GetRootParentEntityGUID() )
		{
#if defined( ASURA_USE_NEW_VISIBILITY_SYSTEM )
            if( !(uRenderFlags & ASURA_HIERARCHY_RENDER_NO_MODULELINK_TEST))
            {
			    if ( ! pxAnim->IsVisible( ( uRenderFlags & ASURA_HIERARCHY_RENDER_NO_FRUSTUM_TEST ) != 0 ) ) continue;
            }
			else if ( ( uRenderFlags & ASURA_HIERARCHY_RENDER_NO_FRUSTUM_TEST ) == 0 )
			{
				if ( !Asura_Camera::IsSphereInFrustum(xCentre, fBSRadius) ) continue;
			}
#else
            if( !(uRenderFlags & ASURA_HIERARCHY_RENDER_NO_FRUSTUM_TEST))
            {
                // In view frustum? Do this first because the test against
                // all modules can be very expensive in comparison.
                {
                    if( !Asura_Camera::IsSphereInFrustum(xCentre, fBSRadius) )
                    {
                        continue;
                    }
                }
			}

            if( !(uRenderFlags & ASURA_HIERARCHY_RENDER_NO_MODULELINK_TEST))
            {
                // In a module that's visible to the current camera?
			    if( !pxAnim->IsInModuleVisibleToCurCam(xPos, xOri) )
                {
                    continue;
			    }
            }
#endif

            uCamVisFlags |= 1;
		}
	}

	/// Kevin Lea - 14/03/2002 10:32:32 - if no camera can see this object, just skip it
	if( !(uRenderFlags & ASURA_HIERARCHY_RENDER_UPDATEBONESALWAYS))
	{
    	if ( !uCamVisFlags ) 
		{ 
			Asura_Render::AddToSkinHierarchiesOutOfView();
			return; 
		}
	}

	//calculate positions for all the other bones. RAR 07/04/03
    if( s_bBoneUpdatesEnabled )
    {
		// Now that we know we're visible, update the bone data
		pxAnim->UpdateBoneData( 1, s_pxSkin->NumberOfBones - 1 );

		// And update the tight bounding sphere used for the shadows etc.
		pxAnim->UpdateTightBoundingSphere( xPos, xOri );
    }

	// (AW - 07.12.2004) - Moved this above the don't-render return so you can validate
	// position data without having to actually render something.
	s_pxTweenStorage->SetPositionDataValid(true);
    s_pxTweenStorage->SetPositionDataInWorldspace((uRenderFlags & ASURA_HIERARCHY_RENDER_BONES_IN_WORLDSPACE) != 0);

	/// Richard May - 06/05/2004 17:09:10 - moved this above the frame number check
	/// so you can call update multiple times a frame with the dontrender flag set 
	/// and still render it later that frame
	if( uRenderFlags & ASURA_HIERARCHY_RENDER_DONTRENDER ) return;

   	if( !uCamVisFlags ) 
	{ 
		Asura_Render::AddToSkinHierarchiesOutOfView();
		return; 
	}

#if defined(ASURA_USE_STREAMING)
	if (uRenderFlags & ASURA_HIERARCHY_RENDER_NO_MODULELINK_TEST)
	{
		// MGH 6/6/2006: Update the containing zone for use by GI lighting.
		// We need to do this explicitly as the module linking test has been disabled.
		pxAnim->UpdateContainingZone(xPos, xOri);
	}
#endif //defined(ASURA_USE_STREAMING)

	Render( pxAnim, xPos, xOri, uCamVisFlags, uRenderFlags, xCentre, fBSRadius, false );
}
void Asura_Hierarchy_System::Render( Asura_Animation* pxAnimation,
                                     const Asura_Vector_3& xPosition,
                                     const Asura_Matrix_3x3& xOrient,
                                     u_int uCamVisFlags,
                                     u_int uRenderFlags,
                                     const Asura_Vector_3& xBSCentre,
                                     float fBSRadius,
                                     bool bUpdateGlobals )
{
	Asura_Assert(pxAnimation,"");

	if (!Asura_Render::IsSystemEnabled(ASURA_RENDER_SYSTEM_OBJECTS))
	{
		return;
	}

#ifdef ASURA_HIERARCHY_QUAD
	// RJT TODO: Ultimately this transform should be passed in
	s_xBaseTransform = Asura_AMatrix4x4(xOrient, xPosition);
	s_xBaseTransform.InvertOrthogonal(s_xInverseBaseTransform);
	s_xOrigPosition = xPosition;
	s_xOrigOrient = xOrient;
#else
	s_xAnimOrient = xOrient;
	s_xAnimPos = xPosition;

	s_pxOri = &s_xAnimOrient;
	s_pxPos = &s_xAnimPos;
#endif

	if(bUpdateGlobals)
	{
		s_pxSkin		= pxAnimation->GetSkin(0);
		s_uSkinIndex	= 0;
		Asura_Assert( s_pxSkin, "" );

#ifdef ASURA_TOOLS
		s_pxMesh = s_pxSkin->GetMesh();
#endif

		s_pxTweenStorage = pxAnimation->GetTweenStore();
	}

	if(Asura_SortedRender::IsActive())
	{
		//add to list of items to be sorted based on texture
		Asura_SortedRender::AddAnimation( pxAnimation,
                                          xPosition,
                                          xOrient,
                                          uCamVisFlags,
                                          uRenderFlags,
                                          xBSCentre,
                                          fBSRadius );

	}
	else
	{
		if( false == BonesToWorldspaceFromTweenStorage())
		{
			return;
		}

		pxAnimation->UpdateLighting(uRenderFlags);
		if( s_pxSkin->IsSkinnedModel() )
		{
			Project_RenderSkin( pxAnimation, uCamVisFlags, uRenderFlags, xBSCentre, fBSRadius );
			Asura_Render::AddToSmoothSkinsRendered();
		}
		else
		{
			Project_RenderHierarchy( pxAnimation, uCamVisFlags, uRenderFlags, xBSCentre, fBSRadius );
			Asura_Render::AddToHierarchiesRendered();
		}
	}
}

void Asura_Hierarchy_System::RenderShadow( Asura_Animation& xAnim, const Asura_Shadow& xShadow, const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOri, const u_int uRenderFlags /*= 0*/ )
{
	if( !xAnim.GetSkin() )
	{
		Asura_Assert( false, "Shadowed animation has no skin." );
		return;
	}

#ifdef ASURA_HIERARCHY_QUAD
	// RJT TODO: Ultimately this transform should be passed in
	s_xBaseTransform = Asura_AMatrix4x4(xOri, xPos);
	s_xBaseTransform.Invert(s_xInverseBaseTransform);
#else
	s_xAnimOrient = xOri;
	s_xAnimPos = xPos;

	s_pxOri = &s_xAnimOrient;
	s_pxPos = &s_xAnimPos;
#endif

	s_pxSkin			= xAnim.GetSkin();
	s_uSkinIndex		= 0;
	s_pxTweenStorage	= xAnim.GetTweenStore();

	if(BonesToWorldspaceFromTweenStorage())
	{
		for(u_int u = 0; u < ASURA_ANIMATION_SKIN_COUNT; u++)
		{
			s_pxSkin		= xAnim.GetSkin(u);
			s_uSkinIndex	= u;

			if(s_pxSkin != NULL)
			{
				if( s_pxSkin->IsSkinnedModel() )
				{
					Platform_RenderSkinShadow( xAnim, xShadow, 0, uRenderFlags );
				}
				else
				{
					Platform_RenderHierarchyShadow( xAnim, xShadow, 0, uRenderFlags );
				}
			}
		}
	}
}




void Asura_Hierarchy_System::Initialise(void)
{
	Project_Initialise();
    Platform_Initialise();
}
void Asura_Hierarchy_System::Shutdown(void)
{	
	Project_Shutdown();
    Platform_Shutdown();
}

void Asura_Hierarchy_System::Reset()
{
	Project_Reset();
    Platform_Reset();
}

// RJT TODO: Ultimately get rid of these (just return 4x4 transform), don't really want interface specialisation though
// RJT TODO: Would be nice to get rid of intermediate when bones are already in world-space too

Asura_Matrix_3x3 Asura_Hierarchy_System::GetWorldspaceBoneOri( int iBoneIdx )
{
    Asura_Assert( 0 <= iBoneIdx && iBoneIdx < s_pxSkin->NumberOfBones, "" );

#ifdef ASURA_HIERARCHY_QUAD
	Asura_Matrix_3x3 xOri;
	if( s_bBonesInWorldspace )
	{
		s_axBoneTransforms[iBoneIdx].ToMatrix(xOri);
	}
	else
	{
		Asura_AMatrix4x4	xTransform;
		
//		xTransform	= s_axBoneTransforms[iBoneIdx] * s_xBaseTransform;
		s_xBaseTransform.Apply(s_axBoneTransforms[iBoneIdx], xTransform);
		xTransform.ToMatrix(xOri);
	}

	return xOri;
#else
    return( s_bBonesInWorldspace ? s_axBoneOri[iBoneIdx] : *s_pxOri * s_axBoneOri[iBoneIdx]  );
#endif
}

Asura_Vector_3 Asura_Hierarchy_System::GetWorldspaceBonePos( int iBoneIdx )
{
    Asura_Assert( 0 <= iBoneIdx && iBoneIdx < s_pxSkin->NumberOfBones, "" );

#ifdef ASURA_HIERARCHY_QUAD
	if( s_bBonesInWorldspace )
	{
		Asura_Vector_3	xPosition = s_axBoneTransforms[iBoneIdx].GetTranslation();
		return(xPosition);
	}
	else
	{
		Asura_AVector4	xTranslation = s_axBoneTransforms[iBoneIdx].GetA4Translation();
		xTranslation	= xTranslation * s_xBaseTransform;

		return(Asura_Vector_3(xTranslation.GetX(), xTranslation.GetY(), xTranslation.GetZ()));
	}
#else
    return( s_bBonesInWorldspace ? s_axBonePos[iBoneIdx] : s_axBonePos[iBoneIdx] * *s_pxOri + *s_pxPos );
#endif
}

#if defined(ASURA_HIERARCHY_QUAD)

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_Hierarchy_System::GetWorldspaceBoneTransform
// Author       : Andy Tate
// Date         : 29-Jun-2007
////////////////////////////////////////////////////////////////////////////////

Asura_AMatrix4x4 Asura_Hierarchy_System::GetWorldspaceBoneTransform(const int iBoneIndex)
{
	Asura_Assert( 0 <= iBoneIndex && iBoneIndex < s_pxSkin->NumberOfBones, "" );
	if(s_bBonesInWorldspace)
	{
		return(s_axBoneTransforms[iBoneIndex]);
	}
	else
	{
		Asura_AMatrix4x4	xResult;

		s_xBaseTransform.Apply(s_axBoneTransforms[iBoneIndex], xResult);
		return(xResult);
	}
}

/// End of function 'Asura_Hierarchy_System::GetWorldspaceBoneTransform'. ///

#endif	// defined(ASURA_HIERARCHY_QUAD)


/// End of file. ///
