static PxTransform computeBoneTransform(PxTransform &rootTransform, Acclaim::Bone &bone, PxVec3* boneFrameData)
{		
	using namespace Acclaim;

	//PxTransform rootTransform(PxVec3(0.0f), PxQuat(PxIdentity));
	PxTransform parentTransform = (bone.mParent) ? 
		computeBoneTransform(rootTransform, *bone.mParent, boneFrameData) : rootTransform;
		
	PxQuat qWorld = EulerAngleToQuat(bone.mAxis);
	PxVec3 offset = bone.mLength * bone.mDirection;
	PxQuat qDelta = PxQuat(PxIdentity);
	PxVec3 boneData = boneFrameData[bone.mID-1];
	
	if (bone.mDOF & BoneDOFFlag::eRX)
		qDelta = PxQuat(Ps::degToRad(boneData.x), PxVec3(1.0f, 0.0f, 0.0f)) * qDelta;
	if (bone.mDOF & BoneDOFFlag::eRY)
		qDelta = PxQuat(Ps::degToRad(boneData.y), PxVec3(0.0f, 1.0f, 0.0f)) * qDelta;
	if (bone.mDOF & BoneDOFFlag::eRZ)
		qDelta = PxQuat(Ps::degToRad(boneData.z), PxVec3(0.0f, 0.0f, 1.0f)) * qDelta;

	PxQuat boneOrientation = qWorld * qDelta * qWorld.getConjugate();

	PxTransform boneTransform(boneOrientation.rotate(offset), boneOrientation);

	return parentTransform.transform(boneTransform);
}
bool Character::buildMotion(Acclaim::AMCData &amcData, Motion &motion, PxU32 start, PxU32 end)
{
	using namespace Acclaim;

	if  (mASFData == NULL)
		return false;

	motion.mNbFrames = end - start + 1;
	motion.mMotionData = SAMPLE_NEW(MotionData)[motion.mNbFrames];

	// compute bounds of all the motion data on normalized frame
	PxBounds3 bounds = PxBounds3::empty();
	for (PxU32 i = start; i < end; i++)
	{
		Acclaim::FrameData &frameData = amcData.mFrameData[i];

		PxTransform rootTransform(PxVec3(0.0f), EulerAngleToQuat(frameData.mRootOrientation));

		for (PxU32 j = 0; j < mASFData->mNbBones; j++)
		{
			PxTransform t = computeBoneTransform(rootTransform, mASFData->mBones[j], frameData.mBoneFrameData);
			bounds.include(t.p);
		}
	}

	Acclaim::FrameData& firstFrame = amcData.mFrameData[0];
	Acclaim::FrameData& lastFrame = amcData.mFrameData[amcData.mNbFrames-1];

	// compute direction vector
	motion.mDistance = mCharacterScale * (lastFrame.mRootPosition - firstFrame.mRootPosition).magnitude();

	PxVec3 firstPosition = firstFrame.mRootPosition;
	PX_UNUSED(firstPosition);
	PxVec3 firstAngles = firstFrame.mRootOrientation;
	PxQuat firstOrientation = EulerAngleToQuat(PxVec3(0, firstAngles.y, 0));

	for (PxU32 i = 0; i < motion.mNbFrames; i++)
	{
		Acclaim::FrameData& frameData = amcData.mFrameData[i+start];
		MotionData &motionData = motion.mMotionData[i];

		// normalize y-rot by computing inverse quat from first frame
		// this makes all the motion aligned in the same (+ z) direction.
		PxQuat currentOrientation = EulerAngleToQuat(frameData.mRootOrientation);
		PxQuat qdel = firstOrientation.getConjugate() * currentOrientation;
		PxTransform rootTransform(PxVec3(0.0f), qdel);
		
		for (PxU32 j = 0; j < mNbBones; j++)
		{
			PxTransform boneTransform = computeBoneTransform(rootTransform, mASFData->mBones[j], frameData.mBoneFrameData);
			motionData.mBoneTransform[j] = boneTransform;
		}

		//PxReal y = mCharacterScale * (frameData.mRootPosition.y - firstPosition.y) - bounds.minimum.y;
		motionData.mRootTransform = PxTransform(PxVec3(0.0f, -bounds.minimum.y, 0.0f), PxQuat(PxIdentity));
	}

	// now make the motion cyclic by linear interpolating root position and joint angles
	const PxU32 windowSize = 10;
	for (PxU32 i = 0; i <= windowSize; i++)
	{
		PxU32 j = motion.mNbFrames - 1 - windowSize + i;

		PxReal t = PxReal(i) / PxReal(windowSize);

		MotionData& motion_i = motion.mMotionData[0];
		MotionData& motion_j = motion.mMotionData[j];

		// lerp root translation
		PxVec3 blendedRootPos = (1.0f - t) * motion_j.mRootTransform.p  + t * motion_i.mRootTransform.p;
		for (PxU32 k = 0; k < mNbBones; k++)
		{
			PxVec3 pj = motion_j.mRootTransform.p + motion_j.mBoneTransform[k].p;
			PxVec3 pi = motion_i.mRootTransform.p + motion_i.mBoneTransform[k].p;

			PxVec3 p = (1.0f - t) * pj + t * pi;
			motion_j.mBoneTransform[k].p = p - blendedRootPos;
		}
		motion_j.mRootTransform.p = blendedRootPos;
	}

	return true;
}