//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.  

#ifndef DY_FEATHERSTONE_ARTICULATION_UTIL_H
#define DY_FEATHERSTONE_ARTICULATION_UTIL_H

#include "PsVecMath.h"
#include "CmSpatialVector.h"
#include "PsBitUtils.h"
#include "foundation/PxMemory.h"

namespace physx
{

namespace Dy
{
	static const size_t DY_MAX_DOF = 6;

	struct SpatialSubspaceMatrix
	{
		static const PxU32 MaxColumns = 3;
	public:

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialSubspaceMatrix() :numColumns(0)
		{
			//PxMemZero(columns, sizeof(Cm::SpatialVectorF) * 6);
			memset(columns, 0, sizeof(Cm::UnAlignedSpatialVector) * MaxColumns);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE void setNumColumns(const PxU32 nc)
		{
			numColumns = nc;
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE PxU32 getNumColumns() const
		{
			return numColumns;
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF transposeMultiply(Cm::SpatialVectorF& v) const
		{
			PxReal result[6];
			for (PxU32 i = 0; i < numColumns; ++i)
			{
				const Cm::UnAlignedSpatialVector& row = columns[i];
				result[i] = row.dot(v);
			}

			Cm::SpatialVectorF res;
			res.top.x = result[0]; res.top.y = result[1]; res.top.z = result[2];
			res.bottom.x = result[3]; res.bottom.y = result[4]; res.bottom.z = result[5];

			return res;

		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE void setColumn(const PxU32 index, const PxVec3& top, const PxVec3& bottom)
		{
			PX_ASSERT(index < MaxColumns);
			columns[index] = Cm::SpatialVectorF(top, bottom);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector& operator[](unsigned int num)
		{
			PX_ASSERT(num < MaxColumns);
			return columns[num];
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE const Cm::UnAlignedSpatialVector& operator[](unsigned int num)  const
		{
			PX_ASSERT(num < MaxColumns);
			return columns[num];
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE const Cm::UnAlignedSpatialVector* getColumns() const
		{
			return columns;
		}


	private:
		Cm::UnAlignedSpatialVector columns[MaxColumns];			//192		192			
		PxU32	numColumns;						//4			208 (12 bytes padding)

	};

	//this should be 6x6 matrix
	//|R,		0|
	//|-R*rX,	R|
	struct SpatialTransform
	{
		PxMat33 R;
		PxQuat q;
		PxMat33 T;

	public:
		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform() : R(PxZero), T(PxZero)
		{
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform(const PxMat33& R_, const PxMat33& T_) : R(R_), T(T_)
		{
			q = PxQuat(R_);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform(const PxQuat& q_, const PxMat33& T_) : q(q_), T(T_)
		{
			R = PxMat33(q_);
		}

		//This assume angular is the top vector and linear is the bottom vector
		/*PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& s) const
		{
			const PxVec3 angular = R * s.angular;
			const PxVec3 linear = T * s.angular + R * s.linear;
			return Cm::SpatialVector(linear, angular);
		}*/


		////This assume angular is the top vector and linear is the bottom vector
		//PX_FORCE_INLINE Cm::SpatialVectorF operator *(Cm::SpatialVectorF& s) const
		//{
		//	const PxVec3 top = R * s.top;
		//	const PxVec3 bottom = T * s.top + R * s.bottom;

		//	const PxVec3 top1 = q.rotate(s.top);
		//	const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom);

		///*	const PxVec3 tDif = (top - top1).abs();
		//	const PxVec3 bDif = (bottom - bottom1).abs();
		//	const PxReal eps = 0.001f;
		//	PX_ASSERT(tDif.x < eps && tDif.y < eps && tDif.z < eps);
		//	PX_ASSERT(bDif.x < eps && bDif.y < eps && bDif.z < eps);*/
		//	return Cm::SpatialVectorF(top1, bottom1);
		//}

		//This assume angular is the top vector and linear is the bottom vector
		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF operator *(const Cm::SpatialVectorF& s) const
		{
			//const PxVec3 top = R * s.top;
			//const PxVec3 bottom = T * s.top + R * s.bottom;

			const PxVec3 top1 = q.rotate(s.top);
			const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom);

			return Cm::SpatialVectorF(top1, bottom1);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector operator *(const Cm::UnAlignedSpatialVector& s) const
		{
			//const PxVec3 top = R * s.top;
			//const PxVec3 bottom = T * s.top + R * s.bottom;

			const PxVec3 top1 = q.rotate(s.top);
			const PxVec3 bottom1 = T * s.top + q.rotate(s.bottom);

			return Cm::UnAlignedSpatialVector(top1, bottom1);
		}

		//transpose is the same as inverse, R(inverse) = R(transpose)
		//|R(t),	0	|
		//|rXR(t),	R(t)|
		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialTransform getTranspose() const
		{
			SpatialTransform ret;
			ret.q = q.getConjugate();
			ret.R = R.getTranspose();
			ret.T = T.getTranspose();
			return ret;
			
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF transposeTransform(const Cm::SpatialVectorF& s) const
		{
			const PxVec3 top1 = q.rotateInv(s.top);
			const PxVec3 bottom1 = T.transformTranspose(s.top) + q.rotateInv(s.bottom);

			return Cm::SpatialVectorF(top1, bottom1);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector transposeTransform(const Cm::UnAlignedSpatialVector& s) const
		{
			const PxVec3 top1 = q.rotateInv(s.top);
			const PxVec3 bottom1 = T.transformTranspose(s.top) + q.rotateInv(s.bottom);

			return Cm::UnAlignedSpatialVector(top1, bottom1);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE void operator =(SpatialTransform& other)
		{
			R = other.R;
			q = other.q;
			T = other.T;
		}

	};

	struct InvStIs
	{
		PxReal invStIs[3][3];
	};

	struct IsInvD
	{
		Cm::SpatialVectorF isInvD[3];
	};

	//this should be 6x6 matrix and initialize to
	//|0,	M|
	//|I,	0|
	//this should be 6x6 matrix but bottomRight is the transpose of topLeft
	//so we can get rid of bottomRight
	struct SpatialMatrix
	{
		PxMat33 topLeft;		// intialize to 0 
		PxMat33 topRight;		// initialize to mass matrix
		PxMat33 bottomLeft;		// initialize to inertia
		PxU32	padding;		//4		112

	public:
		
		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix()
		{
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix(PxZERO r) : topLeft(PxZero), topRight(PxZero),
			bottomLeft(PxZero)
		{
			PX_UNUSED(r);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix(const PxMat33& topLeft_, const PxMat33& topRight_, const PxMat33& bottomLeft_)
		{
			topLeft = topLeft_;
			topRight = topRight_;
			bottomLeft = bottomLeft_;
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 getBottomRight() const
		{
			return topLeft.getTranspose();
		}

		PX_FORCE_INLINE void setZero()
		{
			topLeft = PxMat33(0.f);
			topRight = PxMat33(0.f);
			bottomLeft = PxMat33(0.f);
		}


		//This assume angular is the top vector and linear is the bottom vector
		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVector operator *(const Cm::SpatialVector& s) const
		{
			const PxVec3 angular = topLeft * s.angular + topRight * s.linear;
			const PxVec3 linear = bottomLeft * s.angular + topLeft.getTranspose() * s.linear;
			return Cm::SpatialVector(linear, angular);
		}

		//This assume angular is the top vector and linear is the bottom vector
		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::SpatialVectorF operator *(const Cm::SpatialVectorF& s) const
		{
			const PxVec3 top = topLeft * s.top + topRight * s.bottom;
			const PxVec3 bottom = bottomLeft * s.top + topLeft.transformTranspose(s.bottom);

			return Cm::SpatialVectorF(top, bottom);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE Cm::UnAlignedSpatialVector operator *(const Cm::UnAlignedSpatialVector& s) const
		{
			const PxVec3 top = topLeft * s.top + topRight * s.bottom;
			const PxVec3 bottom = bottomLeft * s.top + topLeft.transformTranspose(s.bottom);

			return Cm::UnAlignedSpatialVector(top, bottom);
		}


		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator *(const PxReal& s) const
		{
			const PxMat33 newTopLeft = topLeft * s;
			const PxMat33 newTopRight = topRight * s;
			const PxMat33 newBottomLeft = bottomLeft * s;

			return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator -(const SpatialMatrix& s) const
		{
			PxMat33 newTopLeft = topLeft - s.topLeft;
			PxMat33 newTopRight = topRight - s.topRight;
			PxMat33 newBottomLeft = bottomLeft - s.bottomLeft;
			
			return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator +(const SpatialMatrix& s) const
		{
			PxMat33 newTopLeft = topLeft + s.topLeft;
			PxMat33 newTopRight = topRight + s.topRight;
			PxMat33 newBottomLeft = bottomLeft + s.bottomLeft;

			return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator-()
		{
			PxMat33 newTopLeft = -topLeft;
			PxMat33 newTopRight = -topRight;
			PxMat33 newBottomLeft = -bottomLeft;
			
			return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE void operator +=(const SpatialMatrix& s)
		{
			topLeft += s.topLeft;
			topRight += s.topRight;
			bottomLeft += s.bottomLeft;
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix operator *(const SpatialMatrix& s)
		{
			PxMat33 sBottomRight = s.topLeft.getTranspose();
			PxMat33 bottomRight = topLeft.getTranspose();

			PxMat33 newTopLeft = topLeft * s.topLeft + topRight * s.bottomLeft;
			PxMat33 newTopRight = topLeft * s.topRight + topRight * sBottomRight;
			PxMat33 newBottomLeft = bottomLeft * s.topLeft + bottomRight * s.bottomLeft;

			return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);
		}

		static SpatialMatrix constructSpatialMatrix(const Cm::SpatialVector& Is, const Cm::SpatialVector& stI)
		{
			//construct top left
			PxVec3 tLeftC0 = Is.angular * stI.angular.x;
			PxVec3 tLeftC1 = Is.angular * stI.angular.y;
			PxVec3 tLeftC2 = Is.angular * stI.angular.z;

			PxMat33 topLeft(tLeftC0, tLeftC1, tLeftC2);

			//construct top right
			PxVec3 tRightC0 = Is.angular * stI.linear.x;
			PxVec3 tRightC1 = Is.angular * stI.linear.y;
			PxVec3 tRightC2 = Is.angular * stI.linear.z;
			PxMat33 topRight(tRightC0, tRightC1, tRightC2);

			//construct bottom left
			PxVec3 bLeftC0 = Is.linear * stI.angular.x;
			PxVec3 bLeftC1 = Is.linear * stI.angular.y;
			PxVec3 bLeftC2 = Is.linear * stI.angular.z;
			PxMat33 bottomLeft(bLeftC0, bLeftC1, bLeftC2);

			return SpatialMatrix(topLeft, topRight, bottomLeft);
		}

		static PX_CUDA_CALLABLE SpatialMatrix constructSpatialMatrix(const Cm::SpatialVectorF& Is, const Cm::SpatialVectorF& stI)
		{
			//construct top left
			PxVec3 tLeftC0 = Is.top * stI.top.x;
			PxVec3 tLeftC1 = Is.top * stI.top.y;
			PxVec3 tLeftC2 = Is.top * stI.top.z;

			PxMat33 topLeft(tLeftC0, tLeftC1, tLeftC2);

			//construct top right
			PxVec3 tRightC0 = Is.top * stI.bottom.x;
			PxVec3 tRightC1 = Is.top * stI.bottom.y;
			PxVec3 tRightC2 = Is.top * stI.bottom.z;
			PxMat33 topRight(tRightC0, tRightC1, tRightC2);

			//construct bottom left
			PxVec3 bLeftC0 = Is.bottom * stI.top.x;
			PxVec3 bLeftC1 = Is.bottom * stI.top.y;
			PxVec3 bLeftC2 = Is.bottom * stI.top.z;
			PxMat33 bottomLeft(bLeftC0, bLeftC1, bLeftC2);

			return SpatialMatrix(topLeft, topRight, bottomLeft);
		}

		static PX_CUDA_CALLABLE SpatialMatrix constructSpatialMatrix(const Cm::SpatialVectorF* columns)
		{
			PxMat33 topLeft(columns[0].top, columns[1].top, columns[2].top);
			PxMat33 bottomLeft(columns[0].bottom, columns[1].bottom, columns[2].bottom);
			PxMat33 topRight(columns[3].top, columns[4].top, columns[5].top);
			
			return SpatialMatrix(topLeft, topRight, bottomLeft);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix getTranspose()
		{
			PxMat33 newTopLeft = topLeft.getTranspose();
			PxMat33 newTopRight = bottomLeft.getTranspose();
			PxMat33 newBottomLeft = topRight.getTranspose();
			//PxMat33 newBottomRight = bottomRight.getTranspose();

			return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);// , newBottomRight);
		}

		//static bool isTranspose(const PxMat33& a, const PxMat33& b)
		//{
		//	PxReal eps = 0.01f;
		//	//test bottomRight is the transpose of topLeft
		//	for (PxU32 i = 0; i <3; ++i)
		//	{
		//		for (PxU32 j = 0; j <3; ++j)
		//		{
		//			if (PxAbs(a[i][j] - b[j][i]) > eps)
		//				return false;
		//		}
		//	}

		//	return true;
		//}

		PX_FORCE_INLINE bool isIdentity(const PxMat33& matrix)
		{
			PxReal eps = 0.00001f;
			float x = PxAbs(1.f - matrix.column0.x);
			float y = PxAbs(1.f - matrix.column1.y);
			float z = PxAbs(1.f - matrix.column2.z);
			bool identity = ((x < eps) && PxAbs(matrix.column0.y - 0.f) < eps && PxAbs(matrix.column0.z - 0.f) < eps) &&
				(PxAbs(matrix.column1.x - 0.f) < eps && (y < eps) && PxAbs(matrix.column1.z - 0.f) < eps) &&
				(PxAbs(matrix.column2.x - 0.f) < eps && PxAbs(matrix.column2.y - 0.f) < eps && (z < eps));

			return identity;
		}

		PX_FORCE_INLINE bool isZero(const PxMat33& matrix)
		{
			PxReal eps = 0.0001f;
			for (PxU32 i = 0; i < 3; ++i)
			{
				for (PxU32 j = 0; j < 3; ++j)
				{
					if (PxAbs(matrix[i][j]) > eps)
						return false;
				}
			}

			return true;
		}

		PX_FORCE_INLINE bool isIdentity()
		{
			bool topLeftIsIdentity = isIdentity(topLeft);

			bool topRightIsZero = isZero(topRight);

			bool bottomLeftIsZero = isZero(bottomLeft);

			return topLeftIsIdentity && topRightIsZero && bottomLeftIsZero;
		}

		static bool isEqual(const PxMat33& s0, const PxMat33& s1)
		{
			PxReal eps = 0.00001f;
			for (PxU32 i = 0; i < 3; ++i)
			{
				for (PxU32 j = 0; j < 3; ++j)
				{
					PxReal t = s0[i][j] - s1[i][j];
					if (PxAbs(t) > eps)
						return false;
				}
			}

			return true;
		}

		PX_FORCE_INLINE bool isEqual(const SpatialMatrix& s)
		{
			bool topLeftEqual = isEqual(topLeft, s.topLeft);
			bool topRightEqual = isEqual(topRight, s.topRight);
			bool bottomLeftEqual = isEqual(bottomLeft, s.bottomLeft);

			return topLeftEqual && topRightEqual && bottomLeftEqual;
		}

		static PX_CUDA_CALLABLE PX_FORCE_INLINE PxMat33 invertSym33(const PxMat33& in)
		{
			PxVec3 v0 = in[1].cross(in[2]),
				v1 = in[2].cross(in[0]),
				v2 = in[0].cross(in[1]);

			PxReal det = v0.dot(in[0]);

			if (det != 0)
			{
				PxReal recipDet = 1.0f / det;

				return PxMat33(v0 * recipDet,
					PxVec3(v0.y, v1.y, v1.z) * recipDet,
					PxVec3(v0.z, v1.z, v2.z) * recipDet);
			}
			else
			{
				return PxMat33(PxIdentity);
			}
		}

		static PX_FORCE_INLINE Ps::aos::Mat33V invertSym33(const Ps::aos::Mat33V& in)
		{
			using namespace Ps::aos;
			const Vec3V v0 = V3Cross(in.col1, in.col2);
			const Vec3V v1 = V3Cross(in.col2, in.col0);
			const Vec3V v2 = V3Cross(in.col0, in.col1);

			FloatV det = V3Dot(v0, in.col0);

			const FloatV recipDet = FRecip(det);

			if (!FAllEq(det, FZero()))
			{
				return Mat33V(V3Scale(v0, recipDet),
					V3Scale(V3Merge(V3GetY(v0), V3GetY(v1), V3GetZ(v1)), recipDet),
					V3Scale(V3Merge(V3GetZ(v0), V3GetZ(v1), V3GetZ(v2)), recipDet));
			}
			else
			{
				return Mat33V(V3UnitX(), V3UnitY(), V3UnitZ());
			}

			//return M33Inverse(in);
		}

		PX_CUDA_CALLABLE PX_FORCE_INLINE SpatialMatrix invertInertia()
		{
			PxMat33 aa = bottomLeft, ll = topRight, la = topLeft;

			aa = (aa + aa.getTranspose())*0.5f;
			ll = (ll + ll.getTranspose())*0.5f;

			PxMat33 AAInv = invertSym33(aa);

			PxMat33 z = -la * AAInv;
			PxMat33 S = ll + z * la.getTranspose();	// Schur complement of mAA

			PxMat33 LL = invertSym33(S);

			PxMat33 LA = LL * z;
			PxMat33 AA = AAInv + z.getTranspose() * LA;

			SpatialMatrix result(LA.getTranspose(), AA, LL);// , LA);

			return result;
		}

		PX_FORCE_INLINE void M33Store(const Ps::aos::Mat33V& src, PxMat33& dest)
		{
			Ps::aos::V3StoreU(src.col0, dest.column0);
			Ps::aos::V3StoreU(src.col1, dest.column1);
			Ps::aos::V3StoreU(src.col2, dest.column2);
		}

		PX_FORCE_INLINE void invertInertiaV(SpatialMatrix& result)
		{
			using namespace Ps::aos;
			Mat33V aa = M33Load(bottomLeft), ll = M33Load(topRight), la = M33Load(topLeft);

			aa = M33Scale(M33Add(aa, M33Trnsps(aa)), FHalf());
			ll = M33Scale(M33Add(ll, M33Trnsps(ll)), FHalf());

			Mat33V AAInv = invertSym33(aa);

			Mat33V z = M33MulM33(M33Neg(la), AAInv);
			Mat33V S = M33Add(ll, M33MulM33(z, M33Trnsps(la)));	// Schur complement of mAA

			Mat33V LL = invertSym33(S);

			Mat33V LA = M33MulM33(LL, z);
			Mat33V AA = M33Add(AAInv, M33MulM33(M33Trnsps(z), LA));

			M33Store(M33Trnsps(LA), result.topLeft);
			M33Store(AA, result.topRight);
			M33Store(LL, result.bottomLeft);
		}

		SpatialMatrix getInverse()
		{
			PxMat33 bottomRight = topLeft.getTranspose();

			PxMat33 blInverse = bottomLeft.getInverse();
			PxMat33 lComp0 = blInverse * (-bottomRight);
			PxMat33 lComp1 = topLeft * lComp0 + topRight;

			//This can be simplified
			PxMat33 newBottomLeft = lComp1.getInverse();
			PxMat33 newTopLeft = lComp0 * newBottomLeft;

			PxMat33 trInverse = topRight.getInverse();
			PxMat33 rComp0 = trInverse * (-topLeft);
			PxMat33 rComp1 = bottomLeft + bottomRight * rComp0;

			PxMat33 newTopRight = rComp1.getInverse();
			
			return SpatialMatrix(newTopLeft, newTopRight, newBottomLeft);
		}

		void zero()
		{
			topLeft = PxMat33(PxZero);
			topRight = PxMat33(PxZero);
			bottomLeft = PxMat33(PxZero);
		}

	};

	struct SpatialImpulseResponseMatrix
	{
		Cm::SpatialVectorF rows[6];

		Cm::SpatialVectorF getResponse(const Cm::SpatialVectorF& impulse) const
		{
			/*return rows[0] * impulse.top.x + rows[1] * impulse.top.y + rows[2] * impulse.top.z
			+ rows[3] * impulse.bottom.x + rows[4] * impulse.bottom.y + rows[5] * impulse.bottom.z;*/

			using namespace Ps::aos;
			Cm::SpatialVectorV row0(V3LoadA(&rows[0].top.x), V3LoadA(&rows[0].bottom.x));
			Cm::SpatialVectorV row1(V3LoadA(&rows[1].top.x), V3LoadA(&rows[1].bottom.x));
			Cm::SpatialVectorV row2(V3LoadA(&rows[2].top.x), V3LoadA(&rows[2].bottom.x));
			Cm::SpatialVectorV row3(V3LoadA(&rows[3].top.x), V3LoadA(&rows[3].bottom.x));
			Cm::SpatialVectorV row4(V3LoadA(&rows[4].top.x), V3LoadA(&rows[4].bottom.x));
			Cm::SpatialVectorV row5(V3LoadA(&rows[5].top.x), V3LoadA(&rows[5].bottom.x));

			Vec4V top = V4LoadA(&impulse.top.x);
			Vec4V bottom = V4LoadA(&impulse.bottom.x);

			const FloatV ix = V4GetX(top);
			const FloatV iy = V4GetY(top);
			const FloatV iz = V4GetZ(top);
			const FloatV ia = V4GetX(bottom);
			const FloatV ib = V4GetY(bottom);
			const FloatV ic = V4GetZ(bottom);

			Cm::SpatialVectorV res =  row0 * ix + row1 * iy + row2 * iz + row3 * ia + row4 * ib + row5 * ic;

			Cm::SpatialVectorF returnVal;
			V4StoreA(Vec4V_From_Vec3V(res.linear), &returnVal.top.x);
			V4StoreA(Vec4V_From_Vec3V(res.angular), &returnVal.bottom.x);

			return returnVal;

		}

		Cm::SpatialVectorV getResponse(const Cm::SpatialVectorV& impulse) const
		{
			using namespace Ps::aos;
			Cm::SpatialVectorV row0(V3LoadA(&rows[0].top.x), V3LoadA(&rows[0].bottom.x));
			Cm::SpatialVectorV row1(V3LoadA(&rows[1].top.x), V3LoadA(&rows[1].bottom.x));
			Cm::SpatialVectorV row2(V3LoadA(&rows[2].top.x), V3LoadA(&rows[2].bottom.x));
			Cm::SpatialVectorV row3(V3LoadA(&rows[3].top.x), V3LoadA(&rows[3].bottom.x));
			Cm::SpatialVectorV row4(V3LoadA(&rows[4].top.x), V3LoadA(&rows[4].bottom.x));
			Cm::SpatialVectorV row5(V3LoadA(&rows[5].top.x), V3LoadA(&rows[5].bottom.x));

			const Vec3V top = impulse.linear;
			const Vec3V bottom = impulse.angular;

			const FloatV ix = V3GetX(top);
			const FloatV iy = V3GetY(top);
			const FloatV iz = V3GetZ(top);
			const FloatV ia = V3GetX(bottom);
			const FloatV ib = V3GetY(bottom);
			const FloatV ic = V3GetZ(bottom);

			Cm::SpatialVectorV res = row0 * ix + row1 * iy + row2 * iz + row3 * ia + row4 * ib + row5 * ic;
			return res;
		}
	};

	struct Temp6x6Matrix;

	struct Temp6x3Matrix
	{
		PxReal column[3][6];
	public:

		Temp6x3Matrix()
		{

		}

		Temp6x3Matrix(const Cm::SpatialVectorF* spatialAxis)
		{
			constructColumn(column[0], spatialAxis[0]);
			constructColumn(column[1], spatialAxis[1]);
			constructColumn(column[2], spatialAxis[2]);
		}

		void constructColumn(PxReal* dest, const Cm::SpatialVectorF& v)
		{
			dest[0] = v.top.x;
			dest[1] = v.top.y;
			dest[2] = v.top.z;

			dest[3] = v.bottom.x;
			dest[4] = v.bottom.y;
			dest[5] = v.bottom.z;
		}

		Temp6x6Matrix operator * (PxReal s[6][3]);

		////s is 3x6 matrix
		//PX_FORCE_INLINE Temp6x6Matrix operator * (PxReal s[6][3])
		//{
		//	Temp6x6Matrix temp;

		//	for (PxU32 i = 0; i < 6; ++i)
		//	{
		//		PxReal* tc = temp.column[i];

		//		for (PxU32 j = 0; j < 6; ++j)
		//		{
		//			tc[j] = 0.f;
		//			for (PxU32 k = 0; k < 3; ++k)
		//			{
		//				tc[j] += column[k][j] * s[i][k];
		//			}
		//		}
		//	}

		//	return temp;
		//}

		PX_FORCE_INLINE Temp6x3Matrix operator * (const PxMat33& s)
		{
			Temp6x3Matrix temp;

			for (PxU32 i = 0; i < 3; ++i)
			{
				PxReal* tc = temp.column[i];
				PxVec3 sc = s[i];

				for (PxU32 j = 0; j < 6; ++j)
				{
					tc[j] = 0.f;
					for (PxU32 k = 0; k < 3; ++k)
					{
						tc[j] += column[k][j] * sc[k];
					}
				}
			}

			return temp;
		}

		PX_FORCE_INLINE bool isColumnEqual(const PxU32 ind, const Cm::SpatialVectorF& col)
		{
			PxReal temp[6];
			constructColumn(temp, col);
			const PxReal eps = 0.00001f;
			for (PxU32 i = 0; i < 6; ++i)
			{
				const PxReal dif = column[ind][i] - temp[i];
				if (PxAbs(dif) > eps)
					return false;
			}
			return true;
		}

	};


	struct Temp6x6Matrix
	{
		PxReal column[6][6];
	public:
		Temp6x6Matrix()
		{

		}

		Temp6x6Matrix(const SpatialMatrix& spatialMatrix)
		{
			constructColumn(column[0], spatialMatrix.topLeft.column0, spatialMatrix.bottomLeft.column0);
			constructColumn(column[1], spatialMatrix.topLeft.column1, spatialMatrix.bottomLeft.column1);
			constructColumn(column[2], spatialMatrix.topLeft.column2, spatialMatrix.bottomLeft.column2);

			const PxMat33 bottomRight = spatialMatrix.getBottomRight();
			constructColumn(column[3], spatialMatrix.topRight.column0, bottomRight.column0);
			constructColumn(column[4], spatialMatrix.topRight.column1, bottomRight.column1);
			constructColumn(column[5], spatialMatrix.topRight.column2, bottomRight.column2);
		}

		void constructColumn(const PxU32 ind, const PxReal* const values)
		{
			for (PxU32 i = 0; i < 6; ++i)
			{
				column[ind][i] = values[i];
			}
		}

		void constructColumn(PxReal* dest, const PxVec3& top, const PxVec3& bottom)
		{
			dest[0] = top.x;
			dest[1] = top.y;
			dest[2] = top.z;

			dest[3] = bottom.x;
			dest[4] = bottom.y;
			dest[5] = bottom.z;
		}

		Temp6x6Matrix getTranspose() const
		{
			Temp6x6Matrix temp;
			for (PxU32 i = 0; i < 6; ++i)
			{
				for (PxU32 j = 0; j < 6; ++j)
				{
					temp.column[i][j] = column[j][i];
				}
			}
			return temp;
		}

		PX_FORCE_INLINE Cm::SpatialVector operator * (const Cm::SpatialVector& s) const
		{
			Temp6x6Matrix tempMatrix = getTranspose();
			PxReal st[6];
			st[0] = s.angular.x; st[1] = s.angular.y; st[2] = s.angular.z;
			st[3] = s.linear.x; st[4] = s.linear.y; st[5] = s.linear.z;

			PxReal result[6];
			for (PxU32 i = 0; i < 6; i++)
			{
				result[i] = 0;
				for (PxU32 j = 0; j < 6; ++j)
				{
					result[i] += tempMatrix.column[i][j] * st[j];
				}
			}


			Cm::SpatialVector temp;
			temp.angular.x = result[0]; temp.angular.y = result[1]; temp.angular.z = result[2];
			temp.linear.x = result[3]; temp.linear.y = result[4]; temp.linear.z = result[5];
			return temp;
		}


		PX_FORCE_INLINE Cm::SpatialVectorF operator * (const Cm::SpatialVectorF& s) const
		{
			PxReal st[6];
			st[0] = s.top.x; st[1] = s.top.y; st[2] = s.top.z;
			st[3] = s.bottom.x; st[4] = s.bottom.y; st[5] = s.bottom.z;

			PxReal result[6];
			for (PxU32 i = 0; i < 6; ++i)
			{
				result[i] = 0.f;
				for (PxU32 j = 0; j < 6; ++j)
				{
					result[i] += column[j][i] * st[j];
				}
			}

			Cm::SpatialVectorF temp;
			temp.top.x = result[0]; temp.top.y = result[1]; temp.top.z = result[2];
			temp.bottom.x = result[3]; temp.bottom.y = result[4]; temp.bottom.z = result[5];
			return temp;
		}

		PX_FORCE_INLINE Temp6x3Matrix operator * (const Temp6x3Matrix& s) const
		{
			Temp6x3Matrix temp;
			for (PxU32 i = 0; i < 3; ++i)
			{
				PxReal* result = temp.column[i];

				const PxReal* input = s.column[i];

				for (PxU32 j = 0; j < 6; ++j)
				{
					result[j] = 0.f;
					for (PxU32 k = 0; k < 6; ++k)
					{
						result[j] += column[k][j] * input[k];
					}
				}
			}

			return temp;

		}

		PX_FORCE_INLINE Cm::SpatialVector spatialVectorMul(const Cm::SpatialVector& s)
		{
			PxReal st[6];
			st[0] = s.angular.x; st[1] = s.angular.y; st[2] = s.angular.z;
			st[3] = s.linear.x; st[4] = s.linear.y; st[5] = s.linear.z;

			PxReal result[6];
			for (PxU32 i = 0; i < 6; ++i)
			{
				result[i] = 0.f;
				for (PxU32 j = 0; j < 6; j++)
				{
					result[i] += column[i][j] * st[j];
				}
			}

			Cm::SpatialVector temp;
			temp.angular.x = result[0]; temp.angular.y = result[1]; temp.angular.z = result[2];
			temp.linear.x = result[3]; temp.linear.y = result[4]; temp.linear.z = result[5];
			return temp;
		}

		PX_FORCE_INLINE bool isEqual(const Cm::SpatialVectorF* m)
		{
			PxReal temp[6];
			PxReal eps = 0.00001f;
			for (PxU32 i = 0; i < 6; ++i)
			{
				temp[0] = m[i].top.x; temp[1] = m[i].top.y; temp[2] = m[i].top.z;
				temp[3] = m[i].bottom.x; temp[4] = m[i].bottom.y; temp[5] = m[i].bottom.z;

				for (PxU32 j = 0; j < 6; ++j)
				{
					PxReal dif = column[i][j] - temp[j];
					if (PxAbs(dif) > eps)
						return false;
				}
			}

			return true;
		}
	};

	//s is 3x6 matrix
	PX_FORCE_INLINE Temp6x6Matrix Temp6x3Matrix::operator * (PxReal s[6][3])
	{
		Temp6x6Matrix temp;

		for (PxU32 i = 0; i < 6; ++i)
		{
			PxReal* tc = temp.column[i];

			for (PxU32 j = 0; j < 6; ++j)
			{
				tc[j] = 0.f;
				for (PxU32 k = 0; k < 3; ++k)
				{
					tc[j] += column[k][j] * s[i][k];
				}
			}
		}

		return temp;
	}

	PX_FORCE_INLINE void calculateNewVelocity(const PxTransform& newTransform, const PxTransform& oldTransform,
		const PxReal dt, PxVec3& linear, PxVec3& angular)
	{
		//calculate the new velocity
		linear = (newTransform.p - oldTransform.p) / dt;
		PxQuat quat = newTransform.q * oldTransform.q.getConjugate();

		if (quat.w < 0)	//shortest angle.
			quat = -quat;

		PxReal angle;
		PxVec3 axis;
		quat.toRadiansAndUnitAxis(angle, axis);
		angular = (axis * angle) / dt;
	}


	// generates a pair of quaternions (swing, twist) such that in = swing * twist, with
	// swing.x = 0
	// twist.y = twist.z = 0, and twist is a unit quat
	PX_CUDA_CALLABLE PX_FORCE_INLINE void separateSwingTwist(const PxQuat& q, PxQuat& twist, PxQuat& swing1, PxQuat& swing2)
	{
		twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity);
		PxQuat swing = q * twist.getConjugate();
		swing1 = swing.y != 0.f ? PxQuat(0.f, swing.y, 0.f, swing.w).getNormalized() : PxQuat(PxIdentity);
		swing = swing * swing1.getConjugate();
		swing2 = swing.z != 0.f ? PxQuat(0.f, 0.f, swing.z, swing.w).getNormalized() : PxQuat(PxIdentity);
	}

	PX_CUDA_CALLABLE PX_FORCE_INLINE void separateSwingTwist2(const PxQuat& q, PxQuat& twist, PxQuat& swing1, PxQuat& swing2)
	{
		swing2 = q.z != 0.0f ? PxQuat(0.f, 0.f, q.z, q.w).getNormalized() : PxQuat(PxIdentity);
		PxQuat swing = q * swing2.getConjugate();
		swing1 = swing.y != 0.f ? PxQuat(0.f, swing.y, 0.f, swing.w).getNormalized() : PxQuat(PxIdentity);
		swing = swing * swing1.getConjugate();
		twist = swing.x != 0.f ? PxQuat(swing.x, 0.f, 0.f, swing.w).getNormalized() : PxQuat(PxIdentity);
	}


} //namespace Dy

}

#endif
