// quaternion.h: interface for the quaternion class.
//
// Only for unit quaternions( norm(q) = 1 )
//
// result with non-unit quaternion by this routine is undefined
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_QUATERNION_H__B048AE81_B0A6_11D4_AD2B_0000E8EB4C69__INCLUDED_)
#define AFX_QUATERNION_H__B048AE81_B0A6_11D4_AD2B_0000E8EB4C69__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "MathBase.h"


struct quaternion  
{
	union
	{
		struct 
		{
			float x, y, z, w;
		};
		float v[4];
	};

	quaternion() {};
	quaternion( float fx, float fy, float fz, float fw )
	{
		x = fx;
		y = fy;
		z = fz;
		w = fw;
	}
	// initialize to identity quaternion
	void Identity()
	{
		x = y = z = 0.0f;
		w = 1.0f;
	}

	quaternion& operator*=(const quaternion& op);
	quaternion operator-()
	{
		return quaternion( -x, -y, -z, -w );
	}

	// perform quaternion rotation on vector
	//void RotateVector( vector3& vec );
	// perform axis-angle tpye rotation
	void Pitch( float a )
	{
		quaternion x( sinf(a/2), 0.0f, 0.0f, cosf(a/2) );
		(*this) *= x;
	}
	void Yaw( float a )
	{
		quaternion x( 0.0f, sinf(a/2), 0.0f, cosf(a/2) );
		(*this) *= x;
	}
	void Roll( float a )
	{
		quaternion x( 0.0f, 0.0f, sinf(a/2), cosf(a/2) );
		(*this) *= x;
	}
	
};

// 'this = op * this', not 'this = this * op'
// due to the relation between rotation matrix&quaternion
inline quaternion& quaternion::operator*=(const quaternion& op)
{
	float fx, fy,fz, fw;
	fw = op.w*w - op.x*x - op.y*y - op.z*z;
	fx = op.w*x + op.x*w + op.y*z - op.z*y;
	fy = op.w*y - op.x*z + op.y*w + op.z*x;
	fz = op.w*z + op.x*y - op.y*x + op.z*w;
	w = fw;
	x = fx;
	y = fy;
	z = fz;
	return *this;
}


// 'result = op2 * op1', not 'result = op1 * op2'
// due to the inverse-relation between rotation matrix&quaternion
inline void Multiply( quaternion& result, quaternion& op1, quaternion& op2 )
{
	result.w = op2.w*op1.w - op2.x*op1.x - op2.y*op1.y - op2.z*op1.z;
	result.x = op2.w*op1.x + op2.x*op1.w + op2.y*op1.z - op2.z*op1.y;
	result.y = op2.w*op1.y - op2.x*op1.z + op2.y*op1.w + op2.z*op1.x;
	result.z = op2.w*op1.z + op2.x*op1.y - op2.y*op1.x + op2.z*op1.w;
}


// 'result = op2 * op1', not 'result = op1 * op2'
// due to the inverse-relation between rotation matrix&quaternion
inline const quaternion operator*( quaternion& op1, quaternion& op2 )
{
	quaternion result;

	result.w = op2.w*op1.w - op2.x*op1.x - op2.y*op1.y - op2.z*op1.z;
	result.x = op2.w*op1.x + op2.x*op1.w + op2.y*op1.z - op2.z*op1.y;
	result.y = op2.w*op1.y - op2.x*op1.z + op2.y*op1.w + op2.z*op1.x;
	result.z = op2.w*op1.z + op2.x*op1.y - op2.y*op1.x + op2.z*op1.w;

	return result;
}



#endif // !defined(AFX_QUATERNION_H__B048AE81_B0A6_11D4_AD2B_0000E8EB4C69__INCLUDED_)
