//
// Asura_AnimationAttachment.cpp
//
// Created by Richard Rice - 27/01/03
// 
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Animation/Asura_AnimationAttachment.h"

#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "Entity/Asura_Entity_Renderable.h"
#include "Debugging/Asura_Logfile.h"
#ifdef ASURA_USE_INDEPENDENT_SHADOW_SYSTEM
#include "Shadows/Asura_Shadow_Instance.h"
#endif
#include "Shapes/Asura_Shapes.h"
#include "SpecialFX/Asura_Corona_System.h"

#ifdef ASURA_TARGET_SM3
#include "Projectors/Asura_Projector.h"
#include "Projectors/Asura_Projector_System.h"
#endif

#include "SpecialFX/PFX/Asura_PFX_Effect.h"
#include "SpecialFX/PFX/Asura_PFX_Entity_Effect.h"
#include "SpecialFX/PFX/Asura_PFX_System.h"

#ifdef ASURA_USE_CONTAINER_SYSTEM
#include "Entity/Container/Asura_ClientContainerInstance.h"
#include "Entity/Container/Asura_Container.h"
#include "Entity/Container/Asura_Container_System.h"
#endif //ASURA_USE_CONTAINER_SYSTEM


// C O N S T A N T S /////////////////////////////////////////////////////////

static const u_int uINITIAL_FRAMESTAMP = static_cast< u_int >( Asura_Animation::uINITIAL_FRAMESTAMP ) - 1; // make it different from the initial frame stamp in Asura_Animation to be safe

////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------**
** Asura_AnimationAttachment **
**---------------------------*/

Asura_AnimationAttachment::Asura_AnimationAttachment()
{
	m_pxNextAttachment = 0;
	m_iMarkerPointIndex = -1;
	m_uMarkerPointSkinIndex = 0;
	m_uDeleteWhenUnlinked = 0;
	m_uDeleteAttachedObjectWithParent = 0;
	m_uWorldOrientOverride = 0;
	m_uWorldPosOverride = 0;
	m_uWorldRelative = 0;
	m_pxParentAnimation = NULL;
	m_iID = DEFAULT_ID;

	m_uCachedFrameStamp = uINITIAL_FRAMESTAMP;

    m_uCanBeTestedForLOS = 0;

	m_uRenderFlagsToAdd		= 0;
	m_uRenderFlagsToRemove	= 0;
}

Asura_AnimationAttachment::Asura_AnimationAttachment(const Asura_AnimationAttachment* pxAnimAttach)
{	
	m_pxNextAttachment = NULL;
	
	m_iMarkerPointIndex = pxAnimAttach->m_iMarkerPointIndex;
	m_uMarkerPointSkinIndex = pxAnimAttach->m_uMarkerPointSkinIndex;
	m_uDeleteWhenUnlinked = pxAnimAttach->m_uDeleteWhenUnlinked;
	m_uDeleteAttachedObjectWithParent = pxAnimAttach->m_uDeleteAttachedObjectWithParent;
	m_uWorldOrientOverride = pxAnimAttach->m_uWorldOrientOverride;
	m_uWorldPosOverride = pxAnimAttach->m_uWorldPosOverride;
	m_uWorldRelative = pxAnimAttach->m_uWorldRelative;

	//  AAB:  (08/04/2004 14:07:21):  This is of course incorrect, but the clone
	// function, from which this constructor is called, will make the new chain as appropriate.
	m_pxParentAnimation = NULL;

	m_iID = pxAnimAttach->m_iID;

	m_uCachedFrameStamp = uINITIAL_FRAMESTAMP;
	m_xCachedOrient = pxAnimAttach->m_xCachedOrient;

    m_uCanBeTestedForLOS = 0;

	m_uRenderFlagsToAdd		= pxAnimAttach->m_uRenderFlagsToAdd;
	m_uRenderFlagsToRemove	= pxAnimAttach->m_uRenderFlagsToRemove;
}

bool Asura_AnimationAttachment::LinkAttachment(Asura_Animation* pxParentAnimation,const char* pszMarkerName,bool bDeleteWhenUnlinked)
{
	Asura_Assert(pszMarkerName,"");	

	return LinkAttachment( pxParentAnimation, Asura_GetHashID(pszMarkerName), bDeleteWhenUnlinked );
}

bool Asura_AnimationAttachment::LinkAttachment(Asura_Animation* pxParentAnimation,Asura_Hash_ID uMarkerHash,bool bDeleteWhenUnlinked)
{
	Asura_Assert(pxParentAnimation,"");
	Asura_Assert(uMarkerHash!=ASURA_HASH_ID_UNSET,"");
	if (m_pxParentAnimation != NULL)
	{
		Asura_Assert(!m_pxParentAnimation, "Attachment already linked to an animation");
		return false;
	}

	//get the index of the marker point
	int iIndex = -1;
	for (u_int uSkin = 0; uSkin < ASURA_ANIMATION_SKIN_COUNT; ++uSkin)
	{
		const Asura_Hierarchy_Skin* pxSkin = pxParentAnimation->GetSkin(uSkin);
		if (pxSkin && pxSkin->GetMarkerPointIndex(uMarkerHash,iIndex))
		{
			//link in the attachment
			m_pxParentAnimation = pxParentAnimation;
			m_iMarkerPointIndex = iIndex;
			m_uMarkerPointSkinIndex = uSkin;
			m_pxNextAttachment = pxParentAnimation->m_pxFirstAttachment;
			m_uDeleteWhenUnlinked = bDeleteWhenUnlinked;
			pxParentAnimation->m_pxFirstAttachment = this;
			return true;
		}
	}

	// Not present on any skin, try any animation attachments on the parent animation
			Asura_AnimationAttachment* pxAttachment = pxParentAnimation->m_pxFirstAttachment;
			while( pxAttachment )
			{
				if( pxAttachment->GetAttachedAnimation() 
				 && (pxAttachment->GetAttachedAnimation() != pxParentAnimation) ) // spot of paranoia - shouldn't ever be the case 
				{
					if( LinkAttachment( pxAttachment->GetAttachedAnimation(), uMarkerHash, false ) )
					{
						// have to set this afterward otherwise LinkAttachment tries to delete this on failure
						SetDeleteWhenUnlinked( bDeleteWhenUnlinked );
						return true;
					}
				}				

				pxAttachment = pxAttachment->GetNextAttachment();
			}

	if(bDeleteWhenUnlinked)
	{
        // (MP - 12.05.2005) We used to assert here but that was no good in
        // Rogue where we expect it to fail and handle it correctly
        // externally. The assert got downgraded to debugger output, and then
        // finally to logging to the debugger precisely once. Given it had
        // been crippled to this extent anyway, it had become pointless.
        // Just breakpoint the delete call below if you think it's an
        // issue for your project.

        delete this;
	}	
	return false;
}

void Asura_AnimationAttachment::UnlinkAttachment()
{
	if(!m_pxParentAnimation)
	{
		Asura_Assert(0,"Unlinking attachment that has yet to be linked");
		return;
	}

	//search for the attachment in the parent's list
	Asura_AnimationAttachment** ppxAttachment = &m_pxParentAnimation->m_pxFirstAttachment;
	while(*ppxAttachment && *ppxAttachment != this)
	{
		ppxAttachment = &((*ppxAttachment)->m_pxNextAttachment);
	}
	//make sure it was found
	if(!*ppxAttachment)
	{
		Asura_Assert(0,"Failed to find attachment in parent attachment list");
		return;
	}

	//remove from the list
	*ppxAttachment = (*ppxAttachment)->m_pxNextAttachment;

	m_pxParentAnimation = 0;
	m_pxNextAttachment = 0;

	if(m_uDeleteWhenUnlinked)
	{
		delete this;
	}
}

void Asura_AnimationAttachment::UnlinkAttachment( bool bAndDelete )
{
	if(bAndDelete)
	{
		m_uDeleteWhenUnlinked = true;
	}
	UnlinkAttachment();
}

void Asura_AnimationAttachment::SetID(int iID)
{
    Asura_Assert( INVALID_ID != iID, "" );
	m_iID = iID;
}

Asura_AnimationAttachment* Asura_AnimationAttachment::Clone(Asura_Animation* pxParent)
{
	Asura_AnimationAttachment* pxAttachment =  new Asura_AnimationAttachment(this);

	pxAttachment->m_pxParentAnimation = pxParent;

	//Recursively clone the list.
	if(m_pxNextAttachment)
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone(pxParent);
	}

	return pxAttachment;
}

bool Asura_AnimationAttachment::GetPosition(Asura_Vector_3* pxOutputPosition,Asura_Matrix_3x3* pxOutputOrient) const
{
	Asura_Assert(m_pxParentAnimation, "no parent animation");
	if (!m_pxParentAnimation)
	{
		return false;
	}
	if(ArePositionAndOrientCached())
	{
		//cached positions out of date , so get animation to recalculate them
		m_pxParentAnimation->UpdateAttachmentLocations();

		if(ArePositionAndOrientCached())
		{
			return false;//failed to update
		}
	}

	if(m_uWorldRelative || m_uWorldPosOverride)
		return GetWorldRelativePosition(pxOutputPosition,pxOutputOrient);
	else
		return GetParentRelativePosition(pxOutputPosition,pxOutputOrient);
}

bool Asura_AnimationAttachment::GetParentRelativePosition(Asura_Vector_3* pxOutputPosition,Asura_Matrix_3x3* pxOutputOrient) const
{
	Asura_Assert(m_pxParentAnimation, "no parent animation");
	if (!m_pxParentAnimation)
	{
		return false;
	}
	if(ArePositionAndOrientCached())
	{
		//cached positions out of date , so get animation to recalculate them
		m_pxParentAnimation->UpdateAttachmentLocations();

		if(ArePositionAndOrientCached())
		{
			return false;//failed to update
		}
	}

	Asura_Quat xParentInverseOrient;
// (MP - 07.04.2006) Causes 'may be used uninitialized' warning in GCC 4.
//	if(m_uWorldRelative || m_uWorldOrientOverride || m_uWorldPosOverride)
	{
		xParentInverseOrient = m_pxParentAnimation->GetOrientQuat();
		xParentInverseOrient.Invert();
	}

	if(pxOutputPosition)
	{
		if(m_uWorldRelative || m_uWorldPosOverride)
		{
			//need to convert world->parent relative
			*pxOutputPosition = m_xCachedPos - m_pxParentAnimation->GetPosition();
			*pxOutputPosition = xParentInverseOrient.RotateVector(*pxOutputPosition);
		}
		else
		{
			*pxOutputPosition = m_xCachedPos;
		}
	}
	if(pxOutputOrient)
	{
		if(m_uWorldRelative || m_uWorldOrientOverride || m_uWorldPosOverride)
		{
			//need to convert world->parent relative
			Asura_Quat xParentRelOrient = xParentInverseOrient * m_xCachedOrient;
			xParentRelOrient.ToMatrix(*pxOutputOrient);
		}
		else
		{
			m_xCachedOrient.ToMatrix(*pxOutputOrient);
		}
	}
	return true;	
}

bool Asura_AnimationAttachment::GetWorldRelativePosition(Asura_Vector_3* pxOutputPosition,Asura_Matrix_3x3* pxOutputOrient) const
{
	Asura_Assert(m_pxParentAnimation, "no parent animation");
	if (!m_pxParentAnimation)
	{
		return false;
	}
	if(ArePositionAndOrientCached())
	{
		//cached positions out of date , so get animation to recalculate them
		m_pxParentAnimation->UpdateAttachmentLocations();

		if(ArePositionAndOrientCached())
		{
			return false;//failed to update
		}
	}


	if(pxOutputPosition)
	{
		if(m_uWorldRelative || m_uWorldPosOverride)
		{
			*pxOutputPosition = m_xCachedPos;
		}
		else
		{
			//need to convert parent->world relative
			*pxOutputPosition = m_pxParentAnimation->GetOrientQuat().RotateVector(m_xCachedPos);
			*pxOutputPosition += m_pxParentAnimation->GetPosition();
		}
	}
	if(pxOutputOrient)
	{
		if(m_uWorldRelative || m_uWorldOrientOverride || m_uWorldPosOverride)
		{
			m_xCachedOrient.ToMatrix(*pxOutputOrient);
		}
		else
		{
			//need to convert model->world relative
			Asura_Quat xWorldRelOrient = m_pxParentAnimation->GetOrientQuat() * m_xCachedOrient;
			xWorldRelOrient.ToMatrix(*pxOutputOrient);
		}
	}
	return true;	
}


void Asura_AnimationAttachment::SetWorldRelativeOrientation(const Asura_Matrix_3x3& xOrient)
{
	m_uCachedFrameStamp = uINITIAL_FRAMESTAMP;
	m_xCachedOrient.FromMatrix(xOrient);
	m_uWorldOrientOverride = 1;
}

void Asura_AnimationAttachment::SetWorldRelativeOrientation(const Asura_Quat& xOrient)
{
	m_uCachedFrameStamp = uINITIAL_FRAMESTAMP;
	m_xCachedOrient = xOrient;
	m_uWorldOrientOverride = 1;
}

void Asura_AnimationAttachment::SetWorldRelativePosition(const Asura_Vector_3& xPos)
{
	m_uCachedFrameStamp	= uINITIAL_FRAMESTAMP;
	m_xCachedPos		= xPos;
	m_uWorldPosOverride	= 1;
}

void Asura_AnimationAttachment::ClearOrientationOverride()
{
	if(m_uWorldOrientOverride)
	{
		m_uCachedFrameStamp = uINITIAL_FRAMESTAMP;
		m_uWorldOrientOverride = 0;
	}
}

void Asura_AnimationAttachment::ClearPositionOverride()
{
	if(m_uWorldPosOverride)
	{
		m_uCachedFrameStamp	= uINITIAL_FRAMESTAMP;
		m_uWorldPosOverride	= 0;
	}
}

bool Asura_AnimationAttachment::UpdateCachedPosition(const Asura_Hierarchy_Bone_RestFrame* pxBoneData,const Asura_Hierarchy_Skin* pxSkin,bool bWorldRelative) const
{
	if( (0 <= m_iMarkerPointIndex) && (m_iMarkerPointIndex < pxSkin->NumberOfMarkerPoints) )
	{
		Asura_Vector_3 xAttachmentPos;
		Asura_Quat xAttachmentOrient;
		
		const Asura_Hierarchy_Marker_Point* pxMarker = &pxSkin->MarkerPoint[m_iMarkerPointIndex];

		//calculate attachment position
		if(pxMarker->BoneIndex == -1)
		{
			//Compass relative marker point
			xAttachmentPos = pxMarker->Position;

			//Since the marker position/orient isn't being multiplied by the bone positions , it will
			//be parent relative regardless of the state of the tween data.
			//RAR  30/30/2006
			bWorldRelative = false;
		}
		else
		{
			xAttachmentPos = pxMarker->Position;
			xAttachmentPos = pxBoneData[pxMarker->BoneIndex].Quat.RotateVector(xAttachmentPos);
			xAttachmentPos += pxBoneData[pxMarker->BoneIndex].Position;
		}

		//and orientation
		if(!m_uWorldOrientOverride)
		{
			Asura_Quat xMarkerQuat;
			xMarkerQuat.FromMatrix(pxMarker->Matrix);

			if(pxMarker->BoneIndex == -1)
			{
				//Compass relative marker point
				xAttachmentOrient = xMarkerQuat;
			}
			else
			{
				xAttachmentOrient = pxBoneData[pxMarker->BoneIndex].Quat * xMarkerQuat;
			}
		}

		SetCachedPositionAndOrient(xAttachmentPos,xAttachmentOrient,bWorldRelative);

		return true;
	}
	else
	{
		Asura_Assertf( false, ("Attachment %d on skin %s has marker point index %d - should belong to [0, %d)!",
							   GetID(), pxSkin->Name.GetString(), m_iMarkerPointIndex,
							   pxSkin->NumberOfMarkerPoints) );

		return false;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationAttachment::UpdateBoundingSphere
// Author       : Andy Tate
// Date         : 31-Jan-2007
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationAttachment::UpdateTightBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	Asura_Vector_3	xNewCentre;
	float			fNewRadius;

	if(GetTightBoundingSphere(xNewCentre, fNewRadius))
	{
		Asura_Vector_3			xDifference(xNewCentre - xCentre);
		const float				fCentreDifference = xDifference.ApproximatelyEquals( Asura_Maths::ZeroVector3 ) ? 0.0f : xDifference.Normalise();

		const float				fMaximumRadius		= fCentreDifference + fNewRadius;

		if(fMaximumRadius > fRadius)
		{
			const float fCentreDisplacement = ( fMaximumRadius - fRadius ) * 0.5f;
			xCentre	+= xDifference * fCentreDisplacement;
			fRadius	+= fCentreDisplacement;
		}
	}
}

void Asura_AnimationAttachment::UpdateCoarseBoundingSpheres( Asura_Vector_3& xCentre, float& fRadius, float& fRadiusIncEffects ) const
{
	Asura_Vector_3	xNewCentre;
	float			fNewRadius;

	if(GetCoarseBoundingSphere(xNewCentre, fNewRadius))
	{
		Asura_Vector_3			xDifference(xNewCentre - xCentre);
		const float				fCentreDifference	= xDifference.Normalise();
		const float				fMaximumRadius		= fCentreDifference + fNewRadius;
		{
			if(fMaximumRadius > fRadius)
			{
				const float fCentreDisplacement = ( fMaximumRadius - fRadius ) * 0.5f;
				xCentre	+= xDifference * fCentreDisplacement;
				fRadius	+= fCentreDisplacement;
				fRadiusIncEffects += fCentreDisplacement;
			}

			if(fMaximumRadius > fRadiusIncEffects)
			{
				fRadiusIncEffects = fMaximumRadius;
			}
		}

	}

	if(GetCoarseBoundingSphereIncludingEffects(xNewCentre, fNewRadius))
	{
		Asura_Vector_3			xDifference(xNewCentre - xCentre);
		const float				fCentreDifference	= xDifference.Normalise();
		const float				fMaximumRadius		= fCentreDifference + fNewRadius;

		if(fMaximumRadius > fRadiusIncEffects)
		{
			fRadiusIncEffects = fMaximumRadius;
		}
	}
}

/// End of function 'Asura_AnimationAttachment::UpdateBoundingSphere'. ///


void Asura_AnimationAttachment::InvalidateFrameStamp()
{
	m_uCachedFrameStamp = uINITIAL_FRAMESTAMP;
}

bool Asura_AnimationAttachment::ArePositionAndOrientCached(void) const
{
	return(m_uCachedFrameStamp != m_pxParentAnimation->GetNumTimerUpdatesAtLastBoneUpdate());
}

void Asura_AnimationAttachment::SetCachedPositionAndOrient(const Asura_Vector_3& xPos,const Asura_Quat& xQuat,bool bWorldRelative) const
{
	if(!m_uWorldPosOverride) m_xCachedPos = xPos;
	if(!m_uWorldOrientOverride) m_xCachedOrient = xQuat;

	m_uCachedFrameStamp = m_pxParentAnimation->GetNumTimerUpdatesAtLastBoneUpdate();

	m_uWorldRelative = bWorldRelative;
}

void Asura_AnimationAttachment::ApplyAnimationMovementOffset(const Asura_Animation* pxAnim) const
{
	Asura_Assert(pxAnim,"");

	Asura_Vector_3 xOffsetPos;
	Asura_Quat xOffsetOri;

	if( pxAnim->GetMovementOffset( &xOffsetPos, &xOffsetOri ) )
	{
		// Convert from animation space to attachment space

		Asura_Vector_3 xOffsetPosAttachSpace;
		xOffsetPosAttachSpace = m_xCachedOrient.RotateVector( xOffsetPos );
		m_xCachedPos += xOffsetPosAttachSpace;

		//If the world orientaiton of the attachment is being overridden , then any
		//orientation that comes from the animation is ignored. RAR 13/06/2005
		if(!m_uWorldOrientOverride)
		{
			m_xCachedOrient = m_xCachedOrient * xOffsetOri;
		}
	}
}

int Asura_AnimationAttachment::GetBoneID() const
{
	const Asura_Hierarchy_Skin* pxSkin = m_pxParentAnimation->GetSkin(m_uMarkerPointSkinIndex);
	if(pxSkin)
	{
		if( (0 <= m_iMarkerPointIndex) && (m_iMarkerPointIndex < pxSkin->NumberOfMarkerPoints) )
		{
			return pxSkin->MarkerPoint[m_iMarkerPointIndex].BoneIndex;
		}
	}
	return -1;
}

void Asura_AnimationAttachment::Render(unsigned uRenderFlags)
{
	//get position/orient
	Asura_Vector_3 xPos;
	Asura_Matrix_3x3 xMat;
	if(GetWorldRelativePosition(&xPos,&xMat))
	{
		const u_int	uModifiedRenderFlags	= (uRenderFlags & ~m_uRenderFlagsToRemove) | m_uRenderFlagsToAdd;

		//and render using these
		Render(xPos,xMat,uModifiedRenderFlags);
	}
}

void Asura_AnimationAttachment::RenderShadow( const u_int uRenderFlags )
{
	Asura_Vector_3 xPos;
	Asura_Matrix_3x3 xMat;

	if( GetWorldRelativePosition( &xPos, &xMat ) )
	{
		const u_int	uModifiedRenderFlags	= (uRenderFlags & ~m_uRenderFlagsToRemove) | m_uRenderFlagsToAdd;

		RenderShadow( xPos, xMat, uModifiedRenderFlags );
	}
}

void Asura_AnimationAttachment::RenderShadow ( Asura_Shadow& xShadow, const u_int uRenderFlags ) const
{
	Asura_Vector_3 xPos;
	Asura_Matrix_3x3 xMat;

	if( GetWorldRelativePosition( &xPos, &xMat ) )
	{
		const u_int	uModifiedRenderFlags	= (uRenderFlags & ~m_uRenderFlagsToRemove) | m_uRenderFlagsToAdd;

		RenderShadow( xShadow, xPos, xMat, uModifiedRenderFlags );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------**
** Asura_AnimationAttachment_Animation **
**-------------------------------------*/

Asura_AnimationAttachment_Animation::Asura_AnimationAttachment_Animation(Asura_Animation* pxAnimation,bool bDeleteAnimationWithParent)
{
	Asura_Assert(pxAnimation,"");

	m_uDeleteAttachedObjectWithParent =  bDeleteAnimationWithParent;
	m_bShouldDeleteAnimation = false;
	m_bUseMovementOffset	 = true;
	m_pxAnimation			 = pxAnimation;
	m_fUpdateTimeOverride	 = -1.0f;

    m_uCanBeTestedForLOS = 1;
}


Asura_AnimationAttachment_Animation::Asura_AnimationAttachment_Animation(const Asura_AnimationAttachment_Animation* pxAnimAttach)
: Asura_AnimationAttachment(pxAnimAttach)
{
	m_pxAnimation = new Asura_Animation(pxAnimAttach->m_pxAnimation);
	m_bShouldDeleteAnimation = true;
	m_bUseMovementOffset	 = true;
	m_fUpdateTimeOverride	 = -1.0f;

    m_uCanBeTestedForLOS = 1;
}


Asura_AnimationAttachment_Animation::~Asura_AnimationAttachment_Animation()
{
	if(m_bShouldDeleteAnimation)
	{
		delete m_pxAnimation;
	}
}

bool Asura_AnimationAttachment_Animation::LinkAttachment(Asura_Animation* pxParentAnimation,const char* pszMarkerName,bool bDeleteWhenUnlinked)
{
	return LinkAttachment(pxParentAnimation,Asura_GetHashID(pszMarkerName),bDeleteWhenUnlinked);
}

bool Asura_AnimationAttachment_Animation::LinkAttachment(Asura_Animation* pxParentAnimation,Asura_Hash_ID uMarkerHash,bool bDeleteWhenUnlinked)
{
	if(Asura_AnimationAttachment::LinkAttachment(pxParentAnimation,uMarkerHash,bDeleteWhenUnlinked))
	{
		//tell the child animation about its parent
		m_pxAnimation->m_pxParent = m_pxParentAnimation;
		return true;
	}
	return false;
}

void Asura_AnimationAttachment_Animation::UnlinkAttachment()
{
	//Clear the child anim's parent pointer (do it first , so we don't have to worry 
	//about whether or not this is getting deleted)
	if(m_pxAnimation)
	{
		m_pxAnimation->m_pxParent = 0;
	}

	Asura_AnimationAttachment::UnlinkAttachment();
}


void Asura_AnimationAttachment_Animation::Update(float fTime, bool bIgnoreTimerFrameStamp)
{
	// if we've specified a time override for this attachment, use it
	if( m_fUpdateTimeOverride >= 0.0f )
	{
		fTime = m_fUpdateTimeOverride;
		m_fUpdateTimeOverride = -1.0f; // override must be reset every frame
	}

	m_pxAnimation->UpdateTimers(fTime,bIgnoreTimerFrameStamp);
}


void Asura_AnimationAttachment_Animation::Render(const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xOrient,unsigned uRenderFlags)
{
	m_pxAnimation->Render(xPos,xOrient,uRenderFlags);
}

void Asura_AnimationAttachment_Animation::RenderShadow( const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, const u_int uRenderFlags )
{
	m_pxAnimation->RenderShadow( xPos, xOrient, uRenderFlags );
}

void Asura_AnimationAttachment_Animation::RenderShadow( Asura_Shadow& xShadow, const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, const u_int uRenderFlags ) const
{
#ifdef ASURA_USE_INDEPENDENT_SHADOW_SYSTEM
	Asura_Shadow_Instance_Animation	xShadowInstance(xShadow, *m_pxAnimation, uRenderFlags, false);

	xShadowInstance.Render(xShadow, xPos, xOrient);
#endif
}

bool Asura_AnimationAttachment_Animation::GetTightBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	xCentre	= m_pxAnimation->GetBoundingSphereCentre();
	fRadius	= m_pxAnimation->GetTightBoundingSphereRadius();
	return(true);
}

bool Asura_AnimationAttachment_Animation::GetCoarseBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	xCentre	= m_pxAnimation->GetBoundingSphereCentre();
	fRadius	= m_pxAnimation->GetCoarseBoundingSphereRadius();
	return(true);
}

void Asura_AnimationAttachment_Animation::OnParentDeletion()
{
	if(m_uDeleteAttachedObjectWithParent && m_pxAnimation)
	{
		//reset parent pointer to prevent this being unlinked again in
		//the Asura_Animation destructor
		m_pxAnimation->m_pxParent = 0;
		delete m_pxAnimation;
		m_pxAnimation = 0;
	}
}

void Asura_AnimationAttachment_Animation::OnParentVisibilityChanged( bool bInvisible )
{
	PARENT::OnParentVisibilityChanged( bInvisible );

	if( m_pxAnimation )
	{
		Asura_AnimationAttachment* pxAttachment = m_pxAnimation->GetFirstAttachment();
		while (pxAttachment)
		{
			pxAttachment->OnParentVisibilityChanged( bInvisible ); 
			pxAttachment = pxAttachment->GetNextAttachment();
		}
	}
}

bool Asura_AnimationAttachment_Animation::LOSTest(const Asura_Vector_3& xPosition,const Asura_Matrix_3x3 xOrient,const class Asura_LOS_InputData* pxInput,Asura_LOS_OutputData* pxOutput) const
{
	return m_pxAnimation->LOSTest(xPosition,xOrient,pxInput,pxOutput);
}

Asura_AnimationAttachment* Asura_AnimationAttachment_Animation::Clone(Asura_Animation* pxParent)
{
	Asura_AnimationAttachment_Animation* pxAttachment = new Asura_AnimationAttachment_Animation(this);

	pxAttachment->m_pxParentAnimation = pxParent;
	pxAttachment->m_pxAnimation->m_pxParent = pxParent;

	//Recursively clone the list.
	if(m_pxNextAttachment)
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone(pxParent);
	}

	return pxAttachment;
}

void Asura_AnimationAttachment_Animation::SetAlpha(float fAlpha, bool bSetAttachments)
{
	m_pxAnimation->SetAlpha(fAlpha, bSetAttachments);
}

void Asura_AnimationAttachment_Animation::SetSkin( Asura_Hash_ID uSkinID )
{
	m_pxAnimation->SetSkin( uSkinID );
}

void Asura_AnimationAttachment_Animation::SetRGB( float fR, float fG, float fB, bool bPropogateToAttachments )
{
    m_pxAnimation->SetRGB( fR, fG, fB, bPropogateToAttachments );
}

bool Asura_AnimationAttachment_Animation::UpdateCachedPosition(const Asura_Hierarchy_Bone_RestFrame* pxBoneData,const Asura_Hierarchy_Skin* pxSkin,bool bWorldRelative) const
{
	if(Asura_AnimationAttachment::UpdateCachedPosition(pxBoneData,pxSkin,bWorldRelative))
	{
		//apply movement offset
		if( m_bUseMovementOffset )
		{
			ApplyAnimationMovementOffset(m_pxAnimation);
		}
		
		return false;
	}
	return false;
}

void Asura_AnimationAttachment_Animation::SetDeleteAnimOnUnlink( bool bDelete )
{
    m_bShouldDeleteAnimation = bDelete;
    
    if( m_bShouldDeleteAnimation )
    {
        m_uDeleteAttachedObjectWithParent = false;
    }
}

void Asura_AnimationAttachment_Animation::SetUseMovementOffset( bool bUseOffset )
{
	m_bUseMovementOffset = bUseOffset;
}

void Asura_AnimationAttachment_Animation::SetUpdateTimeOverride( float fTime )
{
	m_fUpdateTimeOverride = fTime;
}

Asura_Hash_ID Asura_AnimationAttachment_Animation::GetSkinHashID( void ) const
{
	return m_pxAnimation ? m_pxAnimation->GetSkinHashID() : ASURA_HASH_ID_UNSET;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------------**
** Asura_AnimationAttachment_Corona **
**----------------------------------*/

Asura_AnimationAttachment_Corona::Asura_AnimationAttachment_Corona(Asura_Handle iCoronaHandle,bool bDeleteCoronaWithParent)
{
	Asura_Assert(iCoronaHandle != ASURA_INVALID_HANDLE,"");

	m_uDeleteAttachedObjectWithParent = bDeleteCoronaWithParent;
	m_iCoronaHandle = iCoronaHandle;

	Asura_Corona_System::SetFlag_OnlyDrawIfPositionSetPerFrame(m_iCoronaHandle, true);
}


Asura_AnimationAttachment_Corona::Asura_AnimationAttachment_Corona(const Asura_AnimationAttachment_Corona *pxAnimAttach)
: Asura_AnimationAttachment(pxAnimAttach)
{
	/// Richard May - 28/04/2005 10:06:19 - Implemented a proper corona copy functionality
	m_iCoronaHandle = Asura_Corona_System::CopyCorona( pxAnimAttach->m_iCoronaHandle );

	// force this on otherwise the corona will go awol
	m_uDeleteAttachedObjectWithParent = true;

	Asura_Corona_System::SetFlag_OnlyDrawIfPositionSetPerFrame(m_iCoronaHandle, true);
}


void Asura_AnimationAttachment_Corona::Render(const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xOrient,unsigned uRenderFlags)
{
	Asura_Corona_System::SetPosition(m_iCoronaHandle,xPos);

	Asura_Vector_3 xDir;
	xDir.x = xOrient.e13;
	xDir.y = xOrient.e23;
	xDir.z = xOrient.e33;
	Asura_Corona_System::SetDirection(m_iCoronaHandle,xDir);
}

void Asura_AnimationAttachment_Corona::OnParentDeletion()
{
	if(m_uDeleteAttachedObjectWithParent)
	{
		Asura_Corona_System::DestroyCorona(m_iCoronaHandle);
	}
}

Asura_AnimationAttachment* Asura_AnimationAttachment_Corona::Clone(Asura_Animation* pxParent)
{
	Asura_AnimationAttachment_Corona* pxAttachment = new Asura_AnimationAttachment_Corona(this);

	pxAttachment->m_pxParentAnimation = pxParent;

	//Recursively clone the list.
	if(m_pxNextAttachment)
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone(pxParent);
	}

	return pxAttachment;
}

bool Asura_AnimationAttachment_Corona::GetCoarseBoundingSphereIncludingEffects( Asura_Vector_3& xCentre, float& fRadius ) const
{
	fRadius = 0.0f;
	bool bOK = Asura_Corona_System::GetRadius( m_iCoronaHandle, fRadius );
	return GetWorldRelativePosition( &xCentre, NULL ) && bOK;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------**
** Asura_AnimationAttachment_Shape **
**---------------------------------*/

Asura_AnimationAttachment_Shape::Asura_AnimationAttachment_Shape(Asura_Hash_ID uShapeHashID)
{
	m_pxShapeInstance = new Asura_Shape_Instance(uShapeHashID);

    m_uCanBeTestedForLOS = 1;
}

Asura_AnimationAttachment_Shape::Asura_AnimationAttachment_Shape(const Asura_AnimationAttachment_Shape *pxAnimAttach)
: Asura_AnimationAttachment(pxAnimAttach)
{
	if(pxAnimAttach->m_pxShapeInstance)
	{
		m_pxShapeInstance = new Asura_Shape_Instance(pxAnimAttach->m_pxShapeInstance->GetShapeHashID());
		m_pxShapeInstance->SetNewPosAndOri(pxAnimAttach->m_pxShapeInstance->GetPosition(), pxAnimAttach->m_pxShapeInstance->GetOrientation());
	}

    m_uCanBeTestedForLOS = 1;
}

Asura_AnimationAttachment_Shape::~Asura_AnimationAttachment_Shape()
{
	delete m_pxShapeInstance;
}

bool Asura_AnimationAttachment_Shape::LinkAttachment(Asura_Animation* pxParentAnimation,Asura_Hash_ID uMarkerHash,bool bDeleteWhenUnlinked)
{
	if(Asura_AnimationAttachment::LinkAttachment(pxParentAnimation,uMarkerHash,bDeleteWhenUnlinked))
	{
		//tell the shape instance about its parent
		m_pxShapeInstance->SetParentAnimation(m_pxParentAnimation);
		return true;
	}
	return false;
}

void Asura_AnimationAttachment_Shape::UnlinkAttachment()
{
	//Clear the child anim's parent pointer (do it first , so we don't have to worry 
	//about whether or not this is getting deleted)
	if(m_pxShapeInstance)
	{
		m_pxShapeInstance->SetParentAnimation(0);
	}

	Asura_AnimationAttachment::UnlinkAttachment();
}


void Asura_AnimationAttachment_Shape::SetShape(Asura_Hash_ID uShapeHashID)
{
	m_pxShapeInstance->SetShape(uShapeHashID);
}

void Asura_AnimationAttachment_Shape::SetAlpha(float fAlpha, bool bSetAttachments)
{
	m_pxShapeInstance->SetAlpha(fAlpha);
}

void Asura_AnimationAttachment_Shape::Render(const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xOrient,unsigned uRenderFlags)
{
	m_pxShapeInstance->SetNewPosAndOri( xPos,xOrient );

    if( uRenderFlags & ASURA_HIERARCHY_RENDER_DONTRENDER ) { return; }

	/// Richard May - 13/12/2004 19:04:20 - set flags as necessary...
	
	// shadow
	if( uRenderFlags & ASURA_HIERARCHY_RENDER_NO_SHADOW )
	{
		m_pxShapeInstance->AddFlags(ASURA_SHAPE_INSTANCE_NO_SHADOW);
	}
// (MP - 06.02.2005) Doing this is probably correct but prevents attached
// shapes from turning off their shadows even when their parent has one. I
// don't think this flag generally gets turned on and off much during runtime
// so this shouldn't be a problem. And of course the real issue here is all
// the discrepancies between how skins and shapes work.
//	else
//	{
//		m_pxShapeInstance->RemoveFlags(ASURA_SHAPE_INSTANCE_NO_SHADOW);
//	}

	m_pxShapeInstance->Render();
}

void Asura_AnimationAttachment_Shape::RenderShadow( const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, const u_int uRenderFlags )
{
	m_pxShapeInstance->SetNewPosAndOri( xPos, xOrient );

	if( uRenderFlags & ASURA_HIERARCHY_RENDER_DONTRENDER ) return;
	if( uRenderFlags & ASURA_HIERARCHY_RENDER_NO_SHADOW ) return;

	m_pxShapeInstance->RenderShadow();
}

void Asura_AnimationAttachment_Shape::RenderShadow( Asura_Shadow& xShadow, const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, const u_int uRenderFlags ) const
{
#ifdef ASURA_USE_INDEPENDENT_SHADOW_SYSTEM
	Asura_Shadow_Instance_Shape	xShadowInstance(xShadow, *m_pxShapeInstance, uRenderFlags);

	m_pxShapeInstance->SetNewPosAndOri( xPos, xOrient );

	if( uRenderFlags & ASURA_HIERARCHY_RENDER_DONTRENDER ) return;
	if( uRenderFlags & ASURA_HIERARCHY_RENDER_NO_SHADOW ) return;

	// Render the shadow...
	xShadowInstance.Render(xShadow, xPos, xOrient);
#endif
}

bool Asura_AnimationAttachment_Shape::GetTightBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	const Asura_Bounding_Box&	xBB	= m_pxShapeInstance->GetBB();

	xCentre	= xBB.CalculateCentre();
	fRadius	= xBB.CalculateRadius();
	return(true);
}

bool Asura_AnimationAttachment_Shape::GetCoarseBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	const Asura_Bounding_Box&	xBB	= m_pxShapeInstance->GetBB();

	xCentre	= xBB.CalculateCentre();
	fRadius	= xBB.CalculateRadius();
	return(true);
}

bool Asura_AnimationAttachment_Shape::LOSTest(const Asura_Vector_3& xPosition,const Asura_Matrix_3x3 xOrient,const class Asura_LOS_InputData* pxInput,Asura_LOS_OutputData* pxOutput) const
{
	return m_pxShapeInstance->LOSTest(xPosition,xOrient,pxInput,pxOutput);
}

Asura_AnimationAttachment* Asura_AnimationAttachment_Shape::Clone(Asura_Animation* pxParent)
{
	Asura_AnimationAttachment_Shape* pxAttachment = new Asura_AnimationAttachment_Shape( this );

	pxAttachment->m_pxParentAnimation = pxParent;
	pxAttachment->m_pxShapeInstance->SetParentAnimation(pxParent);

	//Recursively clone the list.
	if(m_pxNextAttachment)
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone(pxParent);
	}

	return pxAttachment;
}

//////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------------------**
** Asura_AnimationAttachment_BoneRelative **
**----------------------------------------*/

Asura_AnimationAttachment_BoneRelative::Asura_AnimationAttachment_BoneRelative(const Asura_AnimationAttachment_BoneRelative* pxAnimAttach)
:Asura_AnimationAttachment(pxAnimAttach)
{
	m_iBoneID = pxAnimAttach->m_iBoneID;
	m_xBoneRelativeOrientation = pxAnimAttach->m_xBoneRelativeOrientation;
	m_xBoneRelativePosition = pxAnimAttach->m_xBoneRelativePosition;
}

bool Asura_AnimationAttachment_BoneRelative::LinkAttachment_BoneRelative(Asura_Animation *pxParentAnimation, const Asura_Vector_3& xBoneRelativePos, const Asura_Quat& xBoneRelativeOri, int iBoneID, bool bDeleteWhenUnlinked)
{
	Asura_Assert(pxParentAnimation, "Parent animation is NULL");
	Asura_Assert(!m_pxParentAnimation, "Attachment already linked to an animation");

	//m_pxParentAnimation->GetBoneRelativePositionAndOrientation(xObjectSpacePos, xObjectSpaceOri, iBoneID, m_xBoneRelativePosition, m_xBoneRelativeOrientation);
	m_xBoneRelativePosition = xBoneRelativePos;
	m_xBoneRelativeOrientation = xBoneRelativeOri;
	m_iBoneID = iBoneID;

	//link in the attachment
	m_pxParentAnimation = pxParentAnimation;
	m_pxNextAttachment = pxParentAnimation->m_pxFirstAttachment;
	m_uDeleteWhenUnlinked = bDeleteWhenUnlinked;
	pxParentAnimation->m_pxFirstAttachment = this;

	return true;
}

bool Asura_AnimationAttachment_BoneRelative::UpdateCachedPosition(const Asura_Hierarchy_Bone_RestFrame* pxBoneData,const Asura_Hierarchy_Skin* pxSkin,bool bWorldRelative) const
{
	Asura_Vector_3 xAttachmentPos;
	Asura_Quat xAttachmentOrient;

	//Transfer the bone relative position into object space.
	xAttachmentPos = m_xBoneRelativePosition;
	xAttachmentPos = pxBoneData[m_iBoneID].Quat.RotateVector(xAttachmentPos);
	xAttachmentPos += pxBoneData[m_iBoneID].Position;

	//Transfer the bone relative orientation into object space.
	xAttachmentOrient = pxBoneData[m_iBoneID].Quat * m_xBoneRelativeOrientation;

	//update in base class
	SetCachedPositionAndOrient(xAttachmentPos,xAttachmentOrient,bWorldRelative);

	return true;
}




//////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------------------------**
** Asura_AnimationAttachment_BoneRelativeAnimation **
**-------------------------------------------------*/
Asura_AnimationAttachment_BoneRelativeAnimation::Asura_AnimationAttachment_BoneRelativeAnimation( Asura_Animation* pxAnimation, bool bDeleteAnimationWithParent, bool bCanBeTestedForLOS/*=false*/ )
{
	Asura_Assert(pxAnimation,"");

	m_uDeleteAttachedObjectWithParent =  bDeleteAnimationWithParent;
	m_bShouldDeleteAnimation = false;
	m_pxAnimation = pxAnimation;
	m_uCanBeTestedForLOS = ( bCanBeTestedForLOS ? 1 : 0 );
}


Asura_AnimationAttachment_BoneRelativeAnimation::Asura_AnimationAttachment_BoneRelativeAnimation(const Asura_AnimationAttachment_BoneRelativeAnimation* pxAnimAttach)
: Asura_AnimationAttachment_BoneRelative(pxAnimAttach)
{
	m_pxAnimation = new Asura_Animation(pxAnimAttach->m_pxAnimation);
	m_bShouldDeleteAnimation = true;

}


Asura_AnimationAttachment_BoneRelativeAnimation::~Asura_AnimationAttachment_BoneRelativeAnimation()
{
	if(m_bShouldDeleteAnimation)
	{
		delete m_pxAnimation;
	}
}


bool Asura_AnimationAttachment_BoneRelativeAnimation::LinkAttachment_BoneRelative(Asura_Animation *pxParentAnimation, const Asura_Vector_3& xBoneRelativePos, const Asura_Quat& xBoneRelativeOri, int iBoneID, bool bDeleteWhenUnlinked)
{
	//mostly handled by parent class
    if(Asura_AnimationAttachment_BoneRelative::LinkAttachment_BoneRelative( pxParentAnimation, xBoneRelativePos, xBoneRelativeOri, iBoneID, bDeleteWhenUnlinked))
	{
		//tell the child animation about its parent
		m_pxAnimation->m_pxParent = pxParentAnimation;

		return true;
	}

	return false;
}


void Asura_AnimationAttachment_BoneRelativeAnimation::UnlinkAttachment()
{
	//Clear the child anim's parent pointer (do it first , so we don't have to worry 
	//about whether or not this is getting deleted)
	if(m_pxAnimation)
	{
		m_pxAnimation->m_pxParent = 0;
	}

	Asura_AnimationAttachment::UnlinkAttachment();
}

bool Asura_AnimationAttachment_BoneRelativeAnimation::LOSTest( const Asura_Vector_3& xPosition, const Asura_Matrix_3x3 xOrient, const class Asura_LOS_InputData* pxInput, class Asura_LOS_OutputData* pxOutput ) const
{
    return m_pxAnimation->LOSTest(xPosition,xOrient,pxInput,pxOutput);
}

void Asura_AnimationAttachment_BoneRelativeAnimation::Update(float fTime, bool bIgnoreTimerFrameStamp)
{
	m_pxAnimation->UpdateTimers(fTime, bIgnoreTimerFrameStamp);
}

void Asura_AnimationAttachment_BoneRelativeAnimation::Render(const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, unsigned uRenderFlags)
{
	m_pxAnimation->Render(xPos, xOrient, uRenderFlags);
}

void Asura_AnimationAttachment_BoneRelativeAnimation::RenderShadow( const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, const u_int uRenderFlags )
{
	m_pxAnimation->RenderShadow( xPos, xOrient, uRenderFlags );
}

void Asura_AnimationAttachment_BoneRelativeAnimation::RenderShadow( Asura_Shadow& xShadow, const Asura_Vector_3& xPos, const Asura_Matrix_3x3& xOrient, const u_int uRenderFlags ) const
{
#ifdef ASURA_USE_INDEPENDENT_SHADOW_SYSTEM
	Asura_Shadow_Instance_Animation	xShadowInstance(xShadow, *m_pxAnimation, uRenderFlags, false);

	xShadowInstance.Render(xShadow, xPos, xOrient);
#endif
}

bool Asura_AnimationAttachment_BoneRelativeAnimation::GetTightBoundingSphere (Asura_Vector_3& xCentre, float& fRadius) const
{
	xCentre	= m_pxAnimation->GetBoundingSphereCentre();
	fRadius	= m_pxAnimation->GetTightBoundingSphereRadius();
	return(true);
}

bool Asura_AnimationAttachment_BoneRelativeAnimation::GetCoarseBoundingSphere (Asura_Vector_3& xCentre, float& fRadius) const
{
	xCentre	= m_pxAnimation->GetBoundingSphereCentre();
	fRadius	= m_pxAnimation->GetCoarseBoundingSphereRadius();
	return(true);
}

void Asura_AnimationAttachment_BoneRelativeAnimation::OnParentDeletion()
{
	if(m_uDeleteAttachedObjectWithParent && m_pxAnimation)
	{
		//reset parent pointer to prevent this being unlinked again in
		//the Asura_Animation destructor
		m_pxAnimation->m_pxParent = 0;
		delete m_pxAnimation;
		m_pxAnimation = 0;
	}
}


Asura_AnimationAttachment* Asura_AnimationAttachment_BoneRelativeAnimation::Clone(Asura_Animation* pxParent)
{
	Asura_AnimationAttachment_BoneRelativeAnimation* pxAttachment = new Asura_AnimationAttachment_BoneRelativeAnimation(this);

	pxAttachment->m_pxParentAnimation = pxParent;
	pxAttachment->m_pxAnimation->m_pxParent = pxParent;

	//Recursively clone the list.
	if(m_pxNextAttachment)
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone(pxParent);
	}

	return pxAttachment;
}

void Asura_AnimationAttachment_BoneRelativeAnimation::SetAlpha(float fAlpha, bool bSetAttachments)
{
	m_pxAnimation->SetAlpha(fAlpha, bSetAttachments);
}


void Asura_AnimationAttachment_BoneRelativeAnimation::SetRGB( float fR, float fG, float fB, bool bPropogateToAttachments )
{
    m_pxAnimation->SetRGB( fR, fG, fB, bPropogateToAttachments );
}


bool Asura_AnimationAttachment_BoneRelativeAnimation::UpdateCachedPosition(const Asura_Hierarchy_Bone_RestFrame* pxBoneData,const Asura_Hierarchy_Skin* pxSkin,bool bWorldRelative) const
{
	if(Asura_AnimationAttachment_BoneRelative::UpdateCachedPosition(pxBoneData,pxSkin,bWorldRelative))
	{
		//apply movement offset
		ApplyAnimationMovementOffset(m_pxAnimation);
		return false;
	}
	return false;
}



#if defined(ASURA_TARGET_SM3)

////////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------**
** Asura_AnimationAttachment_Projector **
**------------------------------------------------------*/

Asura_AnimationAttachment_Projector::Asura_AnimationAttachment_Projector( int iProjectorHandle, bool bDeleteEffectWithParent )
: m_iProjectorHandle( iProjectorHandle )
{
#if defined(ASURA_ASSERT)
	const Asura_Projector* pxProjector = Asura_Projector_System::GetProjectorPointer( m_iProjectorHandle );
 	Asura_Assert((pxProjector != NULL), "Cannot find projector for attachment.");
#endif

	m_uDeleteAttachedObjectWithParent = bDeleteEffectWithParent;
}

// Called from Clone() function
Asura_AnimationAttachment_Projector::Asura_AnimationAttachment_Projector( const Asura_AnimationAttachment_Projector* pxOther )
: Asura_AnimationAttachment( pxOther )
{
	m_iProjectorHandle = pxOther->m_iProjectorHandle;
	m_uDeleteAttachedObjectWithParent = pxOther->m_uDeleteAttachedObjectWithParent;
}

Asura_AnimationAttachment_Projector::~Asura_AnimationAttachment_Projector()
{
	if( m_uDeleteAttachedObjectWithParent )
	{
		Asura_Projector* const pxProjector = Asura_Projector_System::GetProjectorPointer( m_iProjectorHandle );
		Asura_Assert( pxProjector != NULL, "Unable to find projector for destruction" );
		if( pxProjector )
		{
			Asura_Projector_System::DestroyProjector( pxProjector );
		}
	}
}

Asura_AnimationAttachment* Asura_AnimationAttachment_Projector::Clone( Asura_Animation* pxParent )
{
	Asura_AnimationAttachment_Projector* pxAttachment = new Asura_AnimationAttachment_Projector( this );

	pxAttachment->m_pxParentAnimation = pxParent;

	// Recursively clone the list.
	if( m_pxNextAttachment )
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone( pxParent );
	}

	return pxAttachment;
}


void Asura_AnimationAttachment_Projector::Update( float fTime, bool bIgnoreTimerFrameStamp )
{
	Asura_Projector* const pxProjector = Asura_Projector_System::GetProjectorPointer( m_iProjectorHandle );

	if( pxProjector )
	{
		//get world pos and orient
		Asura_Vector_3 xPos( Asura_Maths::ZeroVector3 );
		Asura_Matrix_3x3 xOri( Asura_Maths::IdentityMatrix3x3 );

		if ( GetWorldRelativePosition( &xPos, &xOri ) )
		{
			pxProjector->SetPosition( xPos );
			pxProjector->SetOrientation( xOri );
			pxProjector->UpdateVertices();
		}
	}
}

bool Asura_AnimationAttachment_Projector::GetCoarseBoundingSphereIncludingEffects( Asura_Vector_3& xCentre, float& fRadius ) const
{
	Asura_Projector* const pxProjector = Asura_Projector_System::GetProjectorPointer( m_iProjectorHandle );

	if( pxProjector )
	{
		fRadius = pxProjector->GetBoundingBox().CalculateRadius();
		xCentre = pxProjector->GetBoundingBox().CalculateCentre();
		return true;
	}
	return false;
}

#endif // ASURA_TARGET_SM3


////////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------**
** Asura_AnimationAttachment_PFX **
**------------------------------------------------------*/

// IMPORTANT: the effect ID must be an entity effect! Will crash otherwise since there's no way currently to
// verify this!
Asura_AnimationAttachment_PFX::Asura_AnimationAttachment_PFX( u_int uEffectGuid, bool bDeleteEffectWithParent ):
	m_uEffectGuid( uEffectGuid )
{
#if defined(ASURA_ASSERT)
	const Asura_PFX_Entity_Effect* pxEffect = Asura_PFX_System::GetEntityEffect( m_uEffectGuid );

	Asura_Assert((pxEffect != NULL), "Cannot find PFX effect for attachment.");
#endif

	m_uStartOnFirstUpdate = false;
	m_uDeleteWhenFinished = true;
	m_uDeleteAttachedObjectWithParent = bDeleteEffectWithParent;
}

Asura_AnimationAttachment_PFX::Asura_AnimationAttachment_PFX( Asura_Hash_ID uTemplateID,unsigned short usPFXFlags, Asura_Guid uParentGuid)
{
	//Start inactive , since we won't have a position until the first update.
	m_uEffectGuid = Asura_PFX_Entity_Effect::Create(Asura_Maths::ZeroVector3, Asura_Maths::IdentityMatrix3x3, uTemplateID, 0, ASURA_PFX_EFFECT_START_INACTIVE | ASURA_PFX_EFFECT_CAMERA_FLAGS | usPFXFlags, uParentGuid);
	m_uStartOnFirstUpdate = true;
	m_uDeleteWhenFinished = true;
	m_uDeleteAttachedObjectWithParent = true;
}

// Called from Clone() function
Asura_AnimationAttachment_PFX::Asura_AnimationAttachment_PFX( const Asura_AnimationAttachment_PFX* pxOther ):
	Asura_AnimationAttachment( pxOther )
{
	m_uEffectGuid = pxOther->m_uEffectGuid;
	m_uStartOnFirstUpdate = false;
	m_uDeleteWhenFinished = true;
}

Asura_AnimationAttachment_PFX::~Asura_AnimationAttachment_PFX()
{
	if( m_uDeleteAttachedObjectWithParent )
	{
		Asura_PFX_Effect* const pxEffect = Asura_PFX_System::GetEffect( m_uEffectGuid );

		if( pxEffect )
		{
			pxEffect->Destroy();
		}
	}
}

Asura_AnimationAttachment* Asura_AnimationAttachment_PFX::Clone( Asura_Animation* pxParent )
{
	Asura_AnimationAttachment_PFX* pxAttachment = new Asura_AnimationAttachment_PFX( this );	

	pxAttachment->m_pxParentAnimation = pxParent;

	// Recursively clone the list.
	if( m_pxNextAttachment )
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone( pxParent );
	}

	return pxAttachment;
}


void Asura_AnimationAttachment_PFX::Update( float fTime, bool bIgnoreTimerFrameStamp )
{
	Asura_PFX_Entity_Effect* const pxEffect = Asura_PFX_System::GetEntityEffect( m_uEffectGuid );

	if( pxEffect )
	{
		// (NY - 30.05.2007) don't update or start pfx unless pos and ori are valid
		if ( GetWorldRelativePosition( NULL, NULL ) )
		{
		if(m_uStartOnFirstUpdate)
		{
			pxEffect->Start();
			m_uStartOnFirstUpdate = false;
		}
		}

	}
}

void Asura_AnimationAttachment_PFX::Render( const Asura_Vector_3& xPos,const Asura_Matrix_3x3& xOrient,unsigned uRenderFlags )
{
	// we do this in the render function so that the bone data is up to date
	Asura_PFX_Entity_Effect* const pxEffect = Asura_PFX_System::GetEntityEffect( m_uEffectGuid );
	if( pxEffect )
	{
		pxEffect->SetPosition( xPos, xOrient );
	}
}

bool Asura_AnimationAttachment_PFX::NeedsDeleting (void) const
{
	if(!m_uDeleteWhenFinished) return false;

	const Asura_PFX_Entity_Effect*	pxEffect	= Asura_PFX_System::GetEntityEffect(m_uEffectGuid);

	return(pxEffect == NULL);
}

void Asura_AnimationAttachment_PFX::OnParentVisibilityChanged( bool bInvisible )
{
	if (m_uEffectGuid == ASURA_PFX_INVALID_EFFECT_ID) return;

	Asura_PFX_Entity_Effect* pxEffect = Asura_PFX_System::GetEntityEffect( m_uEffectGuid );
	Asura_Assert(pxEffect,"can't find associated pfx effect");

	if (pxEffect) pxEffect->SetInvisibleByAttachment(bInvisible);
}

bool Asura_AnimationAttachment_PFX::GetCoarseBoundingSphereIncludingEffects( Asura_Vector_3& xCentre, float& fRadius ) const
{
	Asura_PFX_Entity_Effect* pxEffect = Asura_PFX_System::GetEntityEffect( m_uEffectGuid );
	//  not asserting as it's valid for a PFX attachment to have no effect, that's how they get destroyed.
	if (pxEffect)
	{
		fRadius = pxEffect->GetBoundingBox().CalculateRadius();
		xCentre = pxEffect->GetBoundingBox().CalculateCentre();
		return true;
	}
	return false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------------------------------------**
** Asura_AnimationAttachment_BoneRelativePFX **
**------------------------------------------------------*/

// IMPORTANT: the effect ID must be an entity effect! Will crash otherwise since there's no way currently to
// verify this!
Asura_AnimationAttachment_BoneRelativePFX::Asura_AnimationAttachment_BoneRelativePFX( u_int uEffectGuid, bool bDeleteEffectWithParent ):
	m_uEffectGuid( uEffectGuid )
{
#if defined(ASURA_ASSERT)
	const Asura_PFX_Effect*	pxEffect	= Asura_PFX_System::GetEffect( m_uEffectGuid );

	Asura_Assert((pxEffect != NULL), "Cannot find PFX effect for attachment.");
#endif

	m_uStartOnFirstUpdate = false;
	m_uDeleteWhenFinished = true;
	m_uDeleteAttachedObjectWithParent = bDeleteEffectWithParent;
}

Asura_AnimationAttachment_BoneRelativePFX::Asura_AnimationAttachment_BoneRelativePFX( Asura_Hash_ID uTemplateID,unsigned short usPFXFlags, Asura_Guid uParentGuid)
{
	//Start inactive , since we won't have a position until the first update.
	m_uEffectGuid = Asura_PFX_Entity_Effect::Create(Asura_Maths::ZeroVector3, Asura_Maths::IdentityMatrix3x3, uTemplateID, 0, ASURA_PFX_EFFECT_START_INACTIVE | ASURA_PFX_EFFECT_CAMERA_FLAGS | usPFXFlags, uParentGuid);
	m_uStartOnFirstUpdate = true;
	m_uDeleteWhenFinished = true;
	m_uDeleteAttachedObjectWithParent = true;
}

// Called from Clone() function
Asura_AnimationAttachment_BoneRelativePFX::Asura_AnimationAttachment_BoneRelativePFX( const Asura_AnimationAttachment_BoneRelativePFX* pxOther ):
	Asura_AnimationAttachment_BoneRelative( pxOther )
{
	m_uStartOnFirstUpdate = false;
	m_uDeleteWhenFinished = true;
	m_uEffectGuid = pxOther->m_uEffectGuid;
}

Asura_AnimationAttachment_BoneRelativePFX::~Asura_AnimationAttachment_BoneRelativePFX()
{
	if( m_uDeleteAttachedObjectWithParent )
	{
		Asura_PFX_Effect* const pxEffect = Asura_PFX_System::GetEffect( m_uEffectGuid );

		if( pxEffect )
		{
			pxEffect->Destroy();
		}
	}
}

Asura_AnimationAttachment* Asura_AnimationAttachment_BoneRelativePFX::Clone( Asura_Animation* pxParent )
{
	Asura_AnimationAttachment_BoneRelativePFX* pxAttachment = new Asura_AnimationAttachment_BoneRelativePFX( this );

	pxAttachment->m_pxParentAnimation = pxParent;

	// Recursively clone the list.
	if( m_pxNextAttachment )
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone( pxParent );
	}

	return pxAttachment;
}


void Asura_AnimationAttachment_BoneRelativePFX::Update( float fTime, bool bIgnoreTimerFrameStamp )
{
	//const Asura_Entity_Renderable* const pxParentEntity = Asura_Entity_Renderable::FindEntity( m_pxParentAnimation->GetParentEntityGUID() );

	Asura_PFX_Entity_Effect* const pxEffect = static_cast< Asura_PFX_Entity_Effect* >( Asura_PFX_System::GetEffect( m_uEffectGuid ) );

	// Messy without a dynamic_cast. We don't actually have any proof this is an entity effect, but we have to
	// have one since we can't set positions & orientations in the base class

	if( pxEffect )
	{
		//get world pos and orient
		Asura_Vector_3 xPos( Asura_Maths::ZeroVector3 );
		Asura_Matrix_3x3 xOri( Asura_Maths::IdentityMatrix3x3 );
		// (NY - 30.05.2007) don't update or start pfx unless pos and ori are valid
		if (GetWorldRelativePosition(&xPos,&xOri))
		{
			pxEffect->SetPosition( xPos, xOri );

			if(m_uStartOnFirstUpdate)
			{
				pxEffect->Start();
				m_uStartOnFirstUpdate = false;
			}
		}

		
	}
}

bool Asura_AnimationAttachment_BoneRelativePFX::NeedsDeleting (void) const
{
	if(!m_uDeleteWhenFinished) return false;

	const Asura_PFX_Entity_Effect*	pxEffect	= static_cast<const Asura_PFX_Entity_Effect*>(Asura_PFX_System::GetEffect(m_uEffectGuid));

	return(pxEffect == NULL);
}

void Asura_AnimationAttachment_BoneRelativePFX::OnParentVisibilityChanged( bool bInvisible )
{
	if (m_uEffectGuid == ASURA_PFX_INVALID_EFFECT_ID) return;

	Asura_PFX_Entity_Effect* pxEffect = Asura_PFX_System::GetEntityEffect( m_uEffectGuid );
	Asura_Assert(pxEffect,"can't find associated pfx effect");

	if (pxEffect) pxEffect->SetInvisibleByAttachment(bInvisible);
}

bool Asura_AnimationAttachment_BoneRelativePFX::GetCoarseBoundingSphereIncludingEffects( Asura_Vector_3& xCentre, float& fRadius ) const
{
	Asura_PFX_Entity_Effect* pxEffect = Asura_PFX_System::GetEntityEffect( m_uEffectGuid );
	//  not asserting as it's valid for a PFX attachment to have no effect, that's how they get destroyed.
	if (pxEffect)
	{
		fRadius = pxEffect->GetBoundingBox().CalculateRadius();
		xCentre = pxEffect->GetBoundingBox().CalculateCentre();
		return true;
	}
	return false;
}



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationAttachment_Sticky::Asura_AnimationAttachment_Sticky
// Author       : Andy Tate
// Date         : 6-Nov-2006
////////////////////////////////////////////////////////////////////////////////

Asura_AnimationAttachment_Sticky::Asura_AnimationAttachment_Sticky (Asura_Animation* pxAnimation,
																	const bool bDeleteAnimationWithParent,
																	const u_int uFixedBoneID,
																	const Asura_Vector_3 &xWorldSpacePosition)
: PARENT(pxAnimation, bDeleteAnimationWithParent)
, m_uFlags(0)
, m_xBoneOffset(xWorldSpacePosition)
{
	m_uStickyBoneID	= uFixedBoneID;
}

/// End of function 'Asura_AnimationAttachment_Sticky::Asura_AnimationAttachment_Sticky'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationAttachment_Sticky::Update
// Author       : Andy Tate
// Date         : 6-Nov-2006
////////////////////////////////////////////////////////////////////////////////

void Asura_AnimationAttachment_Sticky::Update (float fTime, bool bIgnoreTimerFrameStamp)
{
	// Do we need to calculate the bone-offset?
	if(!m_bHasBoneOffset)
	{
		Asura_Vector_3		xBonePosition;
		Asura_Matrix_3x3	xBoneOrientation;

		if(m_pxAnimation->GetBoneLocation(m_uStickyBoneID, &xBonePosition, &xBoneOrientation, true))
		{
			// Transform the position into bone-space...
			xBoneOrientation.Transpose();

			m_xBoneOffset		-= xBonePosition;
			m_xBoneOffset		 = m_xBoneOffset * xBoneOrientation;
			m_bHasBoneOffset	 = true;
		}
	}

	// Call the base-function...
	PARENT::Update(fTime, bIgnoreTimerFrameStamp);

	// Do we need to calculate the orientation?
	if(!m_bHasRotation)
	{
		const Asura_Quat&	xCurrentQuat	= m_pxAnimation->GetOrientQuat();
		Asura_Quat			xParentQuat(m_pxParentAnimation->GetOrientQuat());

		xParentQuat.Invert();

		m_xRotation		= xCurrentQuat * xParentQuat;
		m_bHasRotation	= true;
	}
}

/// End of function 'Asura_AnimationAttachment_Sticky::Update'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : Asura_AnimationAttachment_Sticky::UpdateCachedPosition
// Author       : Andy Tate
// Date         : 6-Nov-2006
////////////////////////////////////////////////////////////////////////////////

bool Asura_AnimationAttachment_Sticky::UpdateCachedPosition (const Asura_Hierarchy_Bone_RestFrame* pxBoneData,
															  const Asura_Hierarchy_Skin* pxSkin,
															  bool bWorldRelative) const
{
	Asura_Matrix_3x3	xBoneOrientation;
	Asura_Vector_3		xBonePosition;

	// Update the world-relative using this animations tween-store...
	bWorldRelative	= (bWorldRelative || m_pxAnimation->GetTweenStore()->IsPositionDataInWorldspace());

	// Calculate the child's bone offset...
	if( m_bHasRotation &&
		m_bHasBoneOffset &&
		m_pxAnimation->GetBoneLocation(m_uStickyBoneID, &xBonePosition, &xBoneOrientation, bWorldRelative) )
	{
		Asura_Matrix_3x3	xOrientation;
		Asura_Vector_3		xPosition;
		Asura_Vector_3		xBoneOffset(m_xBoneOffset * xBoneOrientation);

		// Is our attachment world-relative?
		if(bWorldRelative)
		{
			// Apply the original bone-offset...
			xPosition	= xBonePosition + xBoneOffset;

			// Do we have a marker-point to use?
			if(m_iMarkerPointIndex >= 0)
			{
				Asura_Vector_3	xMarkerPosition;

				// Yes - get the data and use it...
				if(m_pxParentAnimation->GetMarkerLocation(m_iMarkerPointIndex, m_uMarkerPointSkinIndex, &xMarkerPosition, &xOrientation, true))
				{
					xPosition	= (xMarkerPosition * 2.0f) - xPosition;
				}
				else
				{
					xPosition	= (m_pxParentAnimation->GetPosition() * 2.0f) - xPosition;
				}
			}
			else
			{
				xPosition	= (m_pxParentAnimation->GetPosition() * 2.0f) - xPosition;
			}
		}
		else
		{
			// Apply the original bone-offset...
			xBonePosition	+= xBoneOffset;
			xPosition = m_xRotation.RotateVector(xBonePosition);

			// Do we have a marker-point to use?
			if(m_iMarkerPointIndex >= 0)
			{
				Asura_Vector_3	xMarkerPosition;

				// Yes - get the data and use it...
				if(m_pxParentAnimation->GetMarkerLocation(m_iMarkerPointIndex, m_uMarkerPointSkinIndex, &xMarkerPosition, NULL, false))
				{
					m_xRotation.ToMatrix(xOrientation);
					xPosition	= xMarkerPosition - xPosition;
				}
				else
				{
					m_xRotation.ToMatrix(xOrientation);
					xPosition	= -xPosition;
				}
			}
			else
			{
				m_xRotation.ToMatrix(xOrientation);
				xPosition	= -xPosition;
			}
		}

		// Apply this to the child...
		SetCachedPositionAndOrient(xPosition, Asura_Quat(xOrientation), bWorldRelative);
		return(true);
	}
	else
	{
		return(PARENT::UpdateCachedPosition(pxBoneData, pxSkin, bWorldRelative));
	}
}

/// End of function 'Asura_AnimationAttachment_Sticky::UpdateCachedPosition'. ///


///////////////////////////////////////////////////////////////////////////////
// Asura_AnimationAttachment_ContainerInstance_Internal
///////////////////////////////////////////////////////////////////////////////
#ifdef ASURA_USE_CONTAINER_SYSTEM

void Asura_AnimationAttachment_ContainerInstance_Internal::Render(const Asura_Vector_3& xPos,
														 const Asura_Matrix_3x3& xOrient,
														 unsigned uRenderFlags)
{
	if ( (ASURA_HIERARCHY_RENDER_DONTRENDER & uRenderFlags) != 0 &&
		 (ASURA_HIERARCHY_RENDER_UPDATEBONESALWAYS & uRenderFlags) != 0)
	{
		// We don't want TeleportTo to be passed on to attached containers as Render.
		return;
	}

	if (!m_pxContainerInstance) return;

	Asura_ClientContainerInstance* const pxChild = m_pxContainerInstance->GetChild();
	if (!pxChild) return;
	
	// Apply any extra offset from the marker attachment, then render the child.
	const Asura_Container_Attachment* pxAttachment = m_pxContainerInstance->GetContainer();
	if (pxAttachment && pxAttachment->HasOffset())
	{
		Asura_Vector_3 xNewPos;
		Asura_Matrix_3x3 xNewOri;
		// Don't recalculate the marker point.
		pxAttachment->ApplyOffset(xPos, xOrient, xNewPos, xNewOri, 0);
		pxChild->Render(xNewPos, xNewOri);
	}
	else
	{
		pxChild->Render(xPos, xOrient);
	}
}

void Asura_AnimationAttachment_ContainerInstance_Internal::RenderShadow(Asura_Shadow& xShadow,
															   const Asura_Vector_3& xPos,
															   const Asura_Matrix_3x3& xOrient,
															   const u_int uRenderFlags ) const
{
	if (m_pxContainerInstance && m_pxContainerInstance->GetChild())
	{
		m_pxContainerInstance->GetChild()->RenderShadow(&xShadow);
	}
}

bool Asura_AnimationAttachment_ContainerInstance_Internal::GetTightBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	if (m_pxContainerInstance)
	{
		return m_pxContainerInstance->GetTightBoundingSphere(fRadius, xCentre);
	}

	return false;
}

bool Asura_AnimationAttachment_ContainerInstance_Internal::GetCoarseBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	if (m_pxContainerInstance)
	{
		return m_pxContainerInstance->GetCoarseBoundingSphere(fRadius, xCentre);
	}

	return false;
}

bool Asura_AnimationAttachment_ContainerInstance_Internal::GetCoarseBoundingSphereIncludingEffects(Asura_Vector_3& xCentre, float& fRadius) const
{
	if (m_pxContainerInstance)
	{
		return m_pxContainerInstance->GetCoarseBoundingSphereIncludingEffects(fRadius, xCentre);
	}

	return false;
}

Asura_AnimationAttachment* Asura_AnimationAttachment_ContainerInstance_Internal::Clone(Asura_Animation* pxParent)
{
	// Currently it isn't possible to attach containers to non-containers.
	// If we implement that then we'll need to implement this function fully.
	// MGH 25/6/2008: TODO implement cloning container attachments.
	Asura_Assert(0, "Cloning of Asura_AnimationAttachment_ContainerInstance_Internal not fully implemented");
	Asura_AnimationAttachment_ContainerInstance_Internal* pxAttachment = new Asura_AnimationAttachment_ContainerInstance_Internal(0);

	pxAttachment->m_pxParentAnimation = pxParent;

	// Recursively clone the list.
	if(m_pxNextAttachment)
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone(pxParent);
	}

	return pxAttachment;
}


///////////////////////////////////////////////////////////////////////////////
// Asura_AnimationAttachment_ClientContainerInstance
///////////////////////////////////////////////////////////////////////////////
// Animation attachment which allows things other than the container system to attach container instances
// to their animations. This class will create and destroy a container instance as needed.

Asura_AnimationAttachment_ClientContainerInstance::Asura_AnimationAttachment_ClientContainerInstance(Asura_Hash_ID uContainerHash,
																									 const Asura_Container_InputState& xInputState,
																									 Asura_Guid uOwnerGuid /*=ASURA_GUID_UNREGISTERED*/)
: m_pxClientInstance(0)
, m_uContainerHash(uContainerHash)
, m_xInputState(xInputState)
, m_uOwnerGuid(uOwnerGuid)
, m_uFlags(0)
{
	SetCanBeTestedForLOS(false);
	CreateInstance();
}

Asura_AnimationAttachment_ClientContainerInstance::Asura_AnimationAttachment_ClientContainerInstance(const Asura_AnimationAttachment_ClientContainerInstance* pxOther)
: Asura_AnimationAttachment(pxOther)
, m_pxClientInstance(0)
, m_uContainerHash(pxOther->m_uContainerHash)
, m_xInputState(pxOther->m_xInputState)
, m_uOwnerGuid(pxOther->m_uOwnerGuid)
, m_uFlags(0)
{
	SetCanBeTestedForLOS(false);
	CreateInstance();
}

Asura_AnimationAttachment_ClientContainerInstance::~Asura_AnimationAttachment_ClientContainerInstance()
{
	DestroyInstance(false);
}

/////////////////
// ChangeState //
/////////////////
void Asura_AnimationAttachment_ClientContainerInstance::ChangeState(const Asura_Container_InputState& xInputState)
{
	if (m_xInputState != xInputState)
	{
		// Change the state.
		m_xInputState = xInputState;

		if (m_bParentInvisible)
		{
			// Should already be destroyed, but make sure.
			DestroyInstance(true);
		}
		else
		{
			// Create or update the instance.
			CreateInstance();
		}
	}
}

///////////////////////////////
// OnParentVisibilityChanged //
///////////////////////////////
void Asura_AnimationAttachment_ClientContainerInstance::OnParentVisibilityChanged(bool bInvisible)
{
	if (bInvisible)
	{
		// Destroy the instance. Perhaps we should just disable rendering instead.
		DestroyInstance(true);
	}
	else if (!m_pxClientInstance)
	{
		CreateInstance();
	}

	m_bParentInvisible = bInvisible;
}

////////////
// Update //
////////////
void Asura_AnimationAttachment_ClientContainerInstance::Update(float fTime, bool bIgnoreTimerFrameStamp)
{
	if (m_pxClientInstance && m_bShouldCallUpdate)
	{
		m_pxClientInstance->Update();
	}
}

////////////
// Render //
////////////
void Asura_AnimationAttachment_ClientContainerInstance::Render(const Asura_Vector_3& xPos,
														 const Asura_Matrix_3x3& xOrient,
														 unsigned uRenderFlags)
{
	// Make sure that we have called TeleportTo, which initialises a number of things.
	if (m_pxClientInstance && !m_bTeleportInitialised)
	{
		m_pxClientInstance->TeleportTo(xPos, xOrient);
		m_bTeleportInitialised = true;
	}

	if (ASURA_HIERARCHY_RENDER_DONTRENDER & uRenderFlags)
	{
		return;
	}

	if (m_pxClientInstance && m_bShouldCallRender)
	{
		m_pxClientInstance->Render(xPos, xOrient);
	}
}

//////////////////
// RenderShadow //
//////////////////
void Asura_AnimationAttachment_ClientContainerInstance::RenderShadow(Asura_Shadow& xShadow,
															   const Asura_Vector_3& xPos,
															   const Asura_Matrix_3x3& xOrient,
															   const u_int uRenderFlags ) const
{
	if (ASURA_HIERARCHY_RENDER_DONTRENDER & uRenderFlags)
	{
		return;
	}

	if (m_pxClientInstance && m_bShouldCallRenderShadow)
	{
		m_pxClientInstance->RenderShadow(&xShadow);
	}
}

///////////////////////
// GetBoundingSphere //
///////////////////////
bool Asura_AnimationAttachment_ClientContainerInstance::GetTightBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	if (m_pxClientInstance)
	{
		return m_pxClientInstance->GetTightBoundingSphere(fRadius, xCentre);
	}

	return false;
}

bool Asura_AnimationAttachment_ClientContainerInstance::GetCoarseBoundingSphere(Asura_Vector_3& xCentre, float& fRadius) const
{
	if (m_pxClientInstance)
	{
		return m_pxClientInstance->GetCoarseBoundingSphere(fRadius, xCentre);
	}

	return false;
}

bool Asura_AnimationAttachment_ClientContainerInstance::GetCoarseBoundingSphereIncludingEffects(Asura_Vector_3& xCentre, float& fRadius) const
{
	if (m_pxClientInstance)
	{
		return m_pxClientInstance->GetCoarseBoundingSphereIncludingEffects(fRadius, xCentre);
	}

	return false;
}

////////////////////
// PreStateChange //
////////////////////
// Called just before a state change.
void Asura_AnimationAttachment_ClientContainerInstance::PreStateChange()
{
	Asura_Container_System::SetDoingStateChange(true);
}

/////////////////////
// PostStateChange //
/////////////////////
// Called just after a state change.
void Asura_AnimationAttachment_ClientContainerInstance::PostStateChange()
{
	Asura_Container_System::SetDoingStateChange(false);
}

////////////////////
// CreateInstance //
////////////////////
void Asura_AnimationAttachment_ClientContainerInstance::CreateInstance()
{
	const bool bTurnOff = m_xInputState.IsOff();

	if (m_pxClientInstance)
	{
		// We are unlikely to ever want to change container type, but just in case.
		// Also destroy instance if it is being switched "off".
		if (m_pxClientInstance->GetContainerHash() != m_uContainerHash || bTurnOff)
		{
			DestroyInstance(true);
		}
	}

	if (!m_pxClientInstance && !bTurnOff)
	{
		// Create the instance from scratch.
		m_pxClientInstance = Asura_Container_System::CreateClientInstance(m_uContainerHash);
	}


	if (m_pxClientInstance)
	{
		// Change to new state, creating and destroying sub-containers as necessary.
		// Also unlink/relink marker attachments as needed.
		// We've already destroyed the instance for the "off" case, so the root container must be on.
		PreStateChange();
		m_pxClientInstance->ChangeState(m_xInputState);
		m_pxClientInstance->SetAttachedToMarkerPoint();
		PostStateChange();
	
		// Collect data about the instance and initialise it.
		Asura_ContainerInstance_Properties xData(0, m_uOwnerGuid);

		m_pxClientInstance->Initialise(0, xData);

		// Set up the flags from the data.
		m_bTeleportInitialised		= false;
		m_bShouldCallRender			= (xData.m_iNumRender > 0);
		m_bShouldCallRenderShadow	= (xData.m_iNumRenderShadow > 0);
		m_bShouldCallUpdate			= (xData.m_iNumUpdate > 0);
		m_bEnableLOSTest			= (xData.m_iNumLOS > 0);

#ifdef ASURA_TOOLS
		m_bShouldCallRender = true; // Always render in Tools.
		m_bShouldCallUpdate = true; // Always update in Tools.
#endif //ASURA_TOOLS
	}
	else
	{
		// Clear most of the flags now.
		m_bTeleportInitialised		= false;
		m_bShouldCallRender			= false;
		m_bShouldCallRenderShadow	= false;
		m_bShouldCallUpdate			= false;
		m_bEnableLOSTest			= false;
	}

	SetCanBeTestedForLOS(m_bEnableLOSTest);

	Asura_Assertf(m_pxClientInstance || m_uContainerHash == ASURA_HASH_ID_UNSET || m_xInputState.IsOff(), ("Failed to create container instance (0x%08x)", m_uContainerHash));
}

/////////////////////
// DestroyInstance //
/////////////////////
void Asura_AnimationAttachment_ClientContainerInstance::DestroyInstance(bool bChangingState)
{
	if (bChangingState)
	{
		PreStateChange();
	}

	Asura_Container_System::DestroyInstance(m_pxClientInstance);
	m_pxClientInstance = 0;

	if (bChangingState)
	{
		PostStateChange();
	}

	SetCanBeTestedForLOS(false);
}

///////////
// Clone //
///////////
Asura_AnimationAttachment* Asura_AnimationAttachment_ClientContainerInstance::Clone(Asura_Animation* pxParent)
{	
	// Clone the client instance.
	Asura_AnimationAttachment_ClientContainerInstance* pxAttachment = new Asura_AnimationAttachment_ClientContainerInstance(this);

	pxAttachment->m_pxParentAnimation = pxParent;

	// Recursively clone the list.
	if (m_pxNextAttachment)
	{
		pxAttachment->m_pxNextAttachment = m_pxNextAttachment->Clone(pxParent);
	}

	return pxAttachment;
}

/////////////
// LOSTest //
/////////////
bool Asura_AnimationAttachment_ClientContainerInstance::LOSTest(const Asura_Vector_3& xPosition,
																const Asura_Matrix_3x3 xOrient,
																const Asura_LOS_InputData* pxInput,
																Asura_LOS_OutputData* pxOutput) const
{
	if ( m_bParentInvisible || !m_bTeleportInitialised || !m_bEnableLOSTest )
	{
		return false;
	}

	if (pxInput->m_uEntityToIgnore == GetOwnerGuid())
	{
		return false;
	}

	if (!pxInput || !pxOutput)
	{
		Asura_Assert(0,"Null pointer");
		return false;
	}

	if (m_pxClientInstance)
	{
		if (m_pxClientInstance->LOSTest(pxInput, pxOutput))
		{
			// A closer hit. Set the guid.
			pxOutput->m_uHitEntity = GetOwnerGuid();
			// Overwrite the attachment ID which contained the unique ID for the sub-container.
			pxOutput->m_iHitAttachmentID = Asura_AnimationAttachment::INVALID_ID;
			return true;
		}
	}

	return false;
}

#endif //ASURA_USE_CONTAINER_SYSTEM


/// End of file. ///
