/* 
 * 
 * Confidential Information of Telekinesys Research Limited (t/a Havok).  Not for disclosure or distribution without Havok's
 * prior written consent.This software contains code, techniques and know-how which is confidential and proprietary to Havok.
 * Level 2 and Level 3 source code contains trade secrets of Havok. Havok Software (C) Copyright 1999-2008 Telekinesys Research Limited t/a Havok. All Rights Reserved. Use of this software is subject to the terms of an end user license agreement.
 * 
 */

#ifndef HK_KEYFRAME_HIERARCHY_UTILITY_H
#define HK_KEYFRAME_HIERARCHY_UTILITY_H

#include <Common/Base/hkBase.h>
#include <Common/Base/Reflection/Attributes/hkAttributes.h>

class hkpRigidBody;

	/// The hkaKeyFrameHierarchyUtility implements a controller that applies linear and angular velocities to the 
	/// rigid bodies in a constraint hierarchy e.g. a rag doll.
	/// This controller is useful when you are attempting to drive the system to a desired pose / configuration.
class hkaKeyFrameHierarchyUtility 
{

	public:

		HK_DECLARE_REFLECTION();

			/// A structure containing the control data for the utility
			/// The controller works by first matching acceleration, then velocity and finally position.
			/// At each stage the controller examines the difference between the keyframe and the body.
			/// This difference is scaled by the gain and applied it to the body by changing its velocity.
			/// Differences in position are clamped before being applied.
			/// When choosing values for the gains it is common to have higher values in the higher order controllers
			/// e.g. acceleration gain > velocity gain > position gain
			///
			/// Once the three controllers have been applied we perform a final pass which 
			/// snaps the bodies to the keyframes when they are very close. This controller called the snap controller
			/// works similarly to the position controller. It usually has a higher gain than the position controller.
			/// It differs slightly in that its strength is proportional to the error and only uses its full strength
			/// if the current error is smaller than m_snapMaxLinearDistance/m_snapMaxAngularDistance.
		struct ControlData
		{
			HK_DECLARE_REFLECTION();
			HK_DECLARE_NONVIRTUAL_CLASS_ALLOCATOR( HK_MEMORY_CLASS_RIGID_BODY, hkaKeyFrameHierarchyUtility::ControlData );

				/// This parameter blends the desired target for a bone between model space (0.0) or local space(1.0)
				/// Usually the controller will be much stiffer and more stable when driving to model space.
				/// However local space targeting can look more natural. 
				/// It is similar to the deprecated bone controller hierarchyGain parameter
			hkReal m_hierarchyGain; //+default(0.17f)+hk.RangeRealAttribute(absmin=0.0,absmax=1.0)

				/// This gain dampens the velocities of the bodies. The current velocity of the body is 
				/// scaled by this parameter on every frame before the controller is applied. It is
				/// applied every step and is generally more aggressive than standard linear or angular damping.
				/// A value of 0 means no damping.
			hkReal m_velocityDamping; //+default(0.0f)+hk.RangeRealAttribute(absmin=0.0,absmax=1.0)

				/// This parameter controls the proportion of the difference in acceleration that is 
				/// applied to the bodies. This parameter dampens the effects of the velocity control.
			hkReal m_accelerationGain; //+default(1.0f)+hk.RangeRealAttribute(absmin=0.0,absmax=1.0)

				/// This parameter controls the proportion of the difference in velocity that is 
				/// applied to the bodies. This parameter dampens the effects of the position control.
			hkReal m_velocityGain; //+default(0.6f)+hk.RangeRealAttribute(absmin=0.0,absmax=1.0)

				/// This parameter controls the proportion of the difference in position that is 
				/// applied to the bodies. This parameter has the most immediate effect. High gain 
				/// values make the controller very stiff. Once the controller is too stiff
				/// it will tend to overshoot. The velocity gain can help control this.
			hkReal m_positionGain; //+default(0.05f)+hk.RangeRealAttribute(absmin=0.0,absmax=1.0)	

				/// The position difference is scaled by the inverse delta time to compute a 
				/// velocity to be applied to the rigid body. The velocity is first clamped to this 
				/// limit before it is applied.
			hkReal m_positionMaxLinearVelocity; //+default(1.4f)+hk.RangeRealAttribute(absmin=0.0,absmax=100.0,softmax=10.0)

				/// The orientation difference is scaled by the inverse delta time to compute an angular 
				/// velocity to be applied to the rigid body. The velocity is first clamped to this 
				/// limit before it is applied.
			hkReal m_positionMaxAngularVelocity; //+default(1.8f)+hk.RangeRealAttribute(absmin=0.0,absmax=100.0,softmax=10.0)

				/// To allow for precise matching between keyframes and current position. 
				/// It works like the m_positionGain: it calculates an optimal 
				///     deltaVelocity = (keyFramePosition - currentPosition) / deltaTime
				/// scales it by m_snapGain, clips it against m_snapMaxXXXVelocity and scales it down
				/// if (keyFramePosition - currentPosition) > m_snapMaxXXXXDistance
			hkReal m_snapGain; //+default(0.1f)+hk.RangeRealAttribute(absmin=0.0,absmax=1.0) 

				/// See m_snapGain. The linear velocity calculated from the snapGain is clamped to this 
				/// limit before being applied.
			hkReal m_snapMaxLinearVelocity; //+default(0.3f)+hk.RangeRealAttribute(absmin=0.0,absmax=100.0,softmax=10.0)

				/// See m_snapGain. The angular velocity calculated from the snapGain is clamped to this 
				/// limit before being applied.
			hkReal m_snapMaxAngularVelocity; //+default(0.3f)+hk.RangeRealAttribute(absmin=0.0,absmax=100.0,softmax=10.0)

				/// This sets the max linear distance for the snap gain to work at full strength.
				/// The strength of the controller peaks at this distance.
				/// If the current distance is bigger than m_snapMaxLinearDistance, the snap velocity will be scaled
				/// by sqrt( maxDistane/currentDistance ).
			hkReal m_snapMaxLinearDistance; //+default(0.03f)+hk.RangeRealAttribute(absmin=0.0,absmax=10.0,softmax=1.0)

				/// This sets the max angular distance for the snap gain to work at full strength.
				/// The strength of the controller peaks at this distance.
				/// If the current distance is bigger than m_snapMaxAngularDistance, the snap velocity will be scaled
				/// by sqrt( maxDistane/currentDistance ).
			hkReal m_snapMaxAngularDistance; //+default(0.1f)+hk.RangeRealAttribute(absmin=0.0,absmax=10.0,softmax=1.0)

				// These useful defaults are appropriate in most circumstances
			ControlData() :
				m_hierarchyGain(0.17f),
				m_velocityDamping(0.0f),
				m_accelerationGain(1.0f),
				m_velocityGain(0.6f),
				m_positionGain(0.05f),
				m_positionMaxLinearVelocity(1.4f),
				m_positionMaxAngularVelocity(1.8f),
				m_snapGain(0.1f),
				m_snapMaxLinearVelocity(0.3f),
				m_snapMaxAngularVelocity(0.3f),
				m_snapMaxLinearDistance(0.03f),
				m_snapMaxAngularDistance(0.1f) {}

			hkBool isValid() const;

			ControlData( hkFinishLoadedObjectFlag flag ) {}

		};

		/// A structure holding data which used by this utility and has to be persistent
		struct WorkElem
		{
			HK_DECLARE_NONVIRTUAL_CLASS_ALLOCATOR( HK_MEMORY_CLASS_ANIMATION, hkaKeyFrameHierarchyUtility::WorkElem );

			hkVector4    m_prevPosition;
			hkQuaternion m_prevRotation;
			hkVector4    m_prevLinearVelocity;
			hkVector4    m_prevAngularVelocity;
		};

		/// A structure holding the data representing the pose to drive to and the persistent data
		struct KeyFrameData
		{
			HK_DECLARE_NONVIRTUAL_CLASS_ALLOCATOR( HK_MEMORY_CLASS_ANIMATION, hkaKeyFrameHierarchyUtility::KeyFrameData );

			/// The transformation from the ragdoll root to world space
			hkQsTransform			m_worldFromRoot;

			/// The desired pose in local space, one transform for each rigid body
			const hkQsTransform *	m_desiredPoseLocalSpace;

			/// Internal array of work data. Has to be persistent and should be initialized with the initialize() method 
			struct WorkElem*		m_internalReferencePose;
		};

		/// A structure holding the rigid bodies and information about how they are linked / parented
		/// This parent indices are usually derived by looking at how the bodies are constrained; see hkaRagdollUtils::constructSkeletonForRagdoll
		struct BodyData
		{
			HK_DECLARE_NONVIRTUAL_CLASS_ALLOCATOR( HK_MEMORY_CLASS_ANIMATION, hkaKeyFrameHierarchyUtility::BodyData );

			/// The number of rigid bodies
			int				     m_numRigidBodies;

			/// An array of rigid bodies. The velocities of these rigid bodies will be modified to match the input pose
			hkpRigidBody*const*  m_rigidBodies;

			/// The hierarchy of rigid bodies. A rigid body which has no parent has an index of-1
			const hkInt16*       m_parentIndices;

			/// An index one per body indicating which control data to use for this body
			/// If this is HK_NULL all bodies use the first element of the control data array
			int*				 m_controlDataIndices;

			BodyData() : m_controlDataIndices(HK_NULL) {}
		};

			/// The output
		struct Output
		{
			HK_DECLARE_NONVIRTUAL_CLASS_ALLOCATOR( HK_MEMORY_CLASS_ANIMATION, hkaKeyFrameHierarchyUtility::Output );

				/// This output parameter is related to velocity and is calculated
				/// as a byproduct of computing the snap gain. It is a reasonable
				/// measure of the error or stress in the controller
			hkReal m_stressSquared;
		};

			/// Initialize the internal WorkElem array
		static void HK_CALL initialize(const BodyData& bodies, WorkElem* internalReferencePose);

			/// applies the required velocities in order to drive the rigid bodies of this
			/// ragdoll to the desired pose. 
			/// If the output array can be HK_NULL if you wish to ignore the output
		static void HK_CALL applyKeyFrame( hkReal m_deltaTime, const KeyFrameData& pose, const BodyData& body, const ControlData* controlPalette, Output* outputArray = HK_NULL );
};


#endif // HK_KEYFRAME_HIERARCHY_UTILITY_H

/*
* Havok SDK - NO SOURCE PC DOWNLOAD, BUILD(#20080529)
*
* Confidential Information of Havok.  (C) Copyright 1999-2008 
* Telekinesys Research Limited t/a Havok. All Rights Reserved. The Havok
* Logo, and the Havok buzzsaw logo are trademarks of Havok.  Title, ownership
* rights, and intellectual property rights in the Havok software remain in
* Havok and/or its suppliers.
*
* Use of this software for evaluation purposes is subject to and indicates 
* acceptance of the End User licence Agreement for this product. A copy of 
* the license is included with this software and is also available at
* www.havok.com/tryhavok
*
*/
