

#include "stdafx.h"

#ifdef STRICT							// Already defined by stdafx,
#undef STRICT							// so we avoid warning linking msg
#endif
#ifdef _MBCS							// The same as above
#undef _MBCS
#endif

#include "AnimationExport.h"



//undef Verify

#ifndef _DEBUG						//This is a hack To make it link in Release
#undef Spew
//void Spew() {}
#endif

#include <GameOS\GameOs.hpp>
#include <DLLPlatform\DLLPlatform.hpp>

//===========================================================================//

int	AnimExp::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppress_prompts,DWORD options)
{	
	int return_value;
	__try
	{
		return_value = DoExportWrap(name, ei, i, suppress_prompts);
		//return_value = true;
		
		GetSystemSetting(SYSSET_CLEAR_UNDO);
		Verify(!theHold.Holding());
	}
	__except(ProcessException(GetExceptionInformation()))
	{
	}

	return return_value;
}


//===========================================================================//

int	AnimExp::DoExportWrap(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppress_prompts)
{




		TurnOnExceptions();
		AFX_MANAGE_STATE(AfxGetStaticModuleState());
	#if 0
		DEBUG_STREAM << "------------SIZE ANALYSIS------------- " << endl;
		DEBUG_STREAM << "HEADER: " << sizeof(AnimHeader) << endl;
		DEBUG_STREAM << "NAMES: one byte per charachter + one" << endl;
		DEBUG_STREAM << "JOINT: " << sizeof (JointAnimData) << endl;
		DEBUG_STREAM << "CHANNEL: " << sizeof(ChannelAnimData) << endl;
		DEBUG_STREAM << "--------------KEY SIZES--------------- " << endl;
		DEBUG_STREAM << "ScalarLinearKey: " << sizeof(ScalarLinearKey) << endl;
		DEBUG_STREAM << "Point3DLinearKey: " << sizeof(Point3DLinearKey) << endl;
		DEBUG_STREAM << "RadianLinearKey: " << sizeof(RadianLinearKey) << endl;
		DEBUG_STREAM << "QuaternionLinearKey: " << sizeof(QuaternionLinearKey) << endl;
		DEBUG_STREAM << "ScalarTCBKey: " << sizeof(ScalarTCBKey) << endl;
		DEBUG_STREAM << "Point3DTCBKey: " << sizeof(Point3DTCBKey) << endl;
		DEBUG_STREAM << "RadianTCBKey: " << sizeof(RadianTCBKey) << endl;
		DEBUG_STREAM << "AngleAxisTCBKey: " << sizeof(AngleAxisTCBKey) << endl;
		DEBUG_STREAM << "ScalarBezKey: " << sizeof(ScalarBezKey) << endl;
		DEBUG_STREAM << "Point3DBezKey: " << sizeof(Point3DBezKey) << endl; 
		DEBUG_STREAM << "RadianBezKey: " << sizeof(RadianBezKey) << endl;
		DEBUG_STREAM << "QuaternionBezKey: " << sizeof(QuaternionBezKey) << endl;
		DEBUG_STREAM << "------------------END----------------- " << endl;
	#endif
		// Count the joints up
		int node_count = 0;

		
		// Load the prefs from the prefs plugin
		int counter = 0;
		
		JointEditDialog edit_dialog(i->GetRootNode(), i);
		edit_dialog.AddJointPreference(i->GetRootNode());


		edit_dialog.CountNodes(i->GetRootNode(), node_count);

	
		if (!suppress_prompts)
		{
			if (!(edit_dialog.DoModal() == IDOK))
			{
				TurnOffExceptions();
				return false;
			}
		}

		int joint_count = 0;
		counter = 0;
		CountJoint(i->GetRootNode(), joint_count, counter, &edit_dialog);


		// Handle 0 joints
		if (joint_count == 0)
		{
			// just let it go...
			TurnOffExceptions();
			return true;
		}

		// -----------------------------------------------------------------------
		// Allocate all the space
		// -----------------------------------------------------------------------
		

		MW4Animation::AnimData anim_data;

		// Allocate the Header
		anim_data.animHeaderBlock = new MW4Animation::AnimHeader;
		Register_Pointer(anim_data.animHeaderBlock);
		anim_data.animHeaderBlockSize = sizeof(MW4Animation::AnimHeader);


		// Allocate the joint data
		anim_data.animJointBlock = new MW4Animation::JointAnimData[joint_count];
		Register_Pointer(anim_data.animJointBlock);
		anim_data.animJointBlockSize = sizeof(MW4Animation::JointAnimData) * joint_count;


		// Calculate the name size.
		size_t name_block_size = 0;
		counter = 0;
		CalcNameSize(i->GetRootNode(), name_block_size, counter, &edit_dialog);

		// Allocate the name block
		anim_data.animNameBlock = new char[name_block_size];
		Register_Pointer(anim_data.animNameBlock);
		anim_data.animNameBlockSize = sizeof(char) * name_block_size;


		// count the needed amount of channels and allocate the channel block
		counter = 0;
		int channel_count = 0;
		CalcChannelCount(i->GetRootNode(), channel_count, counter, &edit_dialog);
		anim_data.animChannelBlock = new MW4Animation::ChannelAnimData[channel_count];
		Register_Pointer(anim_data.animChannelBlock);
		anim_data.animChannelBlockSize = sizeof(MW4Animation::ChannelAnimData) * channel_count;


		// Get the joint stream data
		// and put it in a special array for later.

		BYTE **key_data;
		key_data = new BYTE*[channel_count];
		Register_Pointer(key_data);

		BYTE **key_time_data;
		key_time_data = new BYTE*[channel_count];
		Register_Pointer(key_time_data);

		size_t *size_of_data = new size_t[channel_count];
		Register_Pointer(size_of_data);

		size_t *size_of_time_data = new size_t[channel_count];
		Register_Pointer(size_of_time_data);


		for (int j = 0; j < channel_count; ++j)
		{
			size_of_data[j] = 0;
			key_data[j] = NULL;

			size_of_time_data[j] = 0;
			key_time_data[j] = NULL;
		}


		// -----------------------------------------------------------------------
		// Enter header info...
		// -----------------------------------------------------------------------
		
		anim_data.animHeaderBlock->majorVersion = MAJOR_ANIM_VERSION_2_1;
		anim_data.animHeaderBlock->minorVersion = MINOR_ANIM_VERSION_2_1;

		Stuff::MString string_name = name;
		string_name.StripDirectory().StripExtension();

		Str_Copy(anim_data.animHeaderBlock->animationName, 
			(const char *)string_name, 50);


		Interval total_time = i->GetAnimRange();
		anim_data.animHeaderBlock->startTime = TicksToSec(total_time.Start());
		anim_data.animHeaderBlock->endTime = TicksToSec(total_time.End() );
		
		// this has been replaced with AnimData->GetJointCount()
		//anim_data.animHeaderBlock->jointCount = joint_count;

		// -----------------------------------------------------------------------
		//  Enter the Joint, Name, Channel and Key info
		// -----------------------------------------------------------------------
		
		// remember name size and let joinstream recount size
		size_t name_size = anim_data.animNameBlockSize;
		anim_data.animNameBlockSize = 0;

		counter = 0;
		int current_channel = 0;
		int current_joint = 0;
		
		// make a progress bar...

		if (!suppress_prompts)
		{
			LPVOID arg = NULL;
			i->ProgressStart(_T("Exporting, Converting & Optimizing"), TRUE, fn, arg);
		}

		//theHold.SuperBegin();
		MakeJointStream(i, i->GetRootNode(), key_data, size_of_data, key_time_data, size_of_time_data, counter, anim_data, current_channel,current_joint, &edit_dialog, suppress_prompts);
		//theHold.Accept("Export MW4");
		

		if (!suppress_prompts)
		{
			i->ProgressEnd();
		}

		Verify(anim_data.animNameBlockSize <= name_size);


		
		// -----------------------------------------------------------------------
		// Copy the key info data
		// Make a block big enough for keyframes
		// Copy keyframes into block
		// -----------------------------------------------------------------------
		
		size_t total_key_size = 0;
		size_t total_key_time_size = 0;

		for (int l = 0; l < channel_count; ++l)
		{
			total_key_size += size_of_data[l];
			total_key_time_size += size_of_time_data[l];
		}

		anim_data.animKeyBlock = new BYTE[total_key_size];
		Register_Pointer(anim_data.animKeyBlock);
		anim_data.animKeyBlockSize = total_key_size;

		Stuff::MemoryStream key_stream(anim_data.animKeyBlock, anim_data.animKeyBlockSize);


		anim_data.animKeyTimeBlock = new BYTE[total_key_time_size];
		Register_Pointer(anim_data.animKeyTimeBlock);
		anim_data.animKeyTimeBlockSize = total_key_time_size;

		Stuff::MemoryStream key_time_stream(anim_data.animKeyTimeBlock, anim_data.animKeyTimeBlockSize);


		for (int k = 0; k < channel_count; ++k)
		{
			anim_data.animChannelBlock[k].offsetToKeyData = 
				(int)key_stream.GetPointer() - (int)anim_data.animKeyBlock;

			Verify(anim_data.animChannelBlock[k].offsetToKeyData >= 0);

			if(size_of_data[k] != 0)
			{
				key_stream.WriteBytes(key_data[k], size_of_data[k]);
			}

			
			anim_data.animChannelBlock[k].offsetToKeyTimeData = 
				(int)key_time_stream.GetPointer() - (int)anim_data.animKeyTimeBlock;

			Verify(anim_data.animChannelBlock[k].offsetToKeyTimeData >= 0);

			if(size_of_time_data[k] != 0)
			{
				key_time_stream.WriteBytes(key_time_data[k], size_of_time_data[k]);
			}
		}


		// -----------------------------------------------------------------------
		//  POST OPTIMIZE!
		// This is here just because it is a hell of a lot easier to do it this way
		// if a channel gets sampled down to have NO keyframes it drops out
		// if a joint drops all of its channels it also drops out
		// write now, no keyframes exist for null channels, 
		// all that remains is to remove the channels and decrement the count.
		// also if a joint totally got optimized out remove it.
		// It might be easier to do this as we detect null joints!
		// Maybe just remove channels but not the joints, since the joints
		// should be marked as not animated to begin with... hmmmm.
		// -----------------------------------------------------------------------
		

		//SPEW(("jerryeds", "%d %d", channel_count, anim_data.GetChannelCount()));

		Optimize(anim_data);

		// -----------------------------------------------------------------------
		// Finish off the header info
		// -----------------------------------------------------------------------
		
		int pointer = 0;
		anim_data.animHeaderBlock->offsetToHeaderBlock = pointer;
		pointer += anim_data.animHeaderBlockSize;

		anim_data.animHeaderBlock->offsetToJointBlock = pointer;
		pointer+= anim_data.animJointBlockSize;
		
		anim_data.animHeaderBlock->offsetToNameBlock = pointer;
		pointer+= anim_data.animNameBlockSize;
	
		anim_data.animHeaderBlock->offsetToKeyBlock = pointer;
		pointer+= anim_data.animKeyBlockSize;

		anim_data.animHeaderBlock->offsetToKeyTimeBlock = pointer;
		pointer+= anim_data.animKeyTimeBlockSize;

		anim_data.animHeaderBlock->offsetToChannelBlock = pointer;
		pointer+= anim_data.animChannelBlockSize;
		
		anim_data.animHeaderBlock->totalSizeOfAnimation = 
			anim_data.animHeaderBlockSize +
			anim_data.animJointBlockSize +
			anim_data.animNameBlockSize +
			anim_data.animKeyTimeBlockSize +
			anim_data.animKeyBlockSize +
			anim_data.animChannelBlockSize;

		// -----------------------------------------------------------------------
		// Write the data out
		// -----------------------------------------------------------------------
		
		anim_data.Save(name);

		// -----------------------------------------------------------------------
		//  Close the damn thing down
		// -----------------------------------------------------------------------
		


		for (int m = 0; m < channel_count; ++m)
		{
			if (size_of_data[m] > 0)
			{
				Unregister_Pointer(key_data[m]);
				delete[] key_data[m];
			}

			if (size_of_time_data[m] > 0)
			{
				Unregister_Pointer(key_time_data[m]);
				delete[] key_time_data[m];
			}
		}


		Unregister_Pointer(size_of_data);
		delete[] size_of_data;
		Unregister_Pointer(key_data);
		delete[] key_data;
		
		Unregister_Pointer(size_of_time_data);
		delete[] size_of_time_data;
		Unregister_Pointer(key_time_data);
		delete[] key_time_data;

		// this is bad since anim_data get deleted after the funcion goes out of scope.
		// at that time it unregisters deleted memory.
		// fix this to be an allocated anim_data.. *ack*
		
		Unregister_Pointer(anim_data.animKeyBlock);
		Unregister_Pointer(anim_data.animKeyTimeBlock);
		Unregister_Pointer(anim_data.animChannelBlock);
		Unregister_Pointer(anim_data.animNameBlock);
		Unregister_Pointer(anim_data.animJointBlock);
		Unregister_Pointer(anim_data.animHeaderBlock);

		delete[] anim_data.animKeyBlock;
		delete[] anim_data.animKeyTimeBlock;
		delete[] anim_data.animChannelBlock;
		delete[] anim_data.animNameBlock;
		delete[] anim_data.animJointBlock;
		delete anim_data.animHeaderBlock;

		anim_data.animKeyBlock = NULL;
		anim_data.animKeyTimeBlock = NULL;
		anim_data.animChannelBlock = NULL;
		anim_data.animNameBlock = NULL;
		anim_data.animJointBlock = NULL;
		anim_data.animHeaderBlock = NULL;

		TurnOffExceptions();
		return true;

	
}


//===========================================================================//

void AnimExp::Optimize(MW4Animation::AnimData &anim_data)
{
	// first we remove unwanted channels...
	int new_channel_count = 0;

	for (int i = 0; i < anim_data.GetChannelCount(); ++i)
	{
		if (anim_data.animChannelBlock[i].keyframeCount == 0)
		{
			// remove channel refs
			--anim_data.animJointBlock[anim_data.animChannelBlock[i].jointNumber].channelsUsed;
			Verify(anim_data.animJointBlock[anim_data.animChannelBlock[i].jointNumber].channelsUsed >= 0);
		}
		else
		{
			++new_channel_count;
		}
	}

	int current_channel = 0;
	// remove the gaps in the channels

	for (i = 0; i < anim_data.GetJointCount(); ++i)
	{	
		anim_data.animJointBlock[i].firstChannel = current_channel;
		current_channel += anim_data.animJointBlock[i].channelsUsed;
	}

	//make a new array for the used channels...
	// and remove all the non used channels
	MW4Animation::ChannelAnimData *new_block = new MW4Animation::ChannelAnimData[new_channel_count];
	Register_Pointer(new_block);
	size_t new_block_size = sizeof(MW4Animation::ChannelAnimData) * new_channel_count;

	current_channel = 0;
	for (i = 0; i < anim_data.GetChannelCount(); ++i)
	{
		if (anim_data.animChannelBlock[i].keyframeCount > 0)
		{
			// copy channel
			new_block[current_channel] = anim_data.animChannelBlock[i];
			++current_channel;
		}
	}

	Unregister_Pointer(anim_data.animChannelBlock);
	delete[] anim_data.animChannelBlock;

	anim_data.animChannelBlock = new_block;
	anim_data.animChannelBlockSize = new_block_size;

	// Channels are done now cull out the joints....

	// count the joints.
	int new_joint_count = 0;
	for (i = 0; i < anim_data.GetJointCount(); ++i)
	{	
		if (anim_data.animJointBlock[i].channelsUsed > 0)
		{
			++new_joint_count;
		}
	}

	// copy the joints... all the while adjusting the channels....
	MW4Animation::JointAnimData *new_joint_block = new MW4Animation::JointAnimData[new_joint_count];
	Register_Pointer(new_joint_block);
	int new_joint_size = sizeof(MW4Animation::JointAnimData) * new_joint_count;

	int current_joint = 0;
	for (i = 0; i < anim_data.GetJointCount(); ++i)
	{	
		if (anim_data.animJointBlock[i].channelsUsed > 0)
		{
			// copy the joint
			new_joint_block[current_joint] = anim_data.animJointBlock[i];
			++current_joint;
		}
	}

	Unregister_Pointer(anim_data.animJointBlock);
	delete[] anim_data.animJointBlock;

	anim_data.animJointBlock = new_joint_block;
	anim_data.animJointBlockSize = new_joint_size;

	
	// now adjust all the channels...
	for (i = 0; i < anim_data.GetJointCount(); ++i)
	{	
		for (
			int chan = anim_data.animJointBlock[i].firstChannel; 
			chan < anim_data.animJointBlock[i].channelsUsed + anim_data.animJointBlock[i].firstChannel;
			++chan
		)
		{
			anim_data.animChannelBlock[chan].jointNumber = i;
		}
	}

 
}

//===========================================================================//

void AnimExp::CountJoint(INode* node, int& joint_count, int& counter, JointEditDialog *dialog)
{
	
	JointPreference *pref = JointEditDialog::GetJointPrefrence(node);
	if(pref != NULL)
	{
		if (pref->m_excludeJoint == JointPreference::IncludeJoint)
		{
			// if not skiped
			++joint_count;
		}
		else if (pref->m_excludeJoint == JointPreference::AutoJoint)
		{
			if(dialog->NodeAnimated(node))
			{
				++joint_count;
			}
		}

	}
	// this is an absolute counter to index into the joints prefs with.
	++counter;
	// For each child of this node, we recurse into ourselves 
	// and increment the counter until no more children are found.
	for (int c = 0; c < node->NumberOfChildren(); c++) 
	{
		CountJoint(node->GetChildNode(c), joint_count, counter, dialog);
	}
}

//===========================================================================//

void AnimExp::CalcNameSize(INode* node, size_t& name_array_size, int& counter, JointEditDialog *dialog)
{

	JointPreference *pref = JointEditDialog::GetJointPrefrence(node);
	if(pref != NULL)
	{
		if (pref->m_excludeJoint == JointPreference::IncludeJoint)
		{
			// if not skiped
				name_array_size += strlen(node->GetName()) + 1;
		}
		else if (pref->m_excludeJoint == JointPreference::AutoJoint)
		{
			if(dialog->NodeAnimated(node))
			{
				name_array_size += strlen(node->GetName()) + 1;
			}
		}
	}

	// this is an absolute counter to index into the joints prefs with.
	++counter;	
	// For each child of this node, we recurse into ourselves 
	// and increment the counter until no more children are found.
	for (int c = 0; c < node->NumberOfChildren(); c++) 
	{
		CalcNameSize(node->GetChildNode(c), name_array_size, counter, dialog);
	}
}

//===========================================================================//

void AnimExp::CalcChannelCount(INode* node, int& channel_count, int& counter, JointEditDialog *dialog)
{
	
	JointPreference *pref = JointEditDialog::GetJointPrefrence(node);
	if(pref != NULL)
	{
		if (pref->m_excludeJoint == JointPreference::IncludeJoint)
		{
			// if not skiped
			channel_count += CalcControlChannels( counter, node);
		}
		else if (pref->m_excludeJoint == JointPreference::AutoJoint)
		{


			if(dialog->NodeAnimated(node))
			{
				channel_count += CalcControlChannels(counter, node);
			}
		}
	}


	// this is an absolute counter to index into the joints prefs with.
	++counter;	
	// For each child of this node, we recurse into ourselves 
	// and increment the counter until no more children are found.
	for (int c = 0; c < node->NumberOfChildren(); c++) 
	{
		CalcChannelCount(node->GetChildNode(c), channel_count, counter, dialog);
	}
}

//===========================================================================//

int AnimExp::CalcControlChannels(int counter, INode* node)
{
	// Calculate how many channels I use.
	
	// for each known type add a channel, if it has a special channel, add that too ( velocity )
	// don't forget to add sub channels for RotationXYZ, and PositionXYZ

	int channel_count = 0;
	JointPreference *prefs = JointEditDialog::GetJointPrefrence(node);
	Verify(prefs != NULL);

	if(!prefs->m_convertRotationJoint)
	{
		// check for XYZ controller here
		
		//if (node->GetTMController()->GetRotationController()->IsAnimated())
		{
			channel_count += 1;
		}
	}
	else
	{
		
		// we are going to convert it so it has 2 channels
		// when we add XYZ controlers we may let this convert
		// to those and then this will be a little diffrent
		// DON'T FORGET!!!!
		channel_count += 1;
		
	}
	
	if(!prefs->m_convertTranslationJoint)
	{
		if (!_stricmp(node->GetName(), "joint_root"))
		{
			channel_count += 1;
		}
		else
		{
			// check for XYZ controller here
			if (node->GetTMController()->GetPositionController()->IsAnimated())
			{
				channel_count += 1;
			}
		}
	}
	else
	{
		// we are going to convert it so it has 2 channels
		// when we add XYZ controlers we may let this convert
		// to those and then this will be a little diffrent
		// DON'T FORGET!!!!
		
		channel_count += 1;
		
	}

	ObjectState os;
	SClass_ID SCID;
	Class_ID CID;

	os = node->EvalWorldState(0);
	
	if (os.obj)
	{
		SCID = os.obj->SuperClassID();
		CID = os.obj->ClassID();
		if (SCID == HELPER_CLASS_ID && CID == SITEOBJECT_CLASS_ID)
		{
			// If there is any animation at all on this guy then add a channel.
			if (node->IsAnimated())
			{
				channel_count += 1;
			}
		}
	}

	//SPEW(("jerryeds", "%s %d", node->GetName(),channel_count));

	return channel_count;
}

//===========================================================================//

void AnimExp::MakeJointStream(
		Interface *ip,
		INode* node, 
		BYTE **key_data,
		size_t *size_of_data, 
		BYTE **key_time_data,
		size_t *size_of_time_data, 
		int& counter,
		MW4Animation::AnimData &anim_data,
		int& current_channel,
		int& current_joint,
		JointEditDialog *dialog,
		BOOL suppress_prompts
	)
{

	bool include_joint = false;

	JointPreference *prefs = JointEditDialog::GetJointPrefrence(node);
	if(prefs != NULL)
	{
		if (prefs->m_excludeJoint == JointPreference::IncludeJoint)
		{
			include_joint = true;
		}
		else if (prefs->m_excludeJoint == JointPreference::AutoJoint)
		{
			if(dialog->NodeAnimated(node))
			{
				include_joint = true;
			}
		}
	}


	if (include_joint)
	{
		
		// write down the name...
		char *name_pointer;
		name_pointer = anim_data.animNameBlock + anim_data.animNameBlockSize;


		anim_data.animJointBlock[current_joint].offsetToName = 
			name_pointer - anim_data.animNameBlock;

		Stuff::MString compressed_name;

		MW4Animation::CompressName(node->GetName(), compressed_name);

		strcpy(name_pointer, (const char *)compressed_name);
		anim_data.animNameBlockSize += strlen((const char *)compressed_name) + 1;


		// Write pointers to the channel data

		Control *tm_controler = node->GetTMController();

		// Make the control count indexes...
		anim_data.animJointBlock[current_joint].firstChannel = current_channel;


		anim_data.animJointBlock[current_joint].channelsUsed =
				CalcControlChannels(counter, node);
	
		current_channel += anim_data.animJointBlock[current_joint].channelsUsed;


		Control *translation_controler = tm_controler->GetPositionController();
		Control *rotation_controler = tm_controler->GetRotationController();
		bool rot_cloned = false;
		bool trans_cloned = false;

		// sample the joint data

		bool cull_optimization = false;

		if (prefs->m_convertRotationJoint)
		{

			theHold.Begin();
			// replace controler pointer with new controller

			// if the type is known but the sub type isn't 
			// then convert that controller.
			
			// Example is RotationXYZ with a sub controler of SineWave or Noise

			switch(prefs->m_rotationConvertType)
			{
				case LinearKeyframeType:
					rotation_controler = (Control *)ip->CreateInstance(
						CTRL_ROTATION_CLASS_ID,
						Class_ID(LININTERP_ROTATION_CLASS_ID, 0)
				);
				break;

			case BezierKeyframeType:
					rotation_controler = (Control *)ip->CreateInstance(
						CTRL_ROTATION_CLASS_ID,
						Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)
					);
				break;

			case TCBKeyframeType:
					rotation_controler = (Control *)ip->CreateInstance(
						CTRL_ROTATION_CLASS_ID,
						Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0)
					);
				break;
			}
			
			SuspendAnimate();
			AnimateOn();

			{
				
				Interval iv;
				int ticks_per_frame = GetTicksPerFrame();
				int num_of_frames = 1;

				TimeValue start = ip->GetAnimRange().Start();
				TimeValue end = ip->GetAnimRange().End();
				int delta = GetTicksPerFrame();

				for (int time_of_key=start; time_of_key<=end; time_of_key+=delta)
				{
					
					Quat rot;
					Point3 pos;
					
					// Get the pos and rot from the frame.

					// Compute the relative TM
					Matrix3 parentTM, nodeTM, localTM;
					nodeTM = node->GetNodeTM(time_of_key);
					parentTM = node->GetParentTM(time_of_key);
					localTM = nodeTM*Inverse(parentTM);

					AffineParts ap;
					decomp_affine(localTM, &ap);
					pos = ap.t;
					rot = ap.q;

					rotation_controler->SetValue(time_of_key,&rot,TRUE,CTRL_ABSOLUTE);
				}
			}

			ResumeAnimate();
			theHold.Accept(0);
		}
		else
		{

			if (!rotation_controler->IsAnimated())
			{
				//put in a start and stop keyframe...
				SuspendAnimate();
				AnimateOn();

					Matrix3 parentTM, nodeTM, localTM;
					nodeTM = node->GetNodeTM(ip->GetAnimRange().Start());
					parentTM = node->GetParentTM(ip->GetAnimRange().Start());
					localTM = nodeTM*Inverse(parentTM);

					AffineParts ap;
					decomp_affine(localTM, &ap);
					
					Quat rot;
					Point3 pos;
					pos = ap.t;
					rot = ap.q;

					rotation_controler->SetValue(ip->GetAnimRange().Start(),&rot,TRUE,CTRL_ABSOLUTE);


					nodeTM = node->GetNodeTM(ip->GetAnimRange().End());
					parentTM = node->GetParentTM(ip->GetAnimRange().End());
					localTM = nodeTM*Inverse(parentTM);

					decomp_affine(localTM, &ap);
					
					pos = ap.t;
					rot = ap.q;

					
					if (!prefs->m_decodeWithVelocity)
					{
						cull_optimization = true;
						rotation_controler->SetValue(ip->GetAnimRange().End(),&rot,TRUE,CTRL_ABSOLUTE);
					}

				ResumeAnimate();
				theHold.Accept(0);



			}


		}


		if (prefs->m_convertTranslationJoint)
		{
			theHold.Begin();
			// replace controler pointer with new controller

			// if the type is known but the sub type isn't 
			// then convert that controller.
			
			// Example is PositionXYZ with a sub controler of SineWave or Noise
			switch(prefs->m_translationConvertType)
			{
				case LinearKeyframeType:
					translation_controler = (Control *)ip->CreateInstance(
					CTRL_POSITION_CLASS_ID,
					Class_ID(LININTERP_POSITION_CLASS_ID, 0)
				);
				break;

			case BezierKeyframeType:
					translation_controler = (Control *)ip->CreateInstance(
						CTRL_POSITION_CLASS_ID,
						Class_ID(HYBRIDINTERP_POSITION_CLASS_ID, 0)
					);
				break;

			case TCBKeyframeType:
					translation_controler = (Control *)ip->CreateInstance(
						CTRL_POSITION_CLASS_ID,
						Class_ID(TCBINTERP_POSITION_CLASS_ID, 0)
					);
				break;
			}
			
			SuspendAnimate();
			AnimateOn();

			{
				
				Interval iv;
				int ticks_per_frame = GetTicksPerFrame();
				int num_of_frames = 1;

				TimeValue start = ip->GetAnimRange().Start();
				TimeValue end = ip->GetAnimRange().End();
				int delta = GetTicksPerFrame();

				for (int time_of_key=start; time_of_key<=end; time_of_key+=delta)
				{
					
					Quat rot;
					Point3 pos;
					
					// Get the pos and rot from the frame.

					// Compute the relative TM
					Matrix3 parentTM, nodeTM, localTM;
					nodeTM = node->GetNodeTM(time_of_key);
					parentTM = node->GetParentTM(time_of_key);
					localTM = nodeTM*Inverse(parentTM);

					AffineParts ap;
					decomp_affine(localTM, &ap);
					pos = ap.t;
					rot = ap.q;

					translation_controler->SetValue(time_of_key,&pos,TRUE,CTRL_ABSOLUTE);
				}
			}

			ResumeAnimate();
			theHold.Accept(0);
		}
		else
		{
			// HACK HACK HACK
			// If this is the joint that has translation 
			// we put keyframes on it so that it will have 
			// animation even if it doesn't. 
			// this only happens at the root for blended mechs... 
			// ACKACKACKACK

			if (!_stricmp(node->GetName(), "joint_root"))
			{
				if (!translation_controler->IsAnimated())
				{
					//put in a start and stop keyframe...
					SuspendAnimate();
					AnimateOn();

						Matrix3 parentTM, nodeTM, localTM;
						nodeTM = node->GetNodeTM(ip->GetAnimRange().Start());
						parentTM = node->GetParentTM(ip->GetAnimRange().Start());
						localTM = nodeTM*Inverse(parentTM);

						AffineParts ap;
						decomp_affine(localTM, &ap);
						
						Quat rot;
						Point3 pos;
						pos = ap.t;
						rot = ap.q;

						translation_controler->SetValue(ip->GetAnimRange().Start(),&pos,TRUE,CTRL_ABSOLUTE);


						nodeTM = node->GetNodeTM(ip->GetAnimRange().End());
						parentTM = node->GetParentTM(ip->GetAnimRange().End());
						localTM = nodeTM*Inverse(parentTM);

						decomp_affine(localTM, &ap);
						
						pos = ap.t;
						rot = ap.q;

						translation_controler->SetValue(ip->GetAnimRange().End(),&pos,TRUE,CTRL_ABSOLUTE);


					ResumeAnimate();
					theHold.Accept(0);
				}

			}
			else
			{
				// cull out non animated joints
				if (!translation_controler->IsAnimated())
				{
					translation_controler = NULL;
				}
			}
		}


		// optimize rotation
		// and optimize translation
		// all channels that get optimized out
		// will be removed later, unlike channels
		// that start unanimated which got removed
		// just above!
		if (prefs->m_optimizeRotation && rotation_controler != NULL && !cull_optimization)
		{
			// if not replaced, replace controler pointer with new controller
			// Optimize the rotation...
			// if the type has sub controllers then optimize those instead!


			int num_keys = rotation_controler->NumKeys();

			if (num_keys != 0)
			{
				// Make a new copy 
				Control *rotation_controler_clone = (Control *)rotation_controler->Clone();
				rot_cloned = true;
				


				TimeValue start = rotation_controler_clone->GetKeyTime(0);
				TimeValue end = rotation_controler_clone->GetKeyTime(num_keys-1);

				if (prefs->m_rotationRange)
				{
					start = (TimeValue)prefs->m_rotationSampleStartFrame * GetTicksPerFrame();
					end = (TimeValue)prefs->m_rotationSampleEndFrame * GetTicksPerFrame();
				}

				Interval range;
				start = (rotation_controler_clone->GetKeyTime(0) > start)   ? rotation_controler_clone->GetKeyTime(0):start;
				end = (rotation_controler_clone->GetKeyTime(num_keys-1) < end) ? rotation_controler_clone->GetKeyTime(num_keys-1):end;
				
				// adjust the optimization to not include the last frame... tehehehehehe
				
				if ((end - GetTicksPerFrame()) > start)
				{
					end -= GetTicksPerFrame();
				}
			

				range.Set(start , end);
				
				MyKeyReduceStatus rot_progress;
				
				//theHold.Release();
				ApplyKeyReduction_MW4(
					rotation_controler_clone, 
					range, 
					prefs->m_rotationThreshold * Stuff::Radians_Per_Degree, 
					GetTicksPerFrame(), 
					&rot_progress
				);



				rotation_controler = rotation_controler_clone;
				//tack on the first frame
				SuspendAnimate();
				AnimateOn();

					Matrix3 parentTM, nodeTM, localTM;
					nodeTM = node->GetNodeTM(ip->GetAnimRange().Start());
					parentTM = node->GetParentTM(ip->GetAnimRange().Start());
					localTM = nodeTM*Inverse(parentTM);

					AffineParts ap;
					decomp_affine(localTM, &ap);
					
					Quat rot;
					Point3 pos;
					pos = ap.t;
					rot = ap.q;

					rotation_controler->SetValue(ip->GetAnimRange().Start(),&rot,TRUE,CTRL_ABSOLUTE);

				ResumeAnimate();
				theHold.Accept(0);



				if (!prefs->m_decodeWithVelocity)
				{
					if (rotation_controler->NumKeys() == 2)
					{
						bool delete_last_key = true;

						Interval iv = ip->GetAnimRange();
			
						Matrix3 firstTM;
						Matrix3 lastTM;

						firstTM = node->GetNodeTM(ip->GetAnimRange().Start());
						lastTM = node->GetNodeTM(ip->GetAnimRange().End());
						

						Matrix3 parentTM, nodeTM, firstLocalTM, lastLocalTM;
						
						parentTM = node->GetParentTM(ip->GetAnimRange().Start());
						firstLocalTM = firstTM*Inverse(parentTM);
						
						parentTM = node->GetParentTM(ip->GetAnimRange().End());
						lastLocalTM = lastTM*Inverse(parentTM);

						AffineParts ap;
						decomp_affine(firstLocalTM, &ap);
						
						Quat q1;
						q1 = ap.q;
						
						decomp_affine(lastLocalTM, &ap);

						Quat q2;
						q2 = ap.q;


						if (!Stuff::Close_Enough(q1.x, q2.x, 0.05f)||!Stuff::Close_Enough(q1.y, q2.y,0.05f)||!Stuff::Close_Enough(q1.z, q2.z, 0.05f)||!Stuff::Close_Enough(q1.w, q2.w, 0.05f))
						{
							delete_last_key = false;
						}

						
						if (delete_last_key)
						{
							IKeyControl *ikeys = GetKeyControlInterface(rotation_controler);
							ikeys->SetNumKeys(1);
						}
					}

				}
			}



		}
		if (prefs->m_optimizeTranslation && translation_controler != NULL)
		{	
			// if not replaced, replace controler pointer with new controller
			// optimize the translation
			// if the type has sub controllers then optimize those instead!
			int num_keys = translation_controler->NumKeys();

			if (num_keys != 0)
			{
				// Make a new copy 
				Control *translation_controler_clone = (Control *)translation_controler->Clone();
				trans_cloned = true;

				TimeValue start = translation_controler_clone->GetKeyTime(0);
				TimeValue end = translation_controler_clone->GetKeyTime(num_keys-1);

				if (prefs->m_translationRange)
				{
					start = (TimeValue)prefs->m_translationSampleStartFrame * GetTicksPerFrame();
					end = (TimeValue)prefs->m_translationSampleEndFrame * GetTicksPerFrame();
				}


				
				Interval range;
				start = (translation_controler_clone->GetKeyTime(0) > start)   ? translation_controler_clone->GetKeyTime(0):start;
				end = (translation_controler_clone->GetKeyTime(num_keys-1) < end) ? translation_controler_clone->GetKeyTime(num_keys-1):end;
				// adjust the optimization to not include the last frame... tehehehehehe
				if ((end - GetTicksPerFrame()) > start)
				{
					end -= GetTicksPerFrame();
				}
				range.Set(start , end);
						
				MyKeyReduceStatus rot_progress;
				
				//theHold.Release();
				ApplyKeyReduction_MW4(
					translation_controler_clone, 
					range, 
					prefs->m_translationThreshold, 
					GetTicksPerFrame(), 
					&rot_progress
				);

				
				if (!_stricmp(node->GetName(), "joint_root"))
				{
					translation_controler = translation_controler_clone;
					//tack on the first frame
					SuspendAnimate();
					AnimateOn();

						Matrix3 parentTM, nodeTM, localTM;
						nodeTM = node->GetNodeTM(ip->GetAnimRange().Start());
						parentTM = node->GetParentTM(ip->GetAnimRange().Start());
						localTM = nodeTM*Inverse(parentTM);

						AffineParts ap;
						decomp_affine(localTM, &ap);
						
						Quat rot;
						Point3 pos;
						pos = ap.t;
						rot = ap.q;

						translation_controler->SetValue(ip->GetAnimRange().Start(),&pos,TRUE,CTRL_ABSOLUTE);

					ResumeAnimate();
					theHold.Accept(0);
				}
				else
				{
					// if this gacks we know we have an empty animation..
					// we are here to optimize and there is no beter optimization
					Control *test_controler = (Control *)translation_controler->Clone();
					start = test_controler->GetKeyTime(0);
					end = test_controler->GetKeyTime(test_controler->NumKeys()-1);

					range.Set(start , end);
					ApplyKeyReduction_MW4(
						test_controler, 
						range, 
						prefs->m_translationThreshold, 
						GetTicksPerFrame(), 
						&rot_progress
					);

					if (test_controler->NumKeys() < 1)
					{
						if (trans_cloned == true)
						{
							translation_controler->DeleteMe();
						}
						translation_controler = test_controler;
					}
					else
					{
						test_controler->DeleteMe();
						translation_controler = translation_controler_clone;
					}
				}

			}


		}


		// if not XYZ controller
		//translation

		int local_channel_counter =	anim_data.animJointBlock[current_joint].firstChannel;


		if (translation_controler != NULL)
		{
	
			if (prefs->m_decodeWithVelocity)
			{
				anim_data.animChannelBlock[local_channel_counter].channelType = 
					MW4Animation::ChannelAnimData::VelocityChannelType;

				MakeChannelVelocityStream(
					translation_controler,
					key_data,
					size_of_data, 
					key_time_data,
					size_of_time_data,
					anim_data, 
					local_channel_counter, 
					current_joint);
			}
			else
			{
				anim_data.animChannelBlock[local_channel_counter].channelType = 
					MW4Animation::ChannelAnimData::PositionChannelType;

				MakeChannelStream(
					translation_controler,
					key_data,
					size_of_data, 
					key_time_data,
					size_of_time_data,
					anim_data, 
					local_channel_counter, 
					current_joint);
			}

			
			if (trans_cloned == true)
			{
				translation_controler->DeleteMe();
			}

			
			local_channel_counter++;
		}

		if (rotation_controler != NULL)
		{
			// if not XYZ controller
			// rotation
			if (prefs->m_decodeWithVelocity)
			{
				anim_data.animChannelBlock[local_channel_counter].channelType = 
					MW4Animation::ChannelAnimData::AngularVelocityChannelType;

				MakeChannelAngularVelocityStream(
					rotation_controler,
					key_data,
					size_of_data,
					key_time_data,
					size_of_time_data,
					anim_data, 
					local_channel_counter, 
					current_joint);
			}
			else
			{
				anim_data.animChannelBlock[local_channel_counter].channelType = 
					MW4Animation::ChannelAnimData::RotationChannelType;

				MakeChannelStream(
					rotation_controler,
					key_data,
					size_of_data, 
					key_time_data,
					size_of_time_data,
					anim_data, 
					local_channel_counter, 
					current_joint);
			}

			
			if (rot_cloned == true)
			{
				rotation_controler->DeleteMe();
			}

			local_channel_counter++;
		}

		// add special channels here
		// 
			ObjectState os;
		SClass_ID SCID;
		Class_ID CID;

		os = node->EvalWorldState(0);
		
		if (os.obj)
		{
			SCID = os.obj->SuperClassID();
			CID = os.obj->ClassID();
			if (SCID == HELPER_CLASS_ID && CID == SITEOBJECT_CLASS_ID)
			{
				// If there is any animation at all on this guy then add a channel.
				if (node->IsAnimated())
				{

					IParamBlock *pb = (IParamBlock*)os.obj->GetParamBlock(); 
				 
					TimeValue start = ip->GetAnimRange().Start();
					TimeValue end = ip->GetAnimRange().End();
					int delta = GetTicksPerFrame();

					int total_frame_count = (end - start) / delta;
					total_frame_count += 2;
					int current_frame = 0;

					TriggerTempStorage *trigger_stream = new TriggerTempStorage[total_frame_count];
					
					//SPEW((0,"++++++++++++++"));

					for (int time_of_key=start; time_of_key<=end; time_of_key+=delta)
					{
						// Get all 16 of the parameters, then combine them into an int...
						int trigger[16];
						pb->GetValue(PB_TRIGGER_0,time_of_key,trigger[0], FOREVER);
						pb->GetValue(PB_TRIGGER_1,time_of_key,trigger[1], FOREVER);
						pb->GetValue(PB_TRIGGER_2,time_of_key,trigger[2], FOREVER);
						pb->GetValue(PB_TRIGGER_3,time_of_key,trigger[3], FOREVER);
						pb->GetValue(PB_TRIGGER_4,time_of_key,trigger[4], FOREVER);
						pb->GetValue(PB_TRIGGER_5,time_of_key,trigger[5], FOREVER);
						pb->GetValue(PB_TRIGGER_6,time_of_key,trigger[6], FOREVER);
						pb->GetValue(PB_TRIGGER_7,time_of_key,trigger[7], FOREVER);
						pb->GetValue(PB_TRIGGER_8,time_of_key,trigger[8], FOREVER);
						pb->GetValue(PB_TRIGGER_9,time_of_key,trigger[9], FOREVER);
						pb->GetValue(PB_TRIGGER_10,time_of_key,trigger[10], FOREVER);
						pb->GetValue(PB_TRIGGER_11,time_of_key,trigger[11], FOREVER);
						pb->GetValue(PB_TRIGGER_12,time_of_key,trigger[12], FOREVER);
						pb->GetValue(PB_TRIGGER_13,time_of_key,trigger[13], FOREVER);
						pb->GetValue(PB_TRIGGER_14,time_of_key,trigger[14], FOREVER);
						pb->GetValue(PB_TRIGGER_15,time_of_key,trigger[15], FOREVER);

						
						int new_value = 0;

						
						for (int i =0; i < 16; ++i)
						{
							if(trigger[i] < 0 || trigger[i] > 3)
							{
//								SPEW((0, "INVALID TRIGGER!!!!"));
								Clamp(trigger[i],0,3);
							}

							new_value = new_value << 2;
							new_value |= trigger[i]; // mask out higher numbers
							
						}
						
						//SPEW((0,"%d",new_value));

						// if the last one is diffrent than this one then mark it down.
						if (current_frame != 0)
						{
							if (trigger_stream[current_frame - 1].triggers != new_value)
							{
								
								trigger_stream[current_frame].time = TicksToSec(time_of_key);
								trigger_stream[current_frame].triggers = new_value;
								current_frame++;
							}
						}
						else //first trigger, write it down
						{
							trigger_stream[current_frame].time = TicksToSec(time_of_key);
							trigger_stream[current_frame].triggers = new_value;
							current_frame++;
						}

					}
				

					// sample the positions of each value into a linear int controller
					// then simplify the controller to a threshhold of 0.0


					MakeTriggerChannelStream(
						trigger_stream,
						current_frame,
						key_data,
						size_of_data, 
						key_time_data,
						size_of_time_data,
						anim_data, 
						local_channel_counter, 
						current_joint);


					anim_data.animChannelBlock[local_channel_counter].channelType = 
						MW4Animation::ChannelAnimData::TriggerChannelType;
					local_channel_counter++;

					delete[] trigger_stream;
					
				}
			}
		}


		++current_joint;
	}


	
	int total_done = (int)(((float)current_joint/(float)anim_data.GetJointCount())*100.0f);
	
	if (!suppress_prompts)
	{
		ip->ProgressUpdate(total_done);
	}


	// this is an absolute counter to index into the joints prefs with.
	++counter;
	// For each child of this node, we recurse into ourselves 
	// and increment the counter until no more children are found.
	for (int c = 0; c < node->NumberOfChildren(); c++) 
	{
		MakeJointStream(ip, node->GetChildNode(c), key_data, size_of_data, key_time_data, size_of_time_data, counter, anim_data, current_channel, current_joint, dialog, suppress_prompts);
	}
}



//===========================================================================//

void AnimExp::MakeTriggerChannelStream(
		TriggerTempStorage* trigger_stream,
		int  frame_count,
		BYTE **key_data,
		size_t *size_of_data,
		BYTE **key_time_data,
		size_t *size_of_time_data, 
		MW4Animation::AnimData &anim_data,
		int current_channel,
		int current_joint
	)
{

	anim_data.animChannelBlock[current_channel].keyType = MW4Animation::JointAnimData::TriggerLinearKeyType;
	anim_data.animChannelBlock[current_channel].sizeOfOneKey = sizeof(MW4Animation::TriggerLinearKey);
	anim_data.animChannelBlock[current_channel].keyframeCount = frame_count;
	anim_data.animChannelBlock[current_channel].jointNumber = current_joint;

	size_t total_size = 
		anim_data.animChannelBlock[current_channel].sizeOfOneKey *
		anim_data.animChannelBlock[current_channel].keyframeCount;	

	size_t total_time_size = 
		4 * anim_data.animChannelBlock[current_channel].keyframeCount;	


	// this is part of an optimization.  We will cull out non used channels later.
	if (total_size == 0)
	{
		return;
	}

	Verify(key_data[current_channel] == NULL);
	key_data[current_channel] = new BYTE[total_size];
	Register_Pointer(key_data[current_channel]);
	size_of_data[current_channel] = total_size;


	Verify(key_time_data[current_channel] == NULL);
	key_time_data[current_channel] = new BYTE[total_time_size];
	Register_Pointer(key_time_data[current_channel]);
	size_of_time_data[current_channel] = total_time_size;



	//convert keys and copy them into the array

	// make keydatastream...
	Stuff::MemoryStream stream(key_data[current_channel], total_size, 0);
	Stuff::MemoryStream time_stream(key_time_data[current_channel], total_time_size, 0);


	for (int i = 0; i < anim_data.animChannelBlock[current_channel].keyframeCount; ++i)
	{
		
		// convert key
		MW4Animation::TriggerLinearKey *new_key = 
			(MW4Animation::TriggerLinearKey*)stream.GetPointer();

		MW4Animation::BaseKeyframeTime *new_time_key = 
			(MW4Animation::BaseKeyframeTime*)time_stream.GetPointer();

		new_time_key->timeOfFrame = trigger_stream[i].time;
		new_key->valueOfKey = trigger_stream[i].triggers;


		stream.AdvancePointer(anim_data.animChannelBlock[current_channel].sizeOfOneKey);
		time_stream.AdvancePointer(4);

	}
}

//===========================================================================//

void AnimExp::MakeChannelVelocityStream(
		Control* c, 
		BYTE **key_data,
		size_t *size_of_data, 
		BYTE **key_time_data,
		size_t *size_of_time_data, 
		MW4Animation::AnimData &anim_data,
		int current_channel,
		int current_joint
	)
{

	anim_data.animChannelBlock[current_channel].keyType = MW4Animation::JointAnimData::VelocityLinearKeyType;
	anim_data.animChannelBlock[current_channel].sizeOfOneKey = sizeof(MW4Animation::VelocityLinearKey);
	anim_data.animChannelBlock[current_channel].keyframeCount = GetFrameCount(c);
	anim_data.animChannelBlock[current_channel].jointNumber = current_joint;

	size_t total_size = 
		anim_data.animChannelBlock[current_channel].sizeOfOneKey *
		anim_data.animChannelBlock[current_channel].keyframeCount;	

	size_t total_time_size = 
		4 * anim_data.animChannelBlock[current_channel].keyframeCount;	




	// this is part of an optimization.  We will cull out non used channels later.
	if (total_size == 0)
	{
		return;
	}

	Verify(key_data[current_channel] == NULL);
	key_data[current_channel] = new BYTE[total_size];
	Register_Pointer(key_data[current_channel]);
	size_of_data[current_channel] = total_size;


	Verify(key_time_data[current_channel] == NULL);
	key_time_data[current_channel] = new BYTE[total_time_size];
	Register_Pointer(key_time_data[current_channel]);
	size_of_time_data[current_channel] = total_time_size;

	//convert keys and copy them into the array

	// make keydatastream...
	Stuff::MemoryStream stream(key_data[current_channel], total_size, 0);
	Stuff::MemoryStream time_stream(key_time_data[current_channel], total_time_size, 0);

	Verify(c->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID,0));
	IKeyControl *ikeys = GetKeyControlInterface(c);

	for (int i = 0; i < anim_data.animChannelBlock[current_channel].keyframeCount; ++i)
	{
		
		// convert key
		MW4Animation::VelocityLinearKey *new_key = 
			(MW4Animation::VelocityLinearKey*)stream.GetPointer();

		MW4Animation::BaseKeyframeTime *new_time_key = 
			(MW4Animation::BaseKeyframeTime*)time_stream.GetPointer();


		
		ILinPoint3Key key;
		ikeys->GetKey(i, &key);
		
		new_time_key->timeOfFrame = TicksToSec(key.time);
		new_key->valueOfKey.x =  key.val.x;
		new_key->valueOfKey.y =  key.val.z;
		new_key->valueOfKey.z = -key.val.y;
	
		new_key->velocityOfKey.x =  key.val.x;
		new_key->velocityOfKey.y =  key.val.z;
		new_key->velocityOfKey.z = -key.val.y;


		stream.AdvancePointer(anim_data.animChannelBlock[current_channel].sizeOfOneKey);
		time_stream.AdvancePointer(4);

	}

	SolveVelocities(c, stream, time_stream, anim_data, current_channel);
}


//===========================================================================//

void AnimExp::MakeChannelAngularVelocityStream(
		Control* c, 
		BYTE **key_data,
		size_t *size_of_data, 
		BYTE **key_time_data,
		size_t *size_of_time_data, 
		MW4Animation::AnimData &anim_data,
		int current_channel,
		int current_joint
	)
{


	anim_data.animChannelBlock[current_channel].keyType = MW4Animation::JointAnimData::AngularVelocityLinearKeyType;
	anim_data.animChannelBlock[current_channel].sizeOfOneKey = sizeof(MW4Animation::AngularVelocityLinearKey);
	anim_data.animChannelBlock[current_channel].keyframeCount = GetFrameCount(c);
	anim_data.animChannelBlock[current_channel].jointNumber = current_joint;

	size_t total_size = 
		anim_data.animChannelBlock[current_channel].sizeOfOneKey *
		anim_data.animChannelBlock[current_channel].keyframeCount;	

	size_t total_time_size = 
		4 * anim_data.animChannelBlock[current_channel].keyframeCount;	


	// this is part of an optimization.  We will cull out non used channels later.
	if (total_size == 0)
	{
		return;
	}

	Verify(key_data[current_channel] == NULL);
	key_data[current_channel] = new BYTE[total_size];
	Register_Pointer(key_data[current_channel]);
	size_of_data[current_channel] = total_size;


	Verify(key_time_data[current_channel] == NULL);
	key_time_data[current_channel] = new BYTE[total_time_size];
	Register_Pointer(key_time_data[current_channel]);
	size_of_time_data[current_channel] = total_time_size;

	//convert keys and copy them into the array

	// make keydatastream...
	Stuff::MemoryStream stream(key_data[current_channel], total_size, 0);
	Stuff::MemoryStream time_stream(key_time_data[current_channel], total_time_size, 0);

	Verify(c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID,0));
	IKeyControl *ikeys = GetKeyControlInterface(c);

	for (int i = 0; i < anim_data.animChannelBlock[current_channel].keyframeCount; ++i)
	{


		MW4Animation::AngularVelocityLinearKey *new_key = (MW4Animation::AngularVelocityLinearKey*)stream.GetPointer();
		MW4Animation::BaseKeyframeTime *new_time_key = (MW4Animation::BaseKeyframeTime*)time_stream.GetPointer();


		ILinRotKey key;
		ikeys->GetKey(i, &key);
		
		key.val.Normalize();

		new_time_key->timeOfFrame = TicksToSec(key.time);
		new_key->valueOfKey.x =  key.val.x;
		new_key->valueOfKey.y =  key.val.z;
		new_key->valueOfKey.z = -key.val.y;
		new_key->valueOfKey.w = -key.val.w;
	

		new_key->valueOfKey.Normalize();

		new_key->velocityOfKey = Stuff::Vector3D::Identity;


		stream.AdvancePointer(anim_data.animChannelBlock[current_channel].sizeOfOneKey);
		time_stream.AdvancePointer(4);
	}

	SolveAngularVelocities(c, stream, time_stream, anim_data, current_channel);

}


//===========================================================================//

void AnimExp::MakeChannelStream(
		Control* c, 
		BYTE **key_data,
		size_t *size_of_data, 
		BYTE **key_time_data,
		size_t *size_of_time_data, 
		MW4Animation::AnimData &anim_data,
		int current_channel,
		int current_joint
	)
{

	Verify(c->IsLeaf());

	// Thanks max for not putting this in the HEADER FILE!
	// you butthead!  
	//Verify(c->ClassID().PartA() != IPOS_CONTROL_CLASS_ID);
	
	Verify(c->ClassID().PartA() != EULER_CONTROL_CLASS_ID);
	//Verify(JointEditDialog::IsKnownControlerType(c));

	// Convert each channles keyframes to MW4 style and add them to 
	// the temp list.
	
	anim_data.animChannelBlock[current_channel].keyType = GetKeyType(c);
	anim_data.animChannelBlock[current_channel].sizeOfOneKey = GetSizeOfKey(GetKeyType(c));
	anim_data.animChannelBlock[current_channel].keyframeCount = GetFrameCount(c);
	anim_data.animChannelBlock[current_channel].jointNumber = current_joint;
	
	size_t total_size = 
		anim_data.animChannelBlock[current_channel].sizeOfOneKey *
		anim_data.animChannelBlock[current_channel].keyframeCount;	
	

	size_t total_time_size = 
		4 * anim_data.animChannelBlock[current_channel].keyframeCount;	


	// this is part of an optimization.  We will cull out non used channels later.
	if (total_size == 0)
	{
		return;
	}

	Verify(key_data[current_channel] == NULL);
	key_data[current_channel] = new BYTE[total_size];
	Register_Pointer(key_data[current_channel]);
	size_of_data[current_channel] = total_size;


	Verify(key_time_data[current_channel] == NULL);
	key_time_data[current_channel] = new BYTE[total_time_size];
	Register_Pointer(key_time_data[current_channel]);
	size_of_time_data[current_channel] = total_time_size;


	//convert keys and copy them into the array

	// make keydatastream...
	Stuff::MemoryStream stream(key_data[current_channel], total_size, 0);
	Stuff::MemoryStream time_stream(key_time_data[current_channel], total_time_size, 0);

	for (int i = 0; i < anim_data.animChannelBlock[current_channel].keyframeCount; ++i)
	{
		ConvertKey(stream, time_stream, c, i);
		stream.AdvancePointer(anim_data.animChannelBlock[current_channel].sizeOfOneKey);
		time_stream.AdvancePointer(4);
	}

	

	//SolveVelocities(c, stream, anim_data, current_channel);

}

//===========================================================================//

//#define Tell_Value(x) SPEW((0, #x" : %f", ##x));

void
	AnimExp::SolveVelocities(
		Control* c,
		Stuff::MemoryStream &stream,
		Stuff::MemoryStream &time_stream,
		MW4Animation::AnimData &anim_data,
		int current_channel
)
{
	// figure out the type of key...
	VERIFY(anim_data.animChannelBlock[current_channel].channelType ==
			MW4Animation::ChannelAnimData::VelocityChannelType);



	
	stream.Rewind();
	time_stream.Rewind();
	
	Verify(anim_data.animChannelBlock[current_channel].keyType == MW4Animation::JointAnimData::VelocityLinearKeyType);
	MW4Animation::VelocityLinearKey *solve_for_key_array = (MW4Animation::VelocityLinearKey*)stream.GetPointer();
	MW4Animation::BaseKeyframeTime *solve_for_key_time_array = (MW4Animation::BaseKeyframeTime*)time_stream.GetPointer();


	int num_keys = anim_data.animChannelBlock[current_channel].keyframeCount;
	
	if (num_keys < 2)
	{
		// no keys, no speed
		if (num_keys==1)
		{
			solve_for_key_array[0].velocityOfKey = Stuff::Point3D(0.0f, 0.0f, 0.0f);
		}
	}
	else
	{
		Stuff::Point3D *velocities = new Stuff::Point3D[num_keys];
		Register_Pointer(velocities);

		for (int i = 0; i < num_keys; ++i)
		{
			//SPEW(("jerryeds", "time : %f",  solve_for_key_time_array[i].timeOfFrame));

			if (i>0)
			{
			
				Stuff::Scalar delta_time = solve_for_key_time_array[i].timeOfFrame - solve_for_key_time_array[i-1].timeOfFrame;
				Stuff::Point3D delta_position;
				delta_position.Subtract(solve_for_key_array[i].valueOfKey, solve_for_key_array[i-1].valueOfKey);
				velocities[i].Divide(delta_position, delta_time);
				
				//Tell_Value(i);
				//Tell_Value(solve_for_key_array[i].timeOfFrame);
				//Tell_Value(solve_for_key_array[i-1].timeOfFrame);
				//Tell_Value(delta_time);

				//Tell_Value(solve_for_key_array[i].valueOfKey.z);
				//Tell_Value(solve_for_key_array[i-1].valueOfKey.z);
				//Tell_Value(delta_position.z);

				//Tell_Value(velocities[i].z);
			}
			else
			{
				
				Stuff::Scalar delta_time = solve_for_key_time_array[i+1].timeOfFrame - solve_for_key_time_array[i].timeOfFrame;
				Stuff::Point3D delta_position;
				delta_position.Subtract(solve_for_key_array[i+1].valueOfKey, solve_for_key_array[i].valueOfKey);
				velocities[i].Divide(delta_position, delta_time);
				
				//Tell_Value(i);
				//Tell_Value(solve_for_key_array[i+1].timeOfFrame);
				//Tell_Value(solve_for_key_array[i].timeOfFrame);
				//Tell_Value(delta_time);

				//Tell_Value(solve_for_key_array[i+1].valueOfKey.z);
				//Tell_Value(solve_for_key_array[i].valueOfKey.z);
				//Tell_Value(delta_position.z);

				//Tell_Value(velocities[i].z);
				
			}
			
		}

		for (i = 0; i < num_keys; ++i)
		{
			solve_for_key_array[i].velocityOfKey = velocities[i];
		}

		Unregister_Pointer(velocities);
		delete[] velocities;

	}

	stream.AdvancePointer(anim_data.animChannelBlock[current_channel].sizeOfOneKey * num_keys);
	time_stream.AdvancePointer(4 * num_keys);

}


//===========================================================================//


void
	AnimExp::SolveAngularVelocities(
		Control* c,
		Stuff::MemoryStream &stream,
		Stuff::MemoryStream &time_stream,
		MW4Animation::AnimData &anim_data,
		int current_channel
)
{

	// figure out the type of key...
	VERIFY(anim_data.animChannelBlock[current_channel].channelType ==
			MW4Animation::ChannelAnimData::AngularVelocityChannelType);



	
	stream.Rewind();
	time_stream.Rewind();

	Verify(anim_data.animChannelBlock[current_channel].keyType == MW4Animation::JointAnimData::AngularVelocityLinearKeyType);
	MW4Animation::AngularVelocityLinearKey *solve_for_key_array = (MW4Animation::AngularVelocityLinearKey*)stream.GetPointer();
	MW4Animation::BaseKeyframeTime *solve_for_key_time_array = (MW4Animation::BaseKeyframeTime*)time_stream.GetPointer();


	int num_keys = anim_data.animChannelBlock[current_channel].keyframeCount;
	
	if (num_keys < 2)
	{
		// no keys, no speed
		if (num_keys==1)
		{
			solve_for_key_array[0].velocityOfKey = Stuff::Vector3D(0.0f, 0.0f, 0.0f);
		}
	}
	else
	{
		Stuff::Vector3D *velocities = new Stuff::Vector3D[num_keys];
		Register_Pointer(velocities);

		for (int i = 0; i < num_keys; ++i)
		{

			//SPEW(("jerryeds", "time : %f",  solve_for_key_time_array[i].timeOfFrame));

			if (i>0)
			{
			
				Stuff::UnitQuaternion instantanious_velocity;
				instantanious_velocity.Subtract(solve_for_key_array[i].valueOfKey, solve_for_key_array[i-1].valueOfKey);
				

				Stuff::Scalar delta_time = solve_for_key_time_array[i].timeOfFrame - solve_for_key_time_array[i-1].timeOfFrame;
				Stuff::Vector3D instantanious_velocity_vector_3d(instantanious_velocity);

				velocities[i].Divide(instantanious_velocity_vector_3d, delta_time);


				//Stuff::Scalar delta_time = solve_for_key_array[i].timeOfFrame - solve_for_key_array[i-1].timeOfFrame;
				//Stuff::Point3D delta_position;
				//delta_position.Subtract(solve_for_key_array[i].valueOfKey, solve_for_key_array[i-1].valueOfKey);
				//velocities[i].Divide(delta_position, delta_time);
				
				//Tell_Value(i);
				//Tell_Value(solve_for_key_array[i].timeOfFrame);
				//Tell_Value(solve_for_key_array[i-1].timeOfFrame);
				//Tell_Value(delta_time);

				//Tell_Value(solve_for_key_array[i].valueOfKey.z);
				//Tell_Value(solve_for_key_array[i-1].valueOfKey.z);
				//Tell_Value(delta_position.z);

				//Tell_Value(velocities[i].z);
			}
			else
			{
				Stuff::UnitQuaternion instantanious_velocity;
				instantanious_velocity.Subtract(solve_for_key_array[i+1].valueOfKey, solve_for_key_array[i].valueOfKey);
				
				Stuff::Scalar delta_time = solve_for_key_time_array[i+1].timeOfFrame - solve_for_key_time_array[i].timeOfFrame;
				Stuff::Vector3D instantanious_velocity_vector_3d(instantanious_velocity);

				velocities[i].Divide(instantanious_velocity_vector_3d, delta_time);


				//Stuff::Scalar delta_time = solve_for_key_array[i+1].timeOfFrame - solve_for_key_array[i].timeOfFrame;
				//Stuff::Point3D delta_position;
				//delta_position.Subtract(solve_for_key_array[i+1].valueOfKey, solve_for_key_array[i].valueOfKey);
				//velocities[i].Divide(delta_position, delta_time);
				
				//Tell_Value(i);
				//Tell_Value(solve_for_key_array[i+1].timeOfFrame);
				//Tell_Value(solve_for_key_array[i].timeOfFrame);
				//Tell_Value(delta_time);

				//Tell_Value(solve_for_key_array[i+1].valueOfKey.z);
				//Tell_Value(solve_for_key_array[i].valueOfKey.z);
				//Tell_Value(delta_position.z);

				//Tell_Value(velocities[i].z);
				
			}
			
		}

		for (i = 0; i < num_keys; ++i)
		{
			solve_for_key_array[i].velocityOfKey = velocities[i];
		}

		Unregister_Pointer(velocities);
		delete[] velocities;

	}

	stream.AdvancePointer(anim_data.animChannelBlock[current_channel].sizeOfOneKey * num_keys);
	time_stream.AdvancePointer(4 * num_keys);
}


//===========================================================================//



BYTE AnimExp::GetKeyType(Control* c)
{
	if (c->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID,0))
	{
		return MW4Animation::JointAnimData::Point3DLinearKeyType;
	}
	else if (c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID,0))
	{
		return MW4Animation::JointAnimData::QuaternionLinearKeyType;
	}
	
	STOP(("INVALID KEY TYPE!!!!!"));
	

	return 0;
}

//===========================================================================//


size_t AnimExp::GetSizeOfKey(int key_type)
{
	switch (key_type)
	{	
		case MW4Animation::JointAnimData::Point3DLinearKeyType:
			return sizeof(MW4Animation::Point3DLinearKey);
		
		case MW4Animation::JointAnimData::QuaternionLinearKeyType:
			return sizeof(MW4Animation::QuaternionLinearKey);
		
		case MW4Animation::JointAnimData::TriggerLinearKeyType:
			return sizeof(MW4Animation::TriggerLinearKey);
		
		case MW4Animation::JointAnimData::VelocityLinearKeyType:
			return sizeof(MW4Animation::TriggerLinearKey);

		case MW4Animation::JointAnimData::AngularVelocityLinearKeyType:
			return sizeof(MW4Animation::AngularVelocityLinearKey);

	}
	return 0;
}

//===========================================================================//

int AnimExp::GetFrameCount(Control* c)
{
	// ALL OUR SUPPORTED TYPES HAVE Interfaces
	// XYZ doesn't have an interface but the known
	// sub controllers do!

	return c->NumKeys();

}

//===========================================================================//


void AnimExp::ConvertKey(Stuff::MemoryStream &stream, Stuff::MemoryStream &time_stream, Control *c, int key_number)
{
	// ALL OUR SUPPORTED TYPES HAVE Interfaces
	// XYZ doesn't have an interface but the known
	// sub controllers do!
	IKeyControl *ikeys = GetKeyControlInterface(c);

	if (c->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID,0))
	{
		MW4Animation::Point3DLinearKey *new_key = (MW4Animation::Point3DLinearKey*)stream.GetPointer();
		MW4Animation::BaseKeyframeTime *new_time_key = (MW4Animation::BaseKeyframeTime*)time_stream.GetPointer();
		ILinPoint3Key key;
		ikeys->GetKey(key_number, &key);
		
		new_time_key->timeOfFrame = TicksToSec(key.time);
		
		new_key->valueOfKey.x =  key.val.x;
		new_key->valueOfKey.y =  key.val.z;
		new_key->valueOfKey.z = -key.val.y;
	}
	else if (c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID,0))
	{
		MW4Animation::QuaternionLinearKey *new_key = (MW4Animation::QuaternionLinearKey*)stream.GetPointer();
		MW4Animation::BaseKeyframeTime *new_time_key = (MW4Animation::BaseKeyframeTime*)time_stream.GetPointer();
		ILinRotKey key;
		ikeys->GetKey(key_number, &key);
		
		key.val.Normalize();

		new_time_key->timeOfFrame = TicksToSec(key.time);
		
		new_key->valueOfKey.x =  key.val.x;
		new_key->valueOfKey.y =  key.val.z;
		new_key->valueOfKey.z = -key.val.y;
		new_key->valueOfKey.w = -key.val.w;

		new_key->valueOfKey.Normalize();
	}
}


