
///////////////////////////////////////////////////////////////////////////////
//
// File   : Asura_MoveData_OrientationInterpolation.h
// Author : Andy Tate
// Date   : 10-Sep-2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif   // ifdef ASURA_USE_PRAGMA_ONCE

#ifndef __ASURA_MOVEDATA_ORIENTATIONINTERPOLATION_H__
#define __ASURA_MOVEDATA_ORIENTATIONINTERPOLATION_H__

#if defined(ASURA_USE_MOVEMENT_SYSTEM)

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura_Maths.h"
#include "Entity/PhysicalObject/Moves/Asura_MoveData.h"


////////////////////////////////////////////////////////////////////////////////
// Forward class declarations...
////////////////////////////////////////////////////////////////////////////////

class Asura_Move;

////////////////////////////////////////////////////////////////////////////////
// Class declaration...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Class        : Asura_MoveData_OrientationInterpolation
// Author       : Andy Tate
// Date         : 10-Sep-2007
//
// Description  : Class to handle the orientation interpolation during a 
//				  particular move.
////////////////////////////////////////////////////////////////////////////////

class Asura_MoveData_OrientationInterpolation : public Asura_MoveData
{
public:
	// Public typedefs...
	enum MovementOffsetOperation
	{
		IGNORE_MOVEMENT_OFFSET,
		ADD_MOVEMENT_OFFSET,
		SUBTRACT_MOVEMENT_OFFSET,
		STRETCH_MOVEMENT_OFFSET,

		MAX_MOVEMENT_OFFSET_OPERATIONS
	};

	// Public functions...
	Asura_MoveData_OrientationInterpolation(void);
//	~Asura_MoveData_OrientationInterpolation();

	// Manipulation functions...
	void	Reset(void);

	bool	Start(const Asura_Quat& xInitial, const Asura_Move& xOwnerMove);

	bool	Update(const float fTimeStep);
	bool	Update(const float fTimeStep, const Asura_Quat& xLSMOCurrentOrientation, const Asura_Quat& xLSMOFinalOrientation);
	void	Finish(void);

	// Query functions...
	bool	HasReachedFinal(const Asura_Quat& xCurrent) const	{ return(xCurrent.ApproximatelyEquals(m_xFinal, m_fReachedFinalEpsilon)); }
	bool	HasExceededDuration(void) const						{ return(m_bDurationBased && (m_fTimer >= m_fDuration));  }

	// Accessor functions...
	void	SetInitial(const Asura_Quat& xInitial)			{ m_bInitialSet = true; m_xInitial = xInitial; m_xCurrent = m_xInitial; m_xActual = m_xInitial; }
	void	SetInitial(const Asura_Matrix_3x3& xInitial)	{ m_bInitialSet = true; m_xInitial.FromMatrix(xInitial); m_xCurrent = m_xInitial; m_xActual = m_xInitial; }
	void	SetFinal(const Asura_Quat& xFinal)				{ m_bFinalSet = true; m_xFinal = xFinal; }
	void	SetFinal(const Asura_Matrix_3x3& xFinal)		{ m_bFinalSet = true; m_xFinal.FromMatrix(xFinal); }

	// This needs to be in local space.
	void	SetSingularityHintAxis(const Asura_AVector3& xAxisLS)	{ m_bHasSingularityHintAxis = true; m_xSingularityHintAxis = xAxisLS; }

	void	SetDuration(const float fDuration)	{ m_bDurationBased = true; m_fDuration = fDuration; }
	void	SetSpeed(const Asura_Angle fSpeed)	{ m_bSpeedBased = true; m_fSpeed = fSpeed; }

	bool	IsDurationBased(void) const		{ return(m_bDurationBased); }
	bool	IsSpeedBased(void) const		{ return(m_bSpeedBased); }

	void					SetMovementOffsetOperation(const MovementOffsetOperation eOperation)	{ m_eMovementOffsetOperation = eOperation; }
	MovementOffsetOperation	GetMovementOffsetOperation(void) const									{ return(static_cast<MovementOffsetOperation>(m_eMovementOffsetOperation)); }

	const Asura_Quat&	GetInitial(void) const	{ return(m_xInitial); }
	const Asura_Quat&	GetCurrent(void) const	{ return(m_xCurrent); }
	const Asura_Quat&	GetFinal(void) const	{ return(m_xFinal); }

	const Asura_Quat&	GetMovementOffsetCurrent(void) const	{ return(m_xMovementOffsetCurrent); }
	const Asura_Quat&	GetMovementOffsetFinal(void) const		{ return(m_xMovementOffsetFinal); }

	const Asura_Quat&	GetActual(void) const	{ return(m_xActual); }

	float	GetTimer(void) const			{ return(m_fTimer); }
	float	GetDuration(void) const			{ return(m_fDuration); }
	float	GetParametricTimer(void) const	{ return(m_fTimer / m_fDuration); }


protected:
	// Protected variables...

	// Protected functions...
	void	CalculateWSSlerp(const float fParametricTimer, const Asura_Quat& xFinalOrientation, Asura_Quat& xCurrentOrientation) const;

	bool	UpdateDurationBased(void);
	bool	UpdateDurationBased(const float fParametricTimer, const Asura_Quat& xLSMOCurrentOrientation, const Asura_Quat& xLSMOFinalOrientation);
	bool	UpdateDurationBased(const Asura_Quat& xLSMOCurrentOrientation, const Asura_Quat& xLSMOFinalOrientation);

	bool	UpdateSpeedBased(const float fTimeStep);


private:
	// Private typedefs...
	typedef Asura_MoveData PARENT;

	// Private variables...
	Asura_Quat	m_xInitial;		// The initial orientation used for the interpolation.
	Asura_Quat	m_xCurrent;		// The current orientation used for the interpolation.
	Asura_Quat	m_xFinal;		// The final orientation used for the interpolation.

	Asura_Quat	m_xMovementOffsetCurrent;	// The desired orientation used for interpolation (where the interpolator actually heads for).
	Asura_Quat	m_xMovementOffsetFinal;		// The desired orientation used for interpolation (where the interpolator actually heads for).

	Asura_Quat	m_xActual;	// The actual orientation used by the move.

	float	m_fReachedFinalEpsilon;	// The epsilon value used when checking whether our current orientation is the final orientation.
	float	m_fTimer;				// The timer used when interpolating orientation.

	float		m_fDuration;	// The duration over which to interpolate orientation.
	Asura_Angle	m_fSpeed;		// The speed at which we're to rotate to our orientation.

	Asura_AVector3	m_xSingularityHintAxis;	// The axis about which we rotate when we have a singularity (due to 180 degree rotation).
	Asura_Angle		m_fSingularityAngle;	// The angle used when we have a singularity.

	union
	{
		u_int		m_uFlags;							// Single access point for the flags.

		struct
		{
			u_int	m_bInitialSet				: 1,	// Flag to say that we have set an initial value.
					m_bFinalSet					: 1,	// Flag to say that we have set a final value.
					m_bSnapToFinal				: 1,	// Flag to say that we need to snap our current value to the final one.
					m_bDurationBased			: 1,	// Flag to say whether this move is duration-based.
					m_bSpeedBased				: 1,	// Flag to say whether this move is speed-based.
					m_bHasSingularityHintAxis	: 1,	// Flag to say that this interpolator has a singularity hint-axis.
					m_eMovementOffsetOperation	: 4,	// Flag to say whether we need to take the movement-offset into account.
												: 0;	// 22 spare-bits.
		};
	};

	// Private functions...
	Asura_MoveData_OrientationInterpolation(const Asura_MoveData_OrientationInterpolation& xOther);

	Asura_MoveData_OrientationInterpolation&	operator=(const Asura_MoveData_OrientationInterpolation& xOther);

};   // End of class 'Asura_MoveData_OrientationInterpolation'.

#endif	// defined(ASURA_USE_MOVEMENT_SYSTEM)

#endif   // ifndef __ASURA_MOVEDATA_ORIENTATIONINTERPOLATION_H__


/// End of file. ///
