//==== Copyright (c) 1996-2011, Valve Corporation, All rights reserved. =====//
//
// Purpose: 
//
// $NoKeywords: $
//
//===========================================================================//

#include "core/stdafx.h"
#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB)

#include "mathlib/transform.h"
#include "mathlib/mathlib.h"

// memdbgon must be the last include file in a .cpp file!!!
//#include "tier0/memdbgon.h"

const CTransform g_TransformIdentity(Vector3D(0.0f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f, 1.0f));

void SetIdentityTransform(CTransform& out)
{
	out.m_vPosition = vec3_origin;
	out.m_orientation = quat_identity;
}

void ConcatTransforms(const CTransform& in1, const CTransform& in2, CTransform& out)
{
	// Store in temp to avoid problems if out == in1 or out == in2
	CTransform result;
	QuaternionMult(in1.m_orientation, in2.m_orientation, result.m_orientation);
	QuaternionMultiply(in1.m_orientation, in2.m_vPosition, result.m_vPosition);
	result.m_vPosition += in1.m_vPosition;
	out = result;
}

void VectorIRotate(const Vector3D& v, const CTransform& t, Vector3D& out)
{
	// FIXME: Make work directly with the transform
	matrix3x4_t m;
	TransformMatrix(t, m);
	VectorIRotate(v, m, out);
}

void VectorITransform(const Vector3D& v, const CTransform& t, Vector3D& out)
{
	// FIXME: Make work directly with the transform
	matrix3x4_t m;
	TransformMatrix(t, m);
	VectorITransform(v, m, out);
}

void TransformSlerp(const CTransform& p, const CTransform& q, float t, CTransform& qt)
{
	QuaternionSlerp(p.m_orientation, q.m_orientation, t, qt.m_orientation);
	VectorLerp(p.m_vPosition, q.m_vPosition, t, qt.m_vPosition);
}

void TransformLerp(const CTransform& p, const CTransform& q, float t, CTransform& qt)
{
	QuaternionBlend(p.m_orientation, q.m_orientation, t, qt.m_orientation);
	VectorLerp(p.m_vPosition, q.m_vPosition, t, qt.m_vPosition);
}

void TransformMatrix(const CTransform& in, matrix3x4_t& out)
{
	QuaternionMatrix(in.m_orientation, in.m_vPosition, out);
}

void TransformMatrix(const CTransformUnaligned& in, matrix3x4_t& out)
{
	QuaternionMatrix(in.m_orientation, in.m_vPosition, out);
}

void TransformMatrix(const CTransform& in, const Vector3D& vScaleIn, matrix3x4_t& out)
{
	QuaternionMatrix(in.m_orientation, in.m_vPosition, vScaleIn, out);
}

void MatrixTransform(const matrix3x4_t& in, CTransformUnaligned& out)
{
	MatrixQuaternion(in, out.m_orientation);
	MatrixGetColumn(in, ORIGIN, out.m_vPosition);
}

void MatrixTransform(const matrix3x4_t& in, CTransform& out)
{
	MatrixQuaternion(in, out.m_orientation);
	MatrixGetColumn(in, ORIGIN, out.m_vPosition);
}

void MatrixTransform(const matrix3x4_t& in, CTransform& out, Vector3D& vScaleOut)
{
	matrix3x4_t norm;
	vScaleOut = MatrixNormalize(in, norm);
	MatrixTransform(norm, out);
}

void AngleTransform(const QAngle& angles, const Vector3D& origin, CTransform& out)
{
	AngleQuaternion(angles, out.m_orientation);
	out.m_vPosition = origin;
}

void TransformInvert(const CTransform& in, CTransform& out)
{
	QuaternionInvert(in.m_orientation, out.m_orientation);
	QuaternionMultiply(out.m_orientation, in.m_vPosition, out.m_vPosition);
	out.m_vPosition *= -1.0f;
}

void AxisAngleTransform(const Vector3D& vecAxis, float flAngleDegrees, CTransform& out)
{
	AxisAngleQuaternion(vecAxis, flAngleDegrees, out.m_orientation);
	out.m_vPosition = vec3_origin;
}

void TransformVectorsFLU(const CTransform& in, Vector3D* pForward, Vector3D* pLeft, Vector3D* pUp)
{
	QuaternionVectorsFLU(in.m_orientation, pForward, pLeft, pUp);
}

void TransformVectorsForward(const CTransform& in, Vector3D* pForward)
{
	QuaternionVectorsForward(in.m_orientation, pForward);
}

bool TransformsAreEqual(const CTransform& src1, const CTransform& src2, float flPosTolerance, float flRotTolerance)
{
	if (!VectorsAreEqual(src1.m_vPosition, src2.m_vPosition, flPosTolerance))
		return false;
	return QuaternionsAreEqual(src1.m_orientation, src2.m_orientation, flRotTolerance);
}

// FIXME: optimize this with simd goodness
void TransformToWorldSpace(int nRootTransformCount, int nTransformCount, const int* pParentIndices, CTransform* pTransforms)
{
#ifdef _DEBUG
	for (int i = 0; i < nRootTransformCount; ++i)
	{
		Assert(pParentIndices[i] < 0);
	}
#endif

	for (int i = nRootTransformCount; i < nTransformCount; ++i)
	{
		int nParentBone = pParentIndices[i];
		Assert(nParentBone >= 0 && nParentBone < i);
		ConcatTransforms(pTransforms[nParentBone], pTransforms[i], pTransforms[i]);
	}
}

// FIXME: optimize this with simd goodness
void TransformToParentSpace(int nRootTransformCount, int nTransformCount, const int* pParentIndices, CTransform* pTransforms)
{
#ifdef _DEBUG
	for (int i = 0; i < nRootTransformCount; ++i)
	{
		Assert(pParentIndices[i] < 0);
	}
#endif

	bool* pComputedParentTransform = (bool*)stackalloc(nTransformCount * sizeof(bool));
	memset(pComputedParentTransform, 0, nTransformCount * sizeof(bool));
	CTransform* pWorldToParentTransforms = (CTransform*)stackalloc(nTransformCount * sizeof(CTransform));

	for (int b = nTransformCount; --b >= nRootTransformCount; )
	{
		int nParentBone = pParentIndices[b];
		if (!pComputedParentTransform[nParentBone])
		{
			TransformInvert(pTransforms[nParentBone], pWorldToParentTransforms[nParentBone]);
			pComputedParentTransform[nParentBone] = true;
		}
		ConcatTransforms(pWorldToParentTransforms[nParentBone], pTransforms[b], pTransforms[b]);
	}
}

#endif // !_STATIC_LINKED || _SHARED_LIB