
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_SurfaceReorientationNormalInterpolator_Slerp.cpp
// Author : Andy Tate
// Date   : 7-Feb-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

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

#include "Core/Asura.h"

// This file's header...
#include "Physics/AVP_SurfaceReorientationNormalInterpolator_Slerp.h"

// Asura independent header-files...
#include "Debugging/Asura_Logfile.h"

// Game independent header-files...
#include "Physics/AVP_CalculateSurfaceReorientationNormal.h"


////////////////////////////////////////////////////////////////////////////////
// Constant declarations...
////////////////////////////////////////////////////////////////////////////////

DEBUGVAR float			dbg_fSmoothingDurationForMinAngle	= 0.250f;
DEBUGVAR float			dbg_fSmoothingDurationForMaxAngle	= 0.125f;
DEBUGVAR Asura_Angle	dbg_fSmoothingMinAngle				= Asura_Maths::Pi / 36;	// 5 degrees
DEBUGVAR Asura_Angle	dbg_fSmoothingMaxAngle				= Asura_Maths::Pi / 6;	// 30 degrees


////////////////////////////////////////////////////////////////////////////////
// Static variable declarations...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Private functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Protected functions...
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// Public functions...
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_SurfaceReorientationNormalInterpolator_Slerp::AVP_SurfaceReorientationNormalInterpolator_Slerp
// Author       : Andy Tate
// Date         : 7-Feb-2008
////////////////////////////////////////////////////////////////////////////////

AVP_SurfaceReorientationNormalInterpolator_Slerp::AVP_SurfaceReorientationNormalInterpolator_Slerp(void)
: m_xInitialNormal(Asura_Maths::ZeroAVector3)
, m_xTargetNormal(Asura_Maths::ZeroAVector3)
, m_xRotationBetweenNormals(Asura_Maths::IdentityQuat)
, m_fTimeTaken(0.0f)
, m_fDurationToTarget(0.0f)
{
	// Nothing more to initialise...
}

/// End of function 'AVP_SurfaceReorientationNormalInterpolator_Slerp::AVP_SurfaceReorientationNormalInterpolator_Slerp'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_SurfaceReorientationNormalInterpolator_Slerp::Reset
// Author       : Andy Tate
// Date         : 7-Feb-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_SurfaceReorientationNormalInterpolator_Slerp::Reset(Asura_AVector3_InParam xNormal)
{
	// Reset the slerp data...
	m_xInitialNormal	= xNormal;
	m_xTargetNormal		= xNormal;
	m_fTimeTaken		= 0.0f;
	m_fDurationToTarget	= 0.0f;

	m_xRotationBetweenNormals.Identity();

	// Call the parent function...
	PARENT::Reset(xNormal);
}

/// End of function 'AVP_SurfaceReorientationNormalInterpolator_Slerp::Reset'. ///



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_SurfaceReorientationNormalInterpolator_Slerp::Update
// Author       : Andy Tate
// Date         : 6-Mar-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_SurfaceReorientationNormalInterpolator_Slerp::Update(const float fTimeStep,
																			Asura_AVector3_InParam xDesiredNormal,
																			Asura_AVector3_InOutParam xCurrentNormal)
{
	if( (dbg_fSmoothingDurationForMinAngle > 0.0f) &&
		(dbg_fSmoothingDurationForMaxAngle > 0.0f) )
	{
		// Are we effectively at our destination?
		if(xCurrentNormal.ApproximatelyEquals(xDesiredNormal))
		{
			xCurrentNormal	= xDesiredNormal;
		}
		else
		{
			const float			fDotProduct			= xDesiredNormal * xCurrentNormal;
			const Asura_Angle	fAngle				= Asura_Maths::ArcCosine(fDotProduct);
			const float			fScale				= (Asura_Maths::Abs(fAngle) - dbg_fSmoothingMinAngle) / (dbg_fSmoothingMaxAngle - dbg_fSmoothingMinAngle);
			const float			fRotationParametric	= Asura_Maths::ClampToRange(fScale, 0.0f, 1.0f);
			const float			fDuration			= Asura_Maths::Lerp(dbg_fSmoothingDurationForMinAngle, dbg_fSmoothingDurationForMaxAngle, fRotationParametric);

			// Update the current state of the slerp...
			if(m_xTargetNormal == xDesiredNormal)
			{
				m_fTimeTaken	+= fTimeStep;
			}
			else
			{
				m_xInitialNormal	= xCurrentNormal;
				m_xTargetNormal		= xDesiredNormal;
				m_fTimeTaken		= fTimeStep;
				m_fDurationToTarget	= fDuration;

				m_xRotationBetweenNormals.RotationFromAToB(m_xInitialNormal, m_xTargetNormal);
			}

			// Calculate the latest normal using the slerp...
			if(m_fTimeTaken >= m_fDurationToTarget)
			{
				xCurrentNormal	= m_xTargetNormal;
			}
			else
			{
				const float	fNormalParametric	= m_fTimeTaken / m_fDurationToTarget;

				Asura_Quat	xRotation;

				xRotation.Slerp(Asura_Maths::IdentityQuat, m_xRotationBetweenNormals, fNormalParametric);
				xCurrentNormal = xRotation.RotateVector(m_xInitialNormal);
			}

			// Ensure that we've got a valid normal...
			xCurrentNormal.Normalise();
		}
	}
	else
	{
		xCurrentNormal	= xDesiredNormal;
	}
}

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



////////////////////////////////////////////////////////////////////////////////
// Function     : AVP_SurfaceReorientationNormalInterpolator_Slerp::Update
// Author       : Andy Tate
// Date         : 7-Feb-2008
////////////////////////////////////////////////////////////////////////////////

void AVP_SurfaceReorientationNormalInterpolator_Slerp::Update(const float fTimeStep,
																			const AVP_CalculateSurfaceReorientationNormal& xNormalCalculation,
																			Asura_AVector3_InOutParam xCurrentNormal)
{
	Asura_AVector3	xDesiredNormal;

	// Fetch the desired normal to use...
	xNormalCalculation.GetDesiredNormal(xDesiredNormal);

	// Call the other version of this function...
	AVP_SurfaceReorientationNormalInterpolator_Slerp::Update(fTimeStep, xDesiredNormal, xCurrentNormal);
}

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


/// End of file. ///
