//
// Asura_Hierarchy.h
//
// Created by Kevin Lea - 12/06/2001 11:13:50
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_HIERARCHY_H__
#define __ASURA_HIERARCHY_H__

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Animation/Asura_PlatformHierarchy.h"
#include "Core/Asura_Handle.h"
#include "Core/Asura_Hash.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_String.h"
#include "Core/Asura_TextureFlags.h"
#include "Core/Memory/Asura_ResourceAllocator.h"

// D E F I N E S /////////////////////////////////////////////////////////////

#ifdef ASURA_TOOLS
	#undef ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES
    #define ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES (1)
#else
// KOR - 28/11/2005 - allow projects to over-ride this setting
#ifndef ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES
    //Need to allow uncompressed keyframes outside of tools for a few awkward anims
	#define ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES (1)
#endif //def ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES
#endif

#ifndef ASURA_MAX_NO_OF_BONES_PER_MODEL
    #define ASURA_MAX_NO_OF_BONES_PER_MODEL (32)
#endif
#ifndef ASURA_MAX_NO_OF_BONES_PER_MATERIAL
	#define ASURA_MAX_NO_OF_BONES_PER_MATERIAL (80)
#endif

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_Animation;
class Asura_Animation_Posable;
class Asura_Collision_Box;
class Asura_Collision_Object;
class Asura_CollisionMesh;
class Asura_Hierarchy_Tween;
#ifdef ASURA_TOOLS
class Asura_Mesh;
#endif
class Asura_NameTree;
class Asura_Shadow;

// C L A S S   D E C L A R A T I O N S ///////////////////////////////////////

class Asura_Hierarchy_Bone_RestFrame
{
	public:	
		Asura_Vector_3 Position;
		Asura_Quat Quat;
};

class Asura_Hierarchy_Bone_KeyFrame : public Asura_Hierarchy_Bone_RestFrame
{
	public:
		float Time;
};

class Asura_Hierarchy_Bone_QuatKeyFrame
{
	public:	
		Asura_Quat Quat;
		float Time;
};

//Marker point is used for specifying positions on a hierarchy relative to a bone. For gun attachment points etc.
class Asura_Hierarchy_Marker_Point
{
	public :
		Asura_Vector_3 Position;
		Asura_Matrix_3x3 Matrix;
		int BoneIndex;
		Asura_Hash_ID uNameHash;

		void SetName(const char* szName) { xName = szName; uNameHash = Asura_GetHashID(szName); }
        Asura_String& GetName() { return xName; }
		const Asura_String& GetName() const { return xName; }
		
	private:
		Asura_String xName;
};

class Asura_Animation_Flag_Interval
{
public :
	int Flag;//the flag(s) that apply for this interval
	float StartTime;//Between 0 and 1
	float EndTime;//Between 0 and 1
};


#define ASURA_ANIMATIONSOUNDFLAG_FOLLOWSKIN 0x00000001 //Causes the sound position to be updated each frame - automatically applies to looped sounds and dialogue

class Asura_Animation_Sound
{
public :
	float m_fStartTime;//Time (0 to 1) at which sound is triggered
	float m_fEndTime;//If different from start time then the sound will loop in between
	int m_iAnimSoundID;//ID of sound to be played.(exact sound is found by checking in the skin)
	u_int m_uFlags;
};


class Asura_Hierarchy_Bone_Anim
{
	public:

		Asura_Hierarchy_Bone_Anim()
		{
			KeyFrames = 0;
			Positions = 0;
			ConstantOffset = false;
			NumberOfKeyFrames = 0;
		}

		~Asura_Hierarchy_Bone_Anim()
		{
			Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
			{
				Asura_ResourceDeallocateArray(KeyFrames);
				Asura_ResourceDeallocateArray(Positions);
			}
			Asura_ResourcePopType();
		}

		void Initialise(int numberOfKeyFrames,bool constantOffset)
		{
			Asura_Assert(!KeyFrames && !Positions,"Already initialised");
			NumberOfKeyFrames = numberOfKeyFrames;
			ConstantOffset = constantOffset;
			Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
			{
				Asura_ResourceAllocateArray(KeyFrames, numberOfKeyFrames);
				Asura_ResourceAllocateArray(Positions, (ConstantOffset ? 1 : numberOfKeyFrames));
			}
			Asura_ResourcePopType();
		}

		void InitialiseForCompressed(int iNumberOfKeyFrames, bool bConstantOffset)
		{
			NumberOfKeyFrames = iNumberOfKeyFrames;
			ConstantOffset = bConstantOffset;

			// KJL - 18/06/2003 18:22:28 - still got these positions in the compressed data...
			Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
			{
				Asura_ResourceAllocateArray(Positions, (bConstantOffset ? 1 : iNumberOfKeyFrames));
			}
			Asura_ResourcePopType();
		}

		const Asura_Hierarchy_Bone_Anim& operator = (const Asura_Hierarchy_Bone_Anim& xAnim);
	

		#ifdef ASURA_TOOLS
		void RemoveUnneededKeyFrames(float fPositionTolerance,float fQuatTolerance);
		#endif

		Asura_Vector_3& Position(int frame){return ConstantOffset ? Positions[0] : Positions[frame];}
		Asura_Quat& Quat(int frame){return KeyFrames[frame].Quat;}
		float& Time(int frame){return KeyFrames[frame].Time;}

		int NumberOfKeyFrames;
		bool ConstantOffset;
	
		Asura_Hierarchy_Bone_QuatKeyFrame* KeyFrames;
		Asura_Vector_3* Positions;//array of length 1 or NumberOfKeyFrames depending on ConstantOffset
};

class Asura_Hierarchy_Skin_Vertex
{
	public:
		Asura_Vector_3 Offset[4];
		short          BoneID[4];
		float          Weight[4];
};

class Asura_Hierarchy_Skin_Normal
{
	public:
		Asura_Vector_3 VertexNormal[4];
};

//for models made out of multiple shapes instead of a single skin
class Asura_Hierarchy_Section
{

	public:

		Asura_Hierarchy_Section( void );
		~Asura_Hierarchy_Section( void );

#if defined(ASURA_TOOLS) || defined(ASURA_HIERARCHY_SKIN_LOAD_SECTION_NAMES)
        Asura_String SectionName;//name for this section , combined with skin name to determine the mesh name
#endif
#ifdef ASURA_TOOLS
		Asura_Mesh* Mesh;
#endif
		Asura_CollisionMesh* m_pxCollisionMesh;

};

// for doing LOS tests against each section
class Asura_Hierarchy_SectionBoundingBox
{
    public:

        Asura_Hierarchy_SectionBoundingBox()
        {
            m_xBB.Reset();
            m_uFlags = 0;
        }

        Asura_Bounding_Box m_xBB;

        // Flags.
        union
        {
            struct
            {
                u_int m_uValid        :  1,
                      _pad            : 31;
            };

            u_int m_uFlags;
        };

};


// KJL - 23/06/2003 16:57:07 - per bone data to go with the compressed keyframe system
// there's room for some compression here too. ;)
// (MH - 27.04.2004) yep, we can use shorts instead
class Asura_Hierarchy_Bone_OldVersion
{
	public:
		u_int	m_uNumberOfKeyFrames;
		u_int	m_uFirstKeyFrameIndex;

		u_int	m_uNumberOfBoneOffsets;
		u_int	m_uFirstBoneOffsetIndex;
};

class Asura_Hierarchy_Bone
{
	public:
		u_short	m_usNumberOfKeyFrames;
		u_short	m_usFirstKeyFrameIndex;

		u_short	m_usNumberOfBoneOffsets;
		u_short	m_usFirstBoneOffsetIndex;
};

class Asura_Hierarchy_Sound
{
public :
	int m_iAnimSoundID;//ID of this sound , matches up to sound ID's used in the animations
	Asura_Hash_ID m_uSoundHash;//hash id of sound filename
	float m_fVolume;//volume for sound (0 to 1)
	float m_fInnerRadius;
	float m_fOuterRadius;
	float m_fPitch;

	Asura_Hash_ID m_uSoundEvent;//If this is non-zero then the rest of the settings are ignored.

	#ifdef ASURA_USE_DIALOGUE_SYSTEM
	Asura_Hash_ID m_uDialogueEvent;//Dialogue event to be triggered
	#endif

	#ifdef ASURA_TOOLS
	Asura_String m_xSoundName;//filename of sound
	#endif
};

class Asura_Hierarchy_Skin
{
	public:

		Asura_Hierarchy_Skin( void );
		virtual ~Asura_Hierarchy_Skin( void );

		// public accessors
		const char*		GetName() const		{ return Name.GetString(); }
		Asura_Hash_ID	GetHashID() const	{ return m_uNameHashID; }

		void Reset();

		void Initialise(int numberOfVertices, int numberOfBones)
		{
#if defined(ASURA_HIERARCHY_QUAD)
			Asura_Assert(!m_pxBoneRestPoseTransforms && !m_pxBoneRestPoseInverseTransforms, "Already initialised");
#else
			Asura_Assert(!BoneRestPosition && !BoneRestOrientation, "Already initialised");
#endif	// defined(ASURA_HIERARCHY_QUAD)
			Asura_Assert(!ParentBone && !BoneRestPose, "Already initialised");
			NumberOfVertices = numberOfVertices;
			NumberOfBones = numberOfBones;

			Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
			{
				if( Asura_PlatformHierarchy_NeedIndependentSkinData() && numberOfVertices )
				{
					Asura_ResourceAllocateArray(VertexData, numberOfVertices);
				}
//				Asura_ResourceAllocateArray(NormalData, numberOfVertices);
				Asura_ResourceAllocateArray(ParentBone, numberOfBones);
				Asura_ResourceAllocateArray(BoneRestPose, NumberOfBones);

#if defined(ASURA_HIERARCHY_QUAD)
				Asura_ResourceAllocateArray(m_pxBoneRestPoseInverseTransforms, NumberOfBones);
				Asura_ResourceAllocateArray(m_pxBoneRestPoseTransforms, NumberOfBones);
#else
				Asura_ResourceAllocateArray(BoneRestPosition, NumberOfBones);
				Asura_ResourceAllocateArray(BoneRestOrientation, NumberOfBones);
#endif
			}
			Asura_ResourcePopType();
		}

		//Is this a hierarchical model? (as opposed to skinned)
		bool IsSkinnedModel() const {return NumberOfVertices!=0;}

		//Virtual functions for skins that can use mirroed animations. RAR 13/02/2006
		//Gets the equivalent bone on the other side of body (or itself , if there none)
		virtual int GetMirroredBone(int iBone) const {return m_piMirroredBone ? m_piMirroredBone[iBone] : iBone;}
		//Applies any mirroring needed to relative orient and position
		virtual void ApplyMirroring(int iBone,bool bDeltaAnim,Asura_Quat& xQuat,Asura_Vector_3& xPos) const; /*{Asura_Assert(0,"ApplyMirroring not implemented for this project.");}*/

		#ifdef ASURA_TOOLS
		//returns true if exported from Max so as to support mirroring
		bool ExportedForMirroring() const {return m_pxInitialRestPose!=0;}
		#endif

		int NumberOfBones;
		
		//These are filled in for smooth skin models
		int NumberOfVertices;
		Asura_Hierarchy_Skin_Vertex* VertexData;
		Asura_Hierarchy_Skin_Normal* NormalData;

		//And this for hierarchical models
		Asura_Hierarchy_Section* SectionData;
		
        // This is filled in for all skins and models, even though in the case of a 
        // hierarchy model each Asura_Hierachy_Section has an Asura_Mesh with a 
        // bounding box. This is because Asura_Mesh will be replaced at some point, 
        // and the SectionData is not used as such on the PS2. May want to add a 
        // collision mesh to section data and use that instead, if collisions with the
        // polys of each section is required.
        // I made it a structure so that extra data can easily be added, ignore flags etc.
        Asura_Hierarchy_SectionBoundingBox* m_pxSectionBoundingBoxData;
#ifdef ASURA_TOOLS
		//A seperate array containing any bounding boxes that have been tweaked by hand , 
		//which will be copied into the main array when InitSectionBoundingBoxes is called
		//(Not put directly into the main array , because it simplifies level exports
		//and max import/exports slightly). RAR 16/07/04
		Asura_Hierarchy_SectionBoundingBox* m_pxManualSectionBoundingBoxData;

        bool InitSectionBoundingBoxes( void );
#endif

        void SetupBB( bool bUseRestPose = true );
        const Asura_Bounding_Box& GetBB( void ) const { return m_xBB; }
        float GetBoundingRadius( void ) const { return m_fBoundingRadius; }

		//gets the dynamics collision mesh if it has one
		//otherwise it gets the collision bounding box (if it has one)
		const Asura_Collision_Object* GetCollisionObject() const;
		
		//gets the bounding box of the collision mesh ,or collision box , or just the skin depending on what it has
		const Asura_Bounding_Box& GetCollisionBB() const;
		void SetCollisionBB(const Asura_Bounding_Box& xBB);
		#ifdef ASURA_TOOLS
		void ResetCollisionBB();//resets collision BB to default
		#endif
		bool HasCollisionBox() const {return m_bHasCollisionBox;}//indicates that the skin has a collision box as opposed to a collision mesh
		bool HasCustomCollisionBox() const {return m_bHasCustomCollisionBox;}//indicates that the collision box has been edited , rather than being the default generated box

		const Asura_CollisionMesh* GetCollisionMesh() const; 
		Asura_CollisionMesh* GetCollisionMesh( void );
		void SetCollisionMesh(Asura_CollisionMesh* pxCollMesh);
		bool HasCollisionMesh() const {return m_bHasCollisionMesh;}
		
		int* ParentBone;
		Asura_Hierarchy_Bone_RestFrame* BoneRestPose;
		#ifdef ASURA_TOOLS
		Asura_Hierarchy_Bone_RestFrame* m_pxInitialRestPose;
		#endif

#ifdef ASURA_HIERARCHY_QUAD
		Asura_AMatrix4x4*	m_pxBoneRestPoseTransforms;			// Array of 4x4 bone rest-pose matrices.
		Asura_AMatrix4x4*	m_pxBoneRestPoseInverseTransforms;	// Array of 4x4 bone rest-pose inverse matrices.
#else
		Asura_Vector_3*		BoneRestPosition;
		Asura_Matrix_3x3*	BoneRestOrientation; //Note that thhis stores the transpose of the orientations
#endif

		//Array giving the index of the mirrored bone for each bone in the skin.
		int* m_piMirroredBone;

		Asura_String Name;
		// KOR - 19/08/2003 12:55:49 - For fast look-up. Set in Asura_Animation::NewSkin
		unsigned int m_uNameHashID;

		int NumberOfMarkerPoints;
		Asura_Hierarchy_Marker_Point* MarkerPoint;

		//Pointers for a linked list of level of detail models
		//Each detail level will have its own name (probably derived from the first) , since they
		//will be using different meshes. I think only the root level will be directly available
		//using GetSkin in Asura_Hierarchy_System.
		//Lower detail levels will share pointers for much of the entries with the root skin.
		//-RAR 03/09/02
		Asura_Hierarchy_Skin* m_pxNextHigherSkin;
		Asura_Hierarchy_Skin* m_pxNextLowerSkin;

		#ifdef ASURA_TOOLS
		bool m_bManualLOD; // true if LOD models were exported from max
		#endif

		float m_fMinDistance;//Minimum distance at which this detail level should be visible

		//sounds associatd with this skin (sorted by ID)
		int m_iNumberOfSoundDefns;
		Asura_Hierarchy_Sound* m_pxSoundDefns;

		#ifdef ASURA_TOOLS
		//Normally duplicate skin chunks are skipped when loading. Setting this flag
		//will allow the newer version to overwrite the current skin.
		//RAR 14/11/2003
		bool m_bAllowOverwriteOnLoad;

		//Modified time of file this was loaded from , for helping with determining what needs to be reloaded
		//RAR 02/04/2007
		FILETIME m_xModifiedTime;
		#endif

		#if defined(ASURA_TOOLS) && defined(ASURA_SUPPORT_PLATFORM_HASHED_FILENAMES)
		//Platform that this belongs to for platform specific skins/anims , or -1 if its the default to use.
		int m_iPlatform;
		#endif

		Asura_NameTree* BoneTree;// names -> IDs

		#ifdef ASURA_TOOLS
		//Sort key for each bone which is used by the max exporters.
		//It only exists here so it can be re-imported back into max/maya.
		//RAR 26/01/2007
		int* m_piBoneSortKey;
		#endif
		
		int  GetParentBoneIndex( int iBoneIndex ) const;
		bool GetBoneIndex( const char* boneName, int& boneIndex );

		bool GetMarkerPointIndex(const char* szMarkerName,int& iMarkerIndex) const
		{
			return GetMarkerPointIndex(Asura_GetHashID(szMarkerName), iMarkerIndex);
		}

		bool GetMarkerPointIndex(Asura_Hash_ID uMarkerHash,int& iMarkerIndex) const
		{
			for(int i=0;i<NumberOfMarkerPoints;i++)
			{
				if(MarkerPoint[i].uNameHash == uMarkerHash)
				{
					iMarkerIndex = i;
					return true;
				}
			}
			return false;
		}

		Asura_Hash_ID GetMarkerPointHashID( int iMarkerIndex ) const
		{
			if( (iMarkerIndex < 0) || (iMarkerIndex >= NumberOfMarkerPoints) ) return ASURA_HASH_ID_UNSET;

			return MarkerPoint[iMarkerIndex].uNameHash;
		}

		bool GetMarkerPositionInRestPose(Asura_Hash_ID uMarkerHash, Asura_Vector_3 *pxPosition, Asura_Matrix_3x3 *pxOrientation = NULL) const;

		void CopyFromRootSkin( const Asura_Hierarchy_Skin* pxRootSkin);

		Asura_Hierarchy_Skin* GetDetailSkinFromDistance(float fDistance);
		const Asura_Hierarchy_Skin* GetDetailSkinFromDistance(float fDistance) const;
		const Asura_Hierarchy_Skin* GetRelativeDetailSkin(unsigned int uLevels) const;

		void CalculateSkinNormalData();
        void CalculateRestPose( void );
#if defined(ASURA_HIERARCHY_QUAD)
		void CalcRestPoseBone(const int iBone, const Asura_AMatrix4x4& xTransform);
#else
		void CalcRestPoseBone(const int iBone, const Asura_Matrix_3x3& xOrientation, const Asura_Vector_3& xPosition);
#endif	// defined(ASURA_HIERARCHY_QUAD)

#ifdef ASURA_TOOLS
		Asura_Mesh* GetMesh( void ) const;
		void ClearMeshPointer(){m_pxMesh = 0;}
		void SetMesh(Asura_Mesh* pxMesh) {m_pxMesh = pxMesh;}
#endif

        int  Platform_GetTextureIndex( void ) const;
        bool Platform_IsOpaque( void ) const;

		//gets the sound properties (if any) from the sound ID used
		//in the animations
		const Asura_Hierarchy_Sound* GetSoundPropertiesFromAnimSoundID(int iAnimSoundID) const;

		//Gets the highest LOD version of this skin.
		const Asura_Hierarchy_Skin* GetRootSkin() const {return m_pxNextHigherSkin ? m_pxNextHigherSkin->GetRootSkin() : this;}

		#ifdef ASURA_TOOLS
		//Determines which bones are Left/Right equivalents by checking the names
		void CalculateMirroredBones();

		static bool IsMirroredBoneName(const char* szName1,const char* szName2);
		#endif

	private:
		//Just to be confusing , this initialise has to be used before the other initialise gets called , 
		//since it just sets everything to zero. RAR 14/11/2003
		void Initialise();
		void Shutdown();


        // Additional visibility culling stuff, generated by SetupBB().
        float              m_fBoundingRadius;
        Asura_Bounding_Box m_xBB;

		//an optional collision mesh or box
		Asura_Collision_Object* m_pxCollisionObject;

			

		unsigned int m_bHasCollisionBox:1;
		unsigned int m_bHasCollisionMesh:1;
		unsigned int m_bHasCustomCollisionBox:1;


		mutable class Asura_Mesh* m_pxMesh;
};

class Asura_Hierarchy_Anim
{
	public:
		Asura_Hierarchy_Anim()
		{
			Initialise();	
		}

		~Asura_Hierarchy_Anim()
		{
			Shutdown();
		}

		// public accessors
		const char*		GetName() const				{ return Name.GetString(); }
		Asura_Hash_ID	GetHashID() const			{ return m_uHashID;	} // setup by Chunk Process
		int				GetNumberOfBones() const	{ return NumberOfBones; }
		float			GetDuration() const			{ return Duration; }
		float			GetSpeed() const			{ return Speed; }	//speed of movement associated with anim. In m/s

		void Initialise()
		{
			NumberOfBones = 1;
			Duration = 0;

			BoneAnims = 0;
			BoneLengths = 0;
			m_iBoneForMovementScale = 0;

			NumberOfAnimationFlags = 0;
			AnimationFlags = 0;

			Speed = 0;
			Direction = 0;	

			IsDeltaAnimation = false;
			m_bSharedAnimDeltaPos = false;
			m_bSharedAnimDeltaOrient = false;
			m_bHasFacialAnimation = false;
			#ifdef ASURA_TOOLS
			m_bDontRemoveKeyframes = false;
			m_bDontCompressKeyframes = false;
			#endif

			m_pxCompressedQuatKeyFrames = 0;
			m_pxCompressedTimeKeyFrames = 0;
			m_pxCompressedBoneData = 0;
			m_pxCompressedBoneOffsets = 0;
			m_uTotalNumberOfCompressedKeyFrames = 0;
			m_uTotalNumberOfCompressedBoneOffsets = 0;

			m_pxBB = 0;
			m_fBoundingRadius = 0;

			m_fLoopPoint = 0;

			m_iNumberOfAnimationSounds = 0;
			m_pxAnimationSounds = 0;

			m_iNumberOfSoundDefns = 0;
			m_pxSoundDefns = 0;

			m_bHasMovementData = false;

			#ifdef ASURA_TOOLS
			m_piParentBones = 0;
			m_pxBoneNames = 0;
			m_pxSkinExportTransform = 0;
			m_pxBoneRestPose = 0;
			m_pxBoneDeltaPose = 0;
			#endif

			#ifdef ASURA_TOOLS
			m_bAllowOverwriteOnLoad = false;
			m_xModifiedTime.dwLowDateTime = 0;
			m_xModifiedTime.dwHighDateTime = 0;
			#endif

			#if defined(ASURA_TOOLS) && defined(ASURA_SUPPORT_PLATFORM_HASHED_FILENAMES)
			m_iPlatform = -1;
			#endif


			m_uHashID = ASURA_HASH_ID_UNSET;
		}
		
		void Shutdown()
		{
			Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
			{
				Asura_ResourceDeallocateArray(BoneAnims);
				Asura_ResourceDeallocateArray(BoneLengths);
				Asura_ResourceDeallocateArray(AnimationFlags);

				Asura_ResourceDeallocateArray(m_pxCompressedQuatKeyFrames);
				Asura_ResourceDeallocateArray(m_pxCompressedTimeKeyFrames);
				Asura_ResourceDeallocateArray(m_pxCompressedBoneData);
				Asura_ResourceDeallocateArray(m_pxCompressedBoneOffsets);

				if(m_pxBB) delete m_pxBB;

				Asura_ResourceDeallocateArray(m_pxAnimationSounds);
				Asura_ResourceDeallocateArray(m_pxSoundDefns);

#ifdef ASURA_TOOLS
				Asura_ResourceDeallocateArray(m_piParentBones);
				Asura_ResourceDeallocateArray(m_pxBoneNames);
				Asura_ResourceDeallocateArray(m_pxSkinExportTransform);
				Asura_ResourceDeallocateArray(m_pxBoneRestPose);
				Asura_ResourceDeallocateArray(m_pxBoneDeltaPose);
#endif
			}
			Asura_ResourcePopType();
		}
		
		void Reset()
		{
			Shutdown();
			Initialise();
		}



		void Initialise(int bones, float duration,bool bHasMovementData = false)
		{
			Asura_Assert(!BoneAnims,"Already initialised");
			NumberOfBones = bones;
			Duration = duration;
			m_bHasMovementData = bHasMovementData;
			
			Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
			{
				Asura_ResourceAllocateArray(BoneAnims, GetBoneArraySize());
			}
			Asura_ResourcePopType();
		}



		void AddBoneKeyFrame( const Asura_Vector_3& position, const Asura_Quat& quat, float t, int f, int boneIndex)
		{
			BoneAnims[boneIndex].Position(f) = position;
			BoneAnims[boneIndex].Quat(f) = quat;
			BoneAnims[boneIndex].Time(f) = t;
		}

		inline bool HasCompressedKeyFrames(void) const
		{
			return (m_uTotalNumberOfCompressedKeyFrames!=0);
		}

		inline int GetNumberOfKeyFrames(const u_int uBone) const
		{
#if ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES
			if (HasCompressedKeyFrames())
			{
				return m_pxCompressedBoneData[uBone].m_usNumberOfKeyFrames;
			}
			else
			{
				return BoneAnims[uBone].NumberOfKeyFrames;
			}
#else
			return m_pxCompressedBoneData[uBone].m_usNumberOfKeyFrames;
#endif
		}
		
		inline void GetKeyFrameQuat(Asura_Quat& xQuat, const u_int uBone, const u_int uFrameNumber) const
		{
#if ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES
			if (HasCompressedKeyFrames())
			{
				const u_int u = uFrameNumber + m_pxCompressedBoneData[uBone].m_usFirstKeyFrameIndex;
				m_pxCompressedQuatKeyFrames[u].ToQuat(xQuat);
			}
			else
			{
				xQuat = BoneAnims[uBone].KeyFrames[uFrameNumber].Quat;
				#ifdef ASURA_TOOLS
				if(s_bPreviewCompressedKeyframes && !m_bDontCompressKeyframes)
				{
					//compress and then decompress quat so that the anim can be viewed as
					//if it had been compressed
					Asura_Comp_Quat_Fast xCompQuat(xQuat);
					xCompQuat.ToQuat(xQuat);
				}
				#endif

			}
#else
			u_int u = uFrameNumber + m_pxCompressedBoneData[uBone].m_usFirstKeyFrameIndex;
			m_pxCompressedQuatKeyFrames[u].ToQuat(xQuat);
#endif
		}
		
		// (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.
		inline void SlowGetKeyFrameQuat(Asura_Quat& xQuat, const u_int uBone, const u_int uFrameNumber) const
		{
			if (BoneAnims)
			{
				Asura_Assert(BoneAnims[uBone].KeyFrames, "no keyframes");
				xQuat = BoneAnims[uBone].KeyFrames[uFrameNumber].Quat;
			}
			else
			{
				GetKeyFrameQuat(xQuat, uBone, uFrameNumber);
			}
		}
				

		inline float GetKeyFrameTime(const u_int uBone, const u_int uFrameNumber) const
		{
			if (HasCompressedKeyFrames())
			{
				u_int u = uFrameNumber + m_pxCompressedBoneData[uBone].m_usFirstKeyFrameIndex;
                int iCompressedTime = m_pxCompressedTimeKeyFrames[u];
                return( iCompressedTime == 65535 ? 1.0f : (static_cast<float>(iCompressedTime) / 65535.0f) );
			}
			else
			{ 
				return BoneAnims[uBone].KeyFrames[uFrameNumber].Time;
			}
		}
		
		// (MH - 17.07.2003) for functions which repeatedly call GetKeyframeTime, these are faster
		// if you are using compressed key frames which we always do in exported levels.
		inline u_int GetBoneFirstKeyframeIndex(const u_int uBone) const
		{
			return m_pxCompressedBoneData[uBone].m_usFirstKeyFrameIndex;
		}

		inline const u_short* GetCompressedTimeKeyFrames() const
		{
			return m_pxCompressedTimeKeyFrames;
		}

		static inline float FastGetCompressedKeyFrameTime(const u_short* usCompressedTimeKeyFrames, const u_int uBoneFirstKeyframeIndex, const u_int uFrameNumber)
		{
			u_int u = uFrameNumber + uBoneFirstKeyframeIndex;
            int iCompressedTime = usCompressedTimeKeyFrames[u];
            return( iCompressedTime == 65535 ? 1.0f : (static_cast<float>(iCompressedTime) / 65535.0f) );
		}

		inline const Asura_Vector_3& GetKeyFramePosition(const u_int uBone, const u_int uFrameNumber) const
		{
#if ASURA_ALLOW_UNCOMPRESSED_KEYFRAMES
			if (HasCompressedKeyFrames())
			{
				if (m_pxCompressedBoneData[uBone].m_usNumberOfBoneOffsets == 1)
				{
					u_int u = m_pxCompressedBoneData[uBone].m_usFirstBoneOffsetIndex;
					return m_pxCompressedBoneOffsets[u];
				}
				else
				{
					//Asura_Assert(m_pxCompressed[uBone].m_uNumberOfBoneOffsets == m_pxCompressedBoneData[uBone].m_usNumberOfKeyFrames, "invalid number of bone offsets!");
					u_int u = m_pxCompressedBoneData[uBone].m_usFirstBoneOffsetIndex+ uFrameNumber;
					return m_pxCompressedBoneOffsets[u];
				}
			}
			else
			{
				return BoneAnims[uBone].Position(uFrameNumber);
			}
#else
			u_int u;
			if (m_pxCompressedBoneData[uBone].m_usNumberOfBoneOffsets == 1)
			{
				u = m_pxCompressedBoneData[uBone].m_usFirstBoneOffsetIndex;
			}
			else
			{
				//Asura_Assert(m_pxCompressed[uBone].m_uNumberOfBoneOffsets == m_pxCompressedBoneData[uBone].m_usNumberOfKeyFrames, "invalid number of bone offsets!");
				u = m_pxCompressedBoneData[uBone].m_usFirstBoneOffsetIndex+ uFrameNumber;
			}
			return m_pxCompressedBoneOffsets[u];
#endif
		}

		int GetBoneArraySize() const
		{
			//If the animation has movement data then it will have an extra entry in the array
			//stuck on the end containing this.
			return m_bHasMovementData ? (NumberOfBones+1) : NumberOfBones;
		}

		bool HasMovementData() const {return m_bHasMovementData;}

		//Gets the index of the fake bone that contains movement offsets for this animation
		int GetMovementBoneIndex() const { return m_bHasMovementData ? NumberOfBones : -1;}

		//If the animation has movement data , this gets the offset from the start of the animation
		//fTime is the time value from 0 to 1
		//If pxSkin is provided then the movement will be scaled based on the relative length of the bones in the skin and the animation
		bool GetMovementOffset(const Asura_Hierarchy_Skin* pxSkin,float fTimer,Asura_Vector_3* pxOutput,bool bMirrored = false) const;
		bool GetMovementOffset(const Asura_Hierarchy_Skin* pxSkin,float fTimer,Asura_Vector_3* pxOutputPos,Asura_Quat* pxOutputQuat,bool bMirrored = false) const;

		//If the animation has movement data , this gets the velocity at a given time
		//fTimer is the time value from 0 to 1
		//fDuration is the duration of the animation in seconds
		//If pxSkin is provided then the movement will be scaled based on the relative length of the bones in the skin and the animation
		bool GetVelocity(const Asura_Hierarchy_Skin* pxSkin,float fTimer,float fDuration,Asura_Vector_3* pxOutput,bool bMirrored =false, bool bReversed = false) const;

		//If the animation has movement data , this gets the angular velocity at a given time
		//fTimer is the time value from 0 to 1
		//fDuration is the duration of the animation in seconds
		bool GetAngularVelocity(float fTimer,float fDuration,Asura_Vector_3* pxOutputAxis,Asura_Angle* pfOutputAngle,bool bMirrored =false, bool bReversed = false) const;


		//Gets position/orientation of bone relative to its parent at a given time.
		//fTimer is the time value from 0 to 1
#ifdef ASURA_USE_KEYFRAME_CACHING
		void GetRelativeBonePosition(int iBone,float fTimer,Asura_Vector_3* pxOutputPos,Asura_Quat* pxOutputQuat, class Asura_Cached_KeyFrame* pxCachedKeyFrame = NULL) const;
#else//def ASURA_USE_KEYFRAME_CACHING
		void GetRelativeBonePosition(int iBone,float fTimer,Asura_Vector_3* pxOutputPos,Asura_Quat* pxOutputQuat) const;
#endif//def ASURA_USE_KEYFRAME_CACHING

		//Gets the amount by which movement/velocity needs to be scaled when playing this animation on the specified skin.
		//(Mainly for internal use in the animation system , since GetMovementOffset() etc will make take this into account).
		float GetMovementScale(const Asura_Hierarchy_Skin* pxSkin) const;
				

		//Gets the BB to use for culling , if this animation has one
		const Asura_Bounding_Box* GetBB() const {return m_pxBB;}
		void SetBB(const Asura_Bounding_Box* pxBB);
		float GetBoundingRadius() const {return m_fBoundingRadius;}

		void SetLoopPoint(float fLoopPoint){m_fLoopPoint = Asura_Maths::ClampToRange(fLoopPoint,0.0f,1.0f);}
		float GetLoopPoint() const{return m_fLoopPoint;}

		#ifdef ASURA_TOOLS
		//returns true if exported from Max so as to support mirroring
		bool ExportedForMirroring() const {return m_pxSkinExportTransform!=0;}

		bool IsSharedAnim() const {return BoneLengths!=0;}
		bool IsSharedAnimDeltaPos() const {return m_bSharedAnimDeltaPos;}
		bool IsSharedAnimDeltaOrient() const {return m_bSharedAnimDeltaOrient;}
		#endif


		#ifdef ASURA_TOOLS
		int CalculateTotalKeyFrames(void) const;
		void RemoveUnneededKeyFrames(float fPositionToleranceMetres = 0.001f,float fAngleToleranceDegrees = 0.01f,int* pxOutputFramesBefore = NULL,int* pxOutputFramesAfter = NULL);
		#endif
		void MakeCompressedKeyFrames(void);
		void RemakeCompressedKeyFrames(void);
		void DeleteCompressedKeyFrames(void)
		{
			// KJL - 24/06/2003 18:49:39 - quick! destroy the evidence
			Asura_ResourcePushType(ASURA_RESOURCE_ANIMATION);
			{
				Asura_ResourceDeallocateArray(m_pxCompressedQuatKeyFrames);
				Asura_ResourceDeallocateArray(m_pxCompressedTimeKeyFrames);
				Asura_ResourceDeallocateArray(m_pxCompressedBoneData);
				Asura_ResourceDeallocateArray(m_pxCompressedBoneOffsets);
			}
			Asura_ResourcePopType();
		
			m_pxCompressedQuatKeyFrames = 0;
			m_pxCompressedTimeKeyFrames = 0;
			m_pxCompressedBoneData = 0;
			m_pxCompressedBoneOffsets = 0; 

			m_uTotalNumberOfCompressedBoneOffsets = 0;
			m_uTotalNumberOfCompressedKeyFrames = 0;

		}

		//gets the sound properties (if any) from the sound ID used
		//in the animations
		const Asura_Hierarchy_Sound* GetSoundPropertiesFromAnimSoundID(int iAnimSoundID) const;

		void DetermineMovementScaleBone();

		//Returns true if there is an associated facial animation that should be played at the same time
		bool HasFacialAnimation() const {return m_bHasFacialAnimation;}

	
		// anim id, name
		Asura_String Name;
		Asura_Hash_ID m_uHashID;	// setup by Chunk Process
		int NumberOfBones;
		
		float Duration;

		float Speed;//speed of movement associated with anim. In m/s
		Asura_Angle Direction;//Euler Y value for movement direction
		
		unsigned int IsDeltaAnimation:1;//if true then frames are deltas to be added on top of another animation
		unsigned int m_bSharedAnimDeltaPos:1;//If true then the animation offsets are deltas relative to the skin rest pose.
		unsigned int m_bSharedAnimDeltaOrient:1;//If true then the orientations are also relative to the rest pose.
		unsigned int m_bHasFacialAnimation:1;//If true then there is an associated facial animation with the same ID
		#ifdef ASURA_TOOLS
		bool m_bDontRemoveKeyframes;//for flagging animations tha shouldn't lose frames to RemoveUnneededKeyFrames()
		bool m_bDontCompressKeyframes;//keyframes should be left in uncompressed format during level export
		#endif


		//Asura_Hierarchy_Bone_KeyFrame* KeyFrames;

		Asura_Hierarchy_Bone_Anim* BoneAnims;
		//Lengths of the bones in rest pose for the character that was used when exporting this animation.
		//Used to help with scaling animations when used on other characters.
		float* BoneLengths;
		//Index of bone to look at in BoneLengths array for determining how to scale the 'compass' movement
		int m_iBoneForMovementScale;

		int NumberOfAnimationFlags;
		Asura_Animation_Flag_Interval* AnimationFlags;

		//sounds to be played during the animation
		int m_iNumberOfAnimationSounds;
		Asura_Animation_Sound* m_pxAnimationSounds;

		//Wavs associated with this skin (sorted by ID)
		int m_iNumberOfSoundDefns;
		Asura_Hierarchy_Sound* m_pxSoundDefns;


		//If true , then the animation has an extra 'bone' stuck on the end of the arrays giving
		//movement data for the compass in the animation.
		bool m_bHasMovementData;

		#ifdef ASURA_TOOLS
		//Parent bone array similar to that in Asura_Hierarchy_Skin.
		//Won't exist for old animations. RAR 16/2/2005
		int* m_piParentBones;
		//Array of bone names for helping with manipulating animation files
		//when the skin isn't available. Similarly won't exist for old anims.
		Asura_String* m_pxBoneNames;
		#endif

		#ifdef ASURA_TOOLS
		//This array of quats is added to animations that use skins which had their rest pose altered
		//upon export from Max/Maya to help with mirroring.
		//The quat given here Inverse(Qn) * Qo
		//Where Qo is the original model relative quat
		//And Qn is the new (exported) model relative quat.
		//RAR 14/11/2006
		Asura_Quat* m_pxSkinExportTransform;

		//Restpose for converting animations back from deltas when importing back into Max
		//RAR 05/02/2007		
		Asura_Hierarchy_Bone_RestFrame* m_pxBoneRestPose;

		//The frame data for the delta frame that was used when converting to deltas for delta animations.
		//RAR 	24/05/2007
		Asura_Hierarchy_Bone_RestFrame* m_pxBoneDeltaPose;
		#endif



		// KJL - 18/06/2003 17:36:46 - compressed keyframes these live here rather than in
		// each individual bone to cut down on the number of allocates we do. Each bone stores
		// an indices into the arrays. The Time & Quats are in separate arrays to minimise memory
		// (put together in a struct/class the compiler would pad out to 4 byte boundaries)
		// and maximise cache coherency (you only need to traverse the time array to find the 
		// correct frame).
		Asura_Hierarchy_Bone*	m_pxCompressedBoneData;
		u_int					m_uTotalNumberOfCompressedKeyFrames;
		u_int					m_uTotalNumberOfCompressedBoneOffsets;

		Asura_Comp_Quat_Fast*	m_pxCompressedQuatKeyFrames;
		u_short*				m_pxCompressedTimeKeyFrames;
		Asura_Vector_3*			m_pxCompressedBoneOffsets;

		#ifdef ASURA_TOOLS
		//Flag for testing compressed keyframes in tools.
		//If set then each keyframe will be compressed before use when playing animations
		//so that the effects of the compression can be seen. RAR 17/09/2004
		static bool s_bPreviewCompressedKeyframes;
		#endif

		#ifdef ASURA_TOOLS
		//Normally duplicate anim chunks are skipped when loading. Setting this flag
		//will allow the newer version to overwrite the current anim.
		//RAR 12/12/2003
		bool m_bAllowOverwriteOnLoad;

		//Modified time of file this was loaded from , for helping with determining what needs to be reloaded
		//RAR 24/05/2006		
		FILETIME m_xModifiedTime;
		#endif

		#if defined(ASURA_TOOLS) && defined(ASURA_SUPPORT_PLATFORM_HASHED_FILENAMES)
		//Platform that this belongs to for platform specific skins/anims , or -1 if its the default to use.
		int m_iPlatform;
		#endif


	private :
		//Bounding box for this animation for culling purposes. If null , then the skin's BB is used instead.
		Asura_Bounding_Box* m_pxBB;
        float  m_fBoundingRadius;

		float m_fLoopPoint;

};


//some flags for RenderAnimation
enum Asura_HierarchyRenderFlags
{
    ASURA_HIERARCHY_RENDER_MASK_ENGINE               = 0xFFFFFF00,
    ASURA_HIERARCHY_RENDER_MASK_PROJECT              = 0x000000FF,

    ASURA_HIERARCHY_RENDER_UPDATEBONESALWAYS         = 0x01000000, // Update bone positions, even if not on screen.
    ASURA_HIERARCHY_RENDER_DONTRENDER                = 0x02000000, // Don't bother with the actual rendering part of things.
    ASURA_HIERARCHY_RENDER_FORCE_AMBIENT_COLOUR_ONLY = 0x04000000, // Just use RGB from the animation for ambient lighting, and nothing else.
	ASURA_HIERARCHY_RENDER_BONES_IN_WORLDSPACE		 = 0x08000000, // Bit to say that our bone data is already in world-space.
    ASURA_HIERARCHY_RENDER_NO_FRUSTUM_TEST           = 0x10000000, // Don't cull hierarchy based on frustum tests.
    ASURA_HIERARCHY_RENDER_NO_MODULELINK_TEST        = 0x20000000, // Don't cull hierarchy based on module visibilities.
    ASURA_HIERARCHY_RENDER_NO_SHADOW                 = 0x40000000, // This hierarchy doesn't require a shadow.
    ASURA_HIERARCHY_RENDER_NO_SOUND                  = 0x80000000, // Don't play anim sounds when updating/rendering.
};

template <class T> class Asura_TrackingHandlePool;

class Asura_Hierarchy_System
{
	friend class Asura_Animation;
	friend class Asura_Animation_Posable;
	friend struct Asura_SortedRenderItem_Animation;
    friend class Asura_Animation_System;
	friend class Asura_Shadow_Instance_Animation;

	public:

		//Allows projects to allocate derived skin class
		static Asura_Hierarchy_Skin* Project_AllocateSkin();

        static void Platform_RenderHierarchy( const Asura_Animation* pxAnim,
                                              u_int uCamVisFlags, u_int uRenderFlags,
                                              const Asura_Vector_3& xBSCentre, float fBSRadius, 
											  const int iTexIDOverride = ASURA_TEXTURE_NONE );
		static void Project_RenderHierarchy(  const Asura_Animation* pxAnim,
                                              u_int uCamVisFlags, u_int uRenderFlags,
                                              const Asura_Vector_3& xBSCentre, float fBSRadius );
		static void Platform_RenderSkin( const Asura_Animation* pxAnim, 
                                         float fAlpha, int iTexIDOverride,
                                         u_int uCamVisFlags, u_int uRenderFlags,
                                         const Asura_Vector_3& xBSCentre, float fBSRadius );
		static void Project_RenderSkin( const Asura_Animation* pxAnim,
                                        u_int uCamVisFlags, u_int uRenderFlags,
                                        const Asura_Vector_3& xBSCentre, float fBSRadius );

#ifdef ASURA_HIERARCHY_QUAD
		static Asura_AMatrix4x4 GetWorldspaceBoneTransform(const int iBoneIndex);
#endif

		static void Platform_RenderHierarchyShadow( const Asura_Animation& xAnim, const Asura_Shadow& xShadow, const u_int uCamVisFlags, const u_int uRenderFlags );
		static void Platform_RenderSkinShadow( const Asura_Animation& xAnim, const Asura_Shadow& xShadow, const u_int uCamVisFlags, const u_int uRenderFlags );

        static void SetNoCullingFlag( bool bOn ) { s_bNoCulling = bOn; }
        static void SetJustRenderForCurCamFlag( bool bOn ) { s_bJustRenderForCurCam = bOn; }
        static void EnableBoneUpdates( bool bEnable ) { s_bBoneUpdatesEnabled = bEnable; }

        static bool ShouldCull( void ) { return !s_bNoCulling; }
        static bool ShouldJustRenderForCurCam( void ) { return s_bJustRenderForCurCam; }
        static bool AreBoneUpdatesEnabled( void ) { return s_bBoneUpdatesEnabled; }
		
        static Asura_Matrix_3x3 GetWorldspaceBoneOri( int iBoneIdx );
        static Asura_Vector_3   GetWorldspaceBonePos( int iBoneIdx );

        // takes the bone positions/orientations in s_pxTweenStorage and converts them
        // to worldspace using data in s_pxSkin, putting the results in s_axBonePos, s_axBoneOri and s_bBonesInWorldspace
        static bool BonesToWorldspaceFromTweenStorage();

        // State for animation that's currently being processed / rendered,
        // for use by platform-specific and other code.
#if defined(ASURA_TOOLS)
		static Asura_Hierarchy_Skin*		s_pxSkin;
		static Asura_Mesh*					s_pxMesh;
#else
		static const Asura_Hierarchy_Skin*	s_pxSkin;
#endif	//defined(ASURA_TOOLS)
		static u_int						s_uSkinIndex;
        static Asura_Hierarchy_Tween*		s_pxTweenStorage;
		static bool							s_bBonesInWorldspace;

#ifdef ASURA_HIERARCHY_QUAD
		static Asura_AMatrix4x4		   s_axBoneTransforms[ ASURA_MAX_NO_OF_BONES_PER_MODEL ];
		static Asura_AMatrix4x4		   s_xBaseTransform;
		static Asura_AMatrix4x4		   s_xInverseBaseTransform;
		
		// this is a 'bit' hackish way of saving the input pos/orient across the Asura_Hierarchy_System::Render() call
		static Asura_Vector_3		   s_xOrigPosition;
		static Asura_Matrix_3x3		   s_xOrigOrient;
#else
		static const Asura_Matrix_3x3* s_pxOri;
		static const Asura_Vector_3*   s_pxPos;

		static Asura_Vector_3          s_axBonePos[ASURA_MAX_NO_OF_BONES_PER_MODEL];
		static Asura_Matrix_3x3        s_axBoneOri[ASURA_MAX_NO_OF_BONES_PER_MODEL];
#endif

	private:

        // (MP - 08.04.2003) These are only for use by Asura_Animation_System
        // so to make that clear I've made them private and made that class a
        // friend.
		static void Initialise(void);
		static void Shutdown(void);
		static void Reset();

        static void Platform_Initialise( void );
        static void Platform_Reset( void );
        static void Platform_Shutdown( void );

		static void Project_Initialise(void);
		static void Project_Shutdown(void);
		static void Project_Reset(void);

		static void SnapshotTweenData(Asura_Handle handle);

		static void RenderAnimation( Asura_Animation* pxAnim, 
                                     const Asura_Vector_3& xPos, 
                                     const Asura_Matrix_3x3& xOri,
                                     u_int uRenderFlags = 0 );

		//Final stage of rendering after bone positions and visibilities are done
		//Used by RenderAnimation or by Asura_SortedRender
		static void 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 );

		static void RenderShadow( Asura_Animation& xAnim,
								  const Asura_Shadow& xShadow,
								  const Asura_Vector_3& xPos,
								  const Asura_Matrix_3x3& xOri,
								  const u_int uRenderFlags = 0 );
		

        static bool s_bNoCulling;
        // Just render for the current camera; don't iterate through them all
        // in splitscreen modes, etc. Note that visibility is still assessed
        // unless s_bNoCulling is also set.
        static bool s_bJustRenderForCurCam;

        static bool s_bBoneUpdatesEnabled;

		#ifndef ASURA_HIERARCHY_QUAD
		static Asura_Matrix_3x3 s_xAnimOrient;
		static Asura_Vector_3 s_xAnimPos;
		#endif


};


#endif//__ASURA_HIERARCHY_H__
