//===========================================================================//
// File:	AnimIteratorManager.cpp                                                 
// Project: MechWarrior 4
// Contents: 
//			
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 07/01/98  JSE  Initial coding, 
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive, Inc.
// All Rights reserved worldwide
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//===========================================================================//


#include <MW4\MW4AnimationSystem.hpp>

#include <Stuff\MemoryBlock.hpp>

namespace MechWarrior4 {
	DECLARE_TIMER(extern, RunAnimStates);
	DECLARE_TIMER(extern, Iterate);
	DECLARE_TIMER(extern, Blend);
	DECLARE_TIMER(extern, Apply);
	DECLARE_TIMER(extern, BlendAlloc);
	DECLARE_TIMER(extern, blendDealloc);
	DECLARE_TIMER(extern, BlendAnimCombine);
	DECLARE_TIMER(extern, BlendCombine);
	DECLARE_TIMER(extern, BlendAndApply);
};

using namespace MW4Animation;
using namespace MechWarrior4;

//define MW4_ANIMATION_HIER_COUNT


bool AnimHierarchyIteratorManager::doTransitionBlend = true;

#if 0
#define ANIM_LOGIC(string) LOGIC("Animation::" string)
#else
#define ANIM_LOGIC(string)
#endif

//
//#############################################################################
//#############################################################################
//


Stuff::MemoryBlock*
	BlendBuffer::AllocatedMemory = NULL;

void BlendBuffer::InitializeClass()
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new Stuff::MemoryBlock(
			sizeof(BlendBuffer),
			10,
			2,
			"Animation-BlendBuffer"
		);
	Register_Object(AllocatedMemory);
}

void BlendBuffer::TerminateClass()
{
	Unregister_Object(AllocatedMemory);
	delete AllocatedMemory;
	AllocatedMemory = NULL;
}

//
//#############################################################################
//#############################################################################
//

Stuff::MemoryBlock*
	AnimHierarchyNode::AllocatedMemory = NULL;

void AnimHierarchyNode::InitializeClass()
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new Stuff::MemoryBlock(
			sizeof(AnimHierarchyNode),
			10,
			2,
			"Animation-BlendHeirarchy"
		);
	Register_Object(AllocatedMemory);
}

void AnimHierarchyNode::TerminateClass()
{
	Unregister_Object(AllocatedMemory);
	delete AllocatedMemory;
	AllocatedMemory = NULL;
}



//
//#############################################################################
//#############################################################################
//


AnimHierarchyNode::AnimHierarchyNode(Stuff::Scalar blend_value):
	Plug(DefaultData),
	childrenNodes(NULL),
	animIterators(NULL),
	parentNode(NULL)
{
	Verify(blend_value >= 0);
	blendValue = blend_value;
}

//
//#############################################################################
//#############################################################################
//


AnimHierarchyNode::~AnimHierarchyNode()
{
	Stuff::ChainIteratorOf<AnimHierarchyNode*> iterator1(&childrenNodes);
	Stuff::Plug *anim_plug;
	while ((anim_plug = iterator1.GetCurrent()) != NULL)
	{
		iterator1.Remove();
		Unregister_Object(anim_plug);
		delete anim_plug;
	}

	// we don't own these so we don't delete them
	Stuff::ChainIteratorOf<AnimIterator*> iterator2(&animIterators);
	while ((anim_plug = iterator1.GetCurrent()) != NULL)
	{
		iterator2.Remove();
	}
}

//
//#############################################################################
//#############################################################################
//

AnimHierarchyIteratorManager::AnimHierarchyIteratorManager(UserDefinedApplyChannelFunction apply_function, void *user_data):
	Plug(DefaultData),
	iteratorHierarchy(NULL),
	currentLayer(NULL)
{
	Check_Pointer(apply_function);
	Check_Pointer(user_data);
	applyFunction = apply_function;
	userData = user_data;
}

//
//#############################################################################
//#############################################################################
//

AnimHierarchyIteratorManager::~AnimHierarchyIteratorManager()
{

}

//
//#############################################################################
//#############################################################################
//

#if defined(MW4_ANIMATION_HIER_COUNT)
	static int AnimationHierarchyPopCount = 0;
	static int AnimationHierarchyPushCount = 0;
#endif

AnimHierarchyNode * AnimHierarchyIteratorManager::PushLayer(Stuff::Scalar blend_value)
{
	#if defined(MW4_ANIMATION_HIER_COUNT)
	++AnimationHierarchyPushCount;
	#endif

	if (iteratorHierarchy.GetCurrent() == NULL)
	{
		Verify(currentLayer.GetCurrent() == NULL);

		AnimHierarchyNode *new_node = new AnimHierarchyNode(blend_value);
		Register_Object(new_node);

		iteratorHierarchy.Add(new_node);
		currentLayer.Add(new_node);
	}
	else
	{
		AnimHierarchyNode *new_node = new AnimHierarchyNode(blend_value);
		Register_Object(new_node);

		AnimHierarchyNode *parent_node = currentLayer.GetCurrent();
		Check_Object(parent_node);

		parent_node->childrenNodes.Add(new_node);
		new_node->parentNode.Add(parent_node);
		
		currentLayer.Remove();
		currentLayer.Add(new_node);
	}

	return currentLayer.GetCurrent();
}

//
//#############################################################################
//#############################################################################
//

void AnimHierarchyIteratorManager::UndoPush()
{
	AnimHierarchyNode *cur_node = currentLayer.GetCurrent();
	Check_Object(cur_node);

	#if defined(MW4_ANIMATION_HIER_COUNT)
	++AnimationHierarchyPopCount;
	#endif

	if( cur_node->parentNode.GetCurrent() != NULL )
	{
		currentLayer.Remove();
		currentLayer.Add(cur_node->parentNode.GetCurrent());
		Unregister_Object(cur_node);
		delete cur_node;
	}
	else
	{
		STOP(("AnimHierarchyIteratorManager::UndoPush() : UNHANDLED CASE"));
		// if it is equal to null we are at the top of the stack...
	}
}

//
//#############################################################################
//#############################################################################
//

void AnimHierarchyIteratorManager::PopLayer()
{

	AnimHierarchyNode *cur_node = currentLayer.GetCurrent();
	Check_Object(cur_node);

	#if defined(MW4_ANIMATION_HIER_COUNT)
	++AnimationHierarchyPopCount;
	#endif

	if( cur_node->parentNode.GetCurrent() != NULL )
	{
		currentLayer.Remove();
		currentLayer.Add(cur_node->parentNode.GetCurrent());
	}
	else
	{
		currentLayer.Remove();

		#if defined(MW4_ANIMATION_HIER_COUNT)
		Verify(AnimationHierarchyPopCount == AnimationHierarchyPushCount);
		AnimationHierarchyPopCount = 0;
		AnimationHierarchyPushCount = 0;
		#endif
		// if it is equal to null we are at the top of the stack...
	}

}

//
//#############################################################################
//#############################################################################
//

void AnimHierarchyIteratorManager::AddAnimToCurrentLayer(AnimIterator *iterator)
{
	Check_Object(iterator);
	AnimHierarchyNode *cur_node = currentLayer.GetCurrent();
	Check_Object(cur_node);
	
	cur_node->animIterators.Add(iterator);

}

//
//#############################################################################
//#############################################################################
//






void AnimHierarchyIteratorManager::BlendAndApply
(
	int total_joint_count, 
	void *override_user_data, 
	UserDefinedApplyChannelFunction override_apply_function
)
{

	ANIM_LOGIC("BlendAndApply");
	Start_Timer(BlendAndApply);


	if (override_user_data == NULL)
	{
		override_user_data = userData;
	}
	if (override_apply_function == NULL)
	{
		override_apply_function = applyFunction;
	}

	if (iteratorHierarchy.GetCurrent() != NULL)
	{
		

		ANIM_LOGIC("BlendAndApply::Allocation");
		Start_Timer(BlendAlloc);

		AnimHierarchyNode *root_node = iteratorHierarchy.GetCurrent();
		Check_Object(root_node);
		
		//blend
		#if defined(MW4_ANIMATION_HIER_COUNT)
		TabCount = 0;
		BlendCount = 0;
		#endif

		BlendBuffer *blend_result = new BlendBuffer(total_joint_count);
		Register_Pointer(blend_result);
		Stop_Timer(BlendAlloc);

		Stuff::Scalar small_backup = Stuff::SMALL;
		Stuff::SMALL = 0.5f;

		{
			ANIM_LOGIC("BlendAndApply::Blend");
			Start_Timer(Blend);
			Blend(root_node, blend_result);
			Stop_Timer(Blend);
		}

		

		#if defined(MW4_ANIMATION_HIER_COUNT)
		SPEW(("jerryeds", "Blend Buffer Allocations : %d", BlendCount ));
		SPEW(("jerryeds", ""));
		#endif

		{
			ANIM_LOGIC("BlendAndApply::Apply");
			Start_Timer(Apply);
			// apply result
			Apply(blend_result, override_user_data, override_apply_function);
			Stop_Timer(Apply);
		}


		Stuff::SMALL = small_backup;

		{
			ANIM_LOGIC("BlendAndApply::Dealloc");
			Start_Timer(blendDealloc);

			Unregister_Pointer(blend_result);
			delete blend_result;

			//then delete...
			Unregister_Object(root_node);
			delete root_node;

			Stop_Timer(blendDealloc);
		}
		
		
	}

	Stop_Timer(BlendAndApply);

}

//
//#############################################################################
//#############################################################################
//

void AnimHierarchyIteratorManager::Apply(
		BlendBuffer *blend_result, 
		void *user_data, 
		UserDefinedApplyChannelFunction apply_function
	)
{

	for (int i = 0; i < blend_result->numberOfJoints; i++)
	{
		ChannelApplication channel_app;

		bool dirty = false;
		if(blend_result->IsPositionDirty(i))
		{
			channel_app.positionValue = blend_result->positionBuffer[i];
			channel_app.hasPosition = true;
			dirty = true;
		}
		if(blend_result->IsRotationDirty(i))
		{
			channel_app.rotationValue = blend_result->rotationBuffer[i];
			channel_app.hasRotation = true;
			dirty = true;
		}
		if(blend_result->IsVelocityDirty(i))
		{

			channel_app.velocityValue = blend_result->velocityBuffer[i];

			if (blend_result->IsAngularVelocityDirty(i))
			{
				Verify(blend_result->IsRotationDirty(i));
				Stuff::LinearMatrix4D mat(Stuff::LinearMatrix4D::Identity);
				mat.BuildRotation(channel_app.rotationValue);

				channel_app.velocityValue.MultiplyByInverse(blend_result->velocityBuffer[i], mat);

			}
			else
			{
				channel_app.velocityValue = blend_result->velocityBuffer[i];
			}

			channel_app.hasVelocity = true;
			dirty = true;
		}
		if(blend_result->IsAngularVelocityDirty(i))
		{
			channel_app.angularVelocityValue = blend_result->angularVelocityBuffer[i];
			channel_app.hasAngularVelocity = true;
			dirty = true;
		}
		if(blend_result->IsTriggerDirty(i))
		{
			channel_app.triggerValue = blend_result->triggerBuffer[i];
			channel_app.hasTrigger = true;
			dirty = true;
		}

		if (dirty)
		{
			(*apply_function)(channel_app, i, user_data);
		}
	}


}

//
//#############################################################################
//#############################################################################
//

void AnimHierarchyIteratorManager::Blend(AnimHierarchyNode *current_node, BlendBuffer *blend_result)
{
	// normalize...
	Stuff::ChainIteratorOf<AnimHierarchyNode*> node_iterator(&current_node->childrenNodes);
	Stuff::ChainIteratorOf<AnimIterator*> anim_iterator(&current_node->animIterators);

	AnimHierarchyNode *child_node;
	AnimIterator *child_anim;

	Stuff::Scalar total_node_weight = 0.0f;
	Stuff::Scalar total_anim_weight = 0.0f;
	Stuff::Scalar total_weight = 0.0f;

	int child_node_count = 0;
	int child_animation_count = 0;

	#if defined(MW4_ANIMATION_HIER_COUNT)

		for (int i = 0; i < TabCount; ++i)
		{
			SPEW(("jerryeds", "\t+"));
		}
		SPEW(("jerryeds","|_"));
		++TabCount;


	#endif


	// normalize stuff
	while ((child_node = node_iterator.ReadAndNext()) != NULL)
	{
		total_node_weight += child_node->blendValue;
		++child_node_count;
	}
	
	while ((child_anim = anim_iterator.ReadAndNext()) != NULL)
	{
		total_anim_weight += child_anim->GetBlendValue();
		++child_animation_count;
	}

	total_weight = total_node_weight + total_anim_weight;

	Stuff::Scalar current_weight = 0.0f;

	// mix in the node children...
	if(total_node_weight != 0.0f)
	{
		node_iterator.First();
		if(child_node_count == 1)
		{
			Blend(node_iterator.GetCurrent(), blend_result);
		}
		else
		{
			BlendBuffer *new_blend_result;
			{
				ANIM_LOGIC("BlendAndApply::Blend::Alloc");
				Start_Timer(BlendAlloc);

				new_blend_result = new BlendBuffer(blend_result->numberOfJoints);
				Register_Pointer(new_blend_result);

				Stop_Timer(BlendAlloc);
			}

			while ((child_node = node_iterator.ReadAndNext()) != NULL)
			{
				if (child_node->blendValue > 0.0f)
				{
					#if defined(MW4_ANIMATION_HIER_COUNT)
					for (int tab_count = 0; tab_count < TabCount; ++tab_count)
					{
						SPEW(("jerryeds", "\t+"));
					}

					SPEW(("jerryeds", "|_ CB : %ff :", child_node->blendValue/total_weight));
					#endif

					Stuff::Scalar unitized_weight = child_node->blendValue/total_weight;
					current_weight += unitized_weight;
					Stuff::Scalar local_unitized_weight = 0.0f;
					
					if (current_weight != 0.0f)
					{
						local_unitized_weight = unitized_weight/current_weight;
					}

					Blend(child_node, new_blend_result);
					Combine(blend_result, new_blend_result, local_unitized_weight);
				}
			}

			{
				ANIM_LOGIC("BlendAndApply::Blend::Dealloc");
				Start_Timer(blendDealloc);

				Unregister_Pointer(new_blend_result);
				delete new_blend_result;

				Stop_Timer(blendDealloc);
			}
		}
	}

	if (total_anim_weight != 0.0f)
	{
		anim_iterator.First();
		while ((child_anim = anim_iterator.ReadAndNext()) != NULL)
		{
			if (child_anim->GetBlendValue() > 0.0f)
			{
				Stuff::Scalar unitized_weight = child_anim->GetBlendValue()/total_weight;
				current_weight += unitized_weight;
				Stuff::Scalar local_unitized_weight = 0.0f;
				
				if (current_weight != 0.0f)
				{
					local_unitized_weight = unitized_weight/current_weight;
				}

				AnimCombine(blend_result, child_anim, local_unitized_weight);
			}
		}
	}

	#if defined(MW4_ANIMATION_HIER_COUNT)
	--TabCount;
	#endif

}


//
//#############################################################################
//#############################################################################
//

void 
	AnimHierarchyIteratorManager::AnimCombine(
		BlendBuffer *target_buffer, 
		AnimIterator *anim_iterator, 
		Stuff::Scalar percent
	)
{
	if (Stuff::Small_Enough(percent, 0.01f))
	{
		return;
	}


	ANIM_LOGIC("BlendAndApply::AnimCombine");
	Start_Timer(BlendAnimCombine);

	#if defined(MW4_ANIMATION_HIER_COUNT)
	for (int i = 0; i < TabCount; ++i)
	{
		SPEW(("jerryeds", "\t+"));
	}

	SPEW(("jerryeds", "|_ AC : %ff :", percent));
	#endif


	
	for (BYTE channel_number = 0; channel_number < anim_iterator->GetAnimChannelCount(); channel_number++)
	{

		ChannelAnimData *channel = &anim_iterator->animationInstance->animData->animChannelBlock[channel_number];
		
		int joint_number = anim_iterator->animationInstance->jointToIndex[channel->jointNumber];
		

		if (joint_number == -1)
		{
			continue;
		}


		if (anim_iterator->DoISkipChannels())
		{
			if (!(anim_iterator->GetAnimData()->animChannelBlock[channel_number].channelType == MW4Animation::ChannelAnimData::VelocityChannelType || 
				 anim_iterator->GetAnimData()->animChannelBlock[channel_number].channelType == MW4Animation::ChannelAnimData::AngularVelocityChannelType) )
			{
				continue;
			}
		}


		if(channel->channelType == MW4Animation::ChannelAnimData::VelocityChannelType)
		{
			Verify(anim_iterator->animationInstance->animData->animChannelBlock[channel_number].keyType ==
			MW4Animation::JointAnimData::VelocityLinearKeyType);


			MW4Animation::BaseKeyframe *nextFrame = anim_iterator->GetForwardKeyFrame(channel_number);
			MW4Animation::BaseKeyframe *lastFrame = anim_iterator->GetReverseKeyFrame(channel_number);
			MW4Animation::BaseKeyframeTime *nextFrameTime = anim_iterator->GetForwardKeyFrameTime(channel_number);
			MW4Animation::BaseKeyframeTime *lastFrameTime = anim_iterator->GetReverseKeyFrameTime(channel_number);


			Stuff::Point3D position(Stuff::Point3D::Identity);
			Stuff::Point3D velocity(Stuff::Point3D::Identity);

			if (lastFrame != NULL && nextFrame != NULL)
			{
				velocity = MW4Animation::Point3DVelPosLinearInterpolator(
					lastFrame, nextFrame, lastFrameTime, nextFrameTime, anim_iterator->GetRealTime());

				position = MW4Animation::Point3DLinearInterpolator(
					lastFrame, nextFrame, lastFrameTime, nextFrameTime, anim_iterator->GetRealTime());
			}
			else if (lastFrame != NULL)
			{
				velocity = MW4Animation::Point3DVelPosSnapInterpolator(
					lastFrame, lastFrame, lastFrameTime, lastFrameTime, anim_iterator->GetRealTime()	);

				position = MW4Animation::Point3DSnapInterpolator(
					lastFrame, lastFrame, lastFrameTime, lastFrameTime, anim_iterator->GetRealTime()	);

			}
			else if (nextFrame != NULL)
			{
				velocity = MW4Animation::Point3DVelPosSnapInterpolator(
					nextFrame, nextFrame, nextFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);
				
				position = MW4Animation::Point3DSnapInterpolator(
					nextFrame, nextFrame, nextFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);

			}
			else
			{
				STOP(("BOTH KEYFRAMES ARE NULL ! : AnimHierarchyIteratorManager::AnimCombine"));
			}
			
			
			velocity.Multiply(
				velocity, 
				anim_iterator->GetVelocityScale());
			
			
			if (!target_buffer->IsPositionDirty(joint_number))
			{
				target_buffer->SetDirtyPosition(joint_number);
				target_buffer->positionBuffer[joint_number] = position;
			}
			else
			{
				target_buffer->positionBuffer[joint_number].Lerp(
					target_buffer->positionBuffer[joint_number], 
					position,
					percent);
			}

			if (!target_buffer->IsVelocityDirty(joint_number))
			{
				target_buffer->SetDirtyVelocity(joint_number);
				target_buffer->velocityBuffer[joint_number] = velocity;
			}
			else
			{
				target_buffer->velocityBuffer[joint_number].Lerp(
					target_buffer->velocityBuffer[joint_number], 
					velocity,
					percent);
			}


		}
		else if (channel->channelType == MW4Animation::ChannelAnimData::AngularVelocityChannelType)
		{
			Verify(anim_iterator->animationInstance->animData->animChannelBlock[channel_number].keyType ==
				MW4Animation::JointAnimData::AngularVelocityLinearKeyType);

			MW4Animation::BaseKeyframe *nextFrame = anim_iterator->GetForwardKeyFrame(channel_number);
			MW4Animation::BaseKeyframe *lastFrame = anim_iterator->GetReverseKeyFrame(channel_number);
			MW4Animation::BaseKeyframeTime *nextFrameTime = anim_iterator->GetForwardKeyFrameTime(channel_number);
			MW4Animation::BaseKeyframeTime *lastFrameTime = anim_iterator->GetReverseKeyFrameTime(channel_number);


			Stuff::UnitQuaternion rotation(Stuff::UnitQuaternion::Identity);
			Stuff::Vector3D		  angular_velocity(Stuff::Vector3D::Identity);

			if (lastFrame != NULL && nextFrame != NULL)
			{
				rotation = MW4Animation::QuaternionLinearInterpolatorSlow(
					lastFrame, nextFrame, lastFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);	

				angular_velocity = MW4Animation::AngularVelocityLinearInterpolator(
					lastFrame, nextFrame, lastFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);

			}
			else if (lastFrame != NULL)
			{
				rotation = MW4Animation::QuaternionSnapInterpolator(
					lastFrame, lastFrame, lastFrameTime, lastFrameTime,anim_iterator->GetRealTime()	);

				angular_velocity = MW4Animation::AngularVelocitySnapInterpolator(
					lastFrame, lastFrame, lastFrameTime, lastFrameTime, anim_iterator->GetRealTime()	);

			}
			else if (nextFrame != NULL)
			{
				rotation = MW4Animation::QuaternionSnapInterpolator(
					nextFrame, nextFrame, nextFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);

				angular_velocity = MW4Animation::AngularVelocitySnapInterpolator(
					nextFrame, nextFrame, nextFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);
			}
			else
			{
				STOP(("BOTH KEYFRAMES ARE NULL ! : AnimHierarchyIteratorManager::AnimCombine"));
			}

			angular_velocity.Multiply(
				angular_velocity, 
				anim_iterator->GetVelocityScale());

			if (!target_buffer->IsRotationDirty(joint_number))
			{
				target_buffer->SetDirtyRotation(joint_number);
				target_buffer->rotationBuffer[joint_number] = rotation;
				
				rotation.FastNormalize();

				#if defined(MW4_ANIMATION_DEBUG)
				rotation.TestInstance();
				#endif
			}
			else
			{
				#if defined(MW4_ANIMATION_DEBUG)
				rotation.TestInstance();
				target_buffer->rotationBuffer[joint_number].TestInstance();
				#endif

				Stuff::UnitQuaternion temp = target_buffer->rotationBuffer[joint_number];
				target_buffer->rotationBuffer[joint_number].Lerp(
					temp, 
					rotation,
					percent);

				target_buffer->rotationBuffer[joint_number].Normalize();

				#if defined(MW4_ANIMATION_DEBUG)
				target_buffer->rotationBuffer[joint_number].TestInstance();
				#endif
			}

			if (!target_buffer->IsAngularVelocityDirty(joint_number))
			{
				target_buffer->SetDirtyAngularVelocity(joint_number);
				target_buffer->angularVelocityBuffer[joint_number] = angular_velocity;
			}
			else
			{
				target_buffer->angularVelocityBuffer[joint_number].Lerp(
					target_buffer->angularVelocityBuffer[joint_number], 
					angular_velocity,
					percent);
			}



		}
		else if (channel->channelType == MW4Animation::ChannelAnimData::PositionChannelType)
		{
			Verify(anim_iterator->animationInstance->animData->animChannelBlock[channel_number].keyType ==
			MW4Animation::JointAnimData::Point3DLinearKeyType);

			MW4Animation::BaseKeyframe *nextFrame = anim_iterator->GetForwardKeyFrame(channel_number);
			MW4Animation::BaseKeyframe *lastFrame = anim_iterator->GetReverseKeyFrame(channel_number);
			MW4Animation::BaseKeyframeTime *nextFrameTime = anim_iterator->GetForwardKeyFrameTime(channel_number);
			MW4Animation::BaseKeyframeTime *lastFrameTime = anim_iterator->GetReverseKeyFrameTime(channel_number);


			Stuff::Point3D position(Stuff::Point3D::Identity);	
			if (lastFrame != NULL && nextFrame != NULL)
			{
				position = MW4Animation::Point3DLinearInterpolator(
					lastFrame, nextFrame, lastFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);	
			}
			else if (lastFrame != NULL)
			{
				position = MW4Animation::Point3DSnapInterpolator(
					lastFrame, lastFrame, lastFrameTime, lastFrameTime, anim_iterator->GetRealTime()	);
			}
			else if (nextFrame != NULL)
			{
				position = MW4Animation::Point3DSnapInterpolator(
					nextFrame, nextFrame, nextFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);
			}
			else
			{
				STOP(("BOTH KEYFRAMES ARE NULL ! : AnimHierarchyIteratorManager::AnimCombine"));
			}

			
			if (!target_buffer->IsPositionDirty(joint_number))
			{
				target_buffer->SetDirtyPosition(joint_number);
				target_buffer->positionBuffer[joint_number] = position;
			}
			else
			{
				if (doTransitionBlend)
				{
					target_buffer->positionBuffer[joint_number].Lerp(
						target_buffer->positionBuffer[joint_number], 
						position,
						percent);
				}
				else
				{
					target_buffer->SetDirtyPosition(joint_number);
					target_buffer->positionBuffer[joint_number] = position;
				}
			}
		
		}
		else if (channel->channelType == MW4Animation::ChannelAnimData::RotationChannelType)
		{
			Verify(anim_iterator->animationInstance->animData->animChannelBlock[channel_number].keyType ==
				MW4Animation::JointAnimData::QuaternionLinearKeyType);

			MW4Animation::BaseKeyframe *nextFrame = anim_iterator->GetForwardKeyFrame(channel_number);
			MW4Animation::BaseKeyframe *lastFrame = anim_iterator->GetReverseKeyFrame(channel_number);
			MW4Animation::BaseKeyframeTime *nextFrameTime = anim_iterator->GetForwardKeyFrameTime(channel_number);
			MW4Animation::BaseKeyframeTime *lastFrameTime = anim_iterator->GetReverseKeyFrameTime(channel_number);

			Stuff::UnitQuaternion rotation(Stuff::UnitQuaternion::Identity);	
			if (lastFrame != NULL && nextFrame != NULL)
			{
				rotation = MW4Animation::QuaternionLinearInterpolator(
					lastFrame, nextFrame, lastFrameTime, nextFrameTime, anim_iterator->GetRealTime());	
			}
			else if (lastFrame != NULL)
			{
				rotation = MW4Animation::QuaternionSnapInterpolator(
					lastFrame, lastFrame, lastFrameTime, lastFrameTime, anim_iterator->GetRealTime());
			}
			else if (nextFrame != NULL)
			{
				rotation = MW4Animation::QuaternionSnapInterpolator(
					nextFrame, nextFrame, nextFrameTime, nextFrameTime, anim_iterator->GetRealTime());
			}
			else
			{
				STOP(("BOTH KEYFRAMES ARE NULL ! : AnimHierarchyIteratorManager::AnimCombine"));
			}

			if (!target_buffer->IsRotationDirty(joint_number))
			{
				target_buffer->SetDirtyRotation(joint_number);
				rotation.FastNormalize();
				target_buffer->rotationBuffer[joint_number] = rotation;
				
				#if defined(MW4_ANIMATION_DEBUG)
				rotation.TestInstance();
				#endif
			}
			else
			{
				#if defined(MW4_ANIMATION_DEBUG)
				rotation.TestInstance();
				target_buffer->rotationBuffer[joint_number].TestInstance();
				#endif


				Stuff::UnitQuaternion temp = target_buffer->rotationBuffer[joint_number];

				target_buffer->rotationBuffer[joint_number].FastLerp(
					temp, 
					rotation,
					percent);

				target_buffer->rotationBuffer[joint_number].FastNormalize();

				#if defined(MW4_ANIMATION_DEBUG)
				target_buffer->rotationBuffer[joint_number].TestInstance();
				#endif
			}
		}
		else if (channel->channelType == MW4Animation::ChannelAnimData::TriggerChannelType)
		{
			Verify(anim_iterator->animationInstance->animData->animChannelBlock[channel_number].keyType ==
			MW4Animation::JointAnimData::TriggerLinearKeyType);

			MW4Animation::BaseKeyframe *nextFrame = anim_iterator->GetForwardKeyFrame(channel_number);
			MW4Animation::BaseKeyframe *lastFrame = anim_iterator->GetReverseKeyFrame(channel_number);
			MW4Animation::BaseKeyframeTime *nextFrameTime = anim_iterator->GetForwardKeyFrameTime(channel_number);
			MW4Animation::BaseKeyframeTime *lastFrameTime = anim_iterator->GetReverseKeyFrameTime(channel_number);

			int trigger = 0;

			if (lastFrame != NULL && nextFrame != NULL)
			{
				trigger = MW4Animation::TriggerSnapInterpolator(
					lastFrame, nextFrame, lastFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);	
			}
			else if (lastFrame != NULL)
			{
				trigger = MW4Animation::TriggerSnapInterpolator(
					lastFrame, lastFrame, lastFrameTime, lastFrameTime, anim_iterator->GetRealTime()	);
			}
			else if (nextFrame != NULL)
			{
				trigger = MW4Animation::TriggerSnapInterpolator(
					nextFrame, nextFrame, nextFrameTime, nextFrameTime, anim_iterator->GetRealTime()	);
			}
			
			if (!target_buffer->IsTriggerDirty(joint_number))
			{
				target_buffer->SetDirtyTrigger(joint_number);
				target_buffer->triggerBuffer[joint_number] = trigger;
			}
			else
			{
				target_buffer->triggerBuffer[joint_number] |= trigger;
			}
		}
	}

	Stop_Timer(BlendAnimCombine);

}

//
//#############################################################################
//#############################################################################
//

void 
	AnimHierarchyIteratorManager::Combine(
		BlendBuffer *target_buffer, 
		BlendBuffer *new_buffer, 
		Stuff::Scalar percent
	)
{
	if (Stuff::Small_Enough(percent,0.01f))
	{
		return;
	}


	ANIM_LOGIC("BlendAndApply::Combine");
	Start_Timer(BlendCombine);

	for (int i = 0; i < target_buffer->numberOfJoints; ++i)
	{
		if (new_buffer->IsPositionDirty(i))
		{
			
			if (!target_buffer->IsPositionDirty(i))
			{
				target_buffer->SetDirtyPosition(i);
				target_buffer->positionBuffer[i] = new_buffer->positionBuffer[i];
			}
			else
			{
				if (doTransitionBlend)
				{
					target_buffer->SetDirtyPosition(i);
					target_buffer->positionBuffer[i].Lerp(
						target_buffer->positionBuffer[i], 
						new_buffer->positionBuffer[i],
						percent);
				}
				else
				{
					target_buffer->SetDirtyPosition(i);
					target_buffer->positionBuffer[i] = new_buffer->positionBuffer[i];
				}
			}
			
		}

		if (new_buffer->IsVelocityDirty(i))
		{
			if (!target_buffer->IsVelocityDirty(i))
			{
				target_buffer->SetDirtyVelocity(i);
				target_buffer->velocityBuffer[i] = new_buffer->velocityBuffer[i];
			}
			else
			{
				target_buffer->SetDirtyVelocity(i);
				target_buffer->velocityBuffer[i].Lerp(
					target_buffer->velocityBuffer[i], 
					new_buffer->velocityBuffer[i],
					percent);
			}
		}

		if (new_buffer->IsAngularVelocityDirty(i))
		{
			if (!target_buffer->IsAngularVelocityDirty(i))
			{
				target_buffer->SetDirtyAngularVelocity(i);
				target_buffer->angularVelocityBuffer[i] = new_buffer->angularVelocityBuffer[i];
			}
			else
			{
				target_buffer->SetDirtyAngularVelocity(i);
				
				target_buffer->angularVelocityBuffer[i].Lerp(
					target_buffer->angularVelocityBuffer[i], 
					new_buffer->angularVelocityBuffer[i],
					percent);
			}
		}

		if (new_buffer->IsRotationDirty(i))
		{
			if (!target_buffer->IsRotationDirty(i))
			{
				target_buffer->SetDirtyRotation(i);
				target_buffer->rotationBuffer[i] = new_buffer->rotationBuffer[i];
				#if defined(MW4_ANIMATION_DEBUG)
				new_buffer->rotationBuffer[i].TestInstance();
				#endif
			}
			else
			{
				target_buffer->SetDirtyRotation(i);
				
				#if defined(MW4_ANIMATION_DEBUG)
				target_buffer->rotationBuffer[i].TestInstance();
				new_buffer->rotationBuffer[i].TestInstance();
				#endif
				
				Stuff::UnitQuaternion temp = target_buffer->rotationBuffer[i];
				target_buffer->rotationBuffer[i].FastLerp(
					temp, 
					new_buffer->rotationBuffer[i],
					percent);
				
				target_buffer->rotationBuffer[i].FastNormalize();

				#if defined(MW4_ANIMATION_DEBUG)
				target_buffer->rotationBuffer[i].TestInstance();
				#endif
			}
		}

		if (new_buffer->IsTriggerDirty(i))
		{
			if (!target_buffer->IsTriggerDirty(i))
			{
				target_buffer->SetDirtyTrigger(i);
				target_buffer->triggerBuffer[i] = new_buffer->triggerBuffer[i];
			}
			else
			{
				target_buffer->SetDirtyTrigger(i);
				target_buffer->triggerBuffer[i] |= new_buffer->triggerBuffer[i];
			}
		}
	}

	Stop_Timer(BlendCombine);

}