#include "munga.h"
#pragma hdrstop

#include "jmover.h"
#include "fileutil.h"
#include "app.h"
#include "notation.h"
#include "namelist.h"

//#############################################################################
//############################    JointedMover    #############################
//#############################################################################

//#############################################################################
// Shared Data Support
//
Derivation* JointedMover::GetClassDerivations()
{	static Derivation classDerivations(Mover::GetClassDerivations(), "JointedMover");
	return &classDerivations;
}


JointedMover::SharedData
	JointedMover::DefaultData(
		JointedMover::GetClassDerivations(),
		JointedMover::GetMessageHandlers(),
		JointedMover::GetAttributeIndex(),
		JointedMover::StateCount,
		(Entity::MakeHandler)JointedMover::Make
	);

//#############################################################################
// Attribute Support
//

	const JointedMover::IndexEntry
	JointedMover::AttributePointers[]=
{
	{
		JointedMover::SegmentCountAttributeID,
		"SegmentCount",
		(Simulation::AttributePointer)&JointedMover::segmentCount
	},
	{
		JointedMover::SegmentTableAttributeID,
		"SegmentTable",
		(Simulation::AttributePointer)&JointedMover::segmentTable
	},
	{
		JointedMover::JointSubsystemAttributeID,
		"JointSubsystem",
		(Simulation::AttributePointer)&JointedMover::jointSubsystem
	}
};

JointedMover::AttributeIndexSet& JointedMover::GetAttributeIndex()
{
	static JointedMover::AttributeIndexSet attributeIndex(ELEMENTS(JointedMover::AttributePointers),
		JointedMover::AttributePointers,
		Mover::GetAttributeIndex()
	);
	return attributeIndex;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntitySegment*
	JointedMover::GetSegment(CString segment_name)
{
	Check(this);
	Check_Pointer(segment_name);
	EntitySegment::SegmentTableIterator iterator(segmentTable);

	EntitySegment *current_segment;

	while( (current_segment = iterator.ReadAndNext() ) != NULL)
	{
		if(segment_name.Compare(current_segment->GetName()) == 0)
		{
			return current_segment;
		}
	}
	return NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntitySegment*
	JointedMover::GetSegmentFromDamageZone(CString damage_zone)
{
	EntitySegment::SegmentTableIterator iterator(segmentTable);
	EntitySegment *current_segment;
	while ((current_segment = iterator.ReadAndNext() ) != NULL)
	{
		EntitySegment::IntegerTableIterator *damage_iterator;
		damage_iterator = current_segment->MakeDamageZoneIndexTable();
		Register_Object(damage_iterator);
		EntitySegment::IntegerPlug *index_plug;
		while ((index_plug = damage_iterator->ReadAndNext() ) != NULL)
		{
			CString current_name;
			Verify(index_plug->GetItem() >= 0);
			Verify(index_plug->GetItem() <= damageZoneCount);

			current_name = damageZones[index_plug->GetItem()]->damageZoneName;
			Check_Pointer(&current_name);
			if(current_name.Compare(damage_zone) == 0)
			{
				Unregister_Object(damage_iterator);
				delete damage_iterator;
				return current_segment;
			}
		}
		Unregister_Object(damage_iterator);
		delete damage_iterator;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	JointedMover::GetSegmentToWorld(
		EntitySegment &my_segment,
		LinearMatrix *transform
	)
{
	Check(this);
	JointSubsystem *joints = GetJointSubsystem();
	Check(joints);

	//
	//---------------------------------------------------------------
	// Check to see if the segment transforms need to be recalculated
	//---------------------------------------------------------------
	//
	if (joints->AreJointsModified())
	{
		EntitySegment::SegmentTableIterator iterator(segmentTable);
		EntitySegment *current_segment;
		while( (current_segment = iterator.ReadAndNext() ) != NULL)
		{
			current_segment->ModifySegment();
		}
		joints->ModifyJoints(False);
	}

	//
	//-----------------------------
	// Compute the actual transform
	//-----------------------------
	//
	transform->Multiply(
		my_segment.GetSegmentToEntity(),
		localToWorld
	);
	Check_Fpu();
}

//#############################################################################
// Construction and Destruction
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointedMover::JointedMover(
	JointedMover::MakeMessage *creation_message,
	JointedMover::SharedData &virtual_data
):
	Mover(creation_message, virtual_data),
	jointSubsystem(NULL),
	segmentTable(this, True)
{
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Create the JointSubsystem
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	JointSubsystem *joint_subsystem = new JointSubsystem(
		(Entity*) this,
		Entity::EntitySubsystemID
	);

	Register_Object(joint_subsystem);
	jointSubsystem.Add(joint_subsystem);
	Check(joint_subsystem);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Get Resource Description
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	ResourceDescription *skl_resource =
		application->GetResourceFile()->SearchList(
			creation_message->resourceID,
			ResourceDescription::SkeletonStreamResourceType
		);
	Check(skl_resource);
	skl_resource->Lock();

	DynamicMemoryStream segment_stream(
		skl_resource->resourceAddress,
		skl_resource->resourceSize);
	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// read in segmentCount and jointCount
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	int joint_count;
	segment_stream >> segmentCount >> joint_count;

	int current_joint_type;
	int current_joint_index = 0;

	CString                 segment_name;
	EntitySegment                 *parent_segment;
	EntitySegment                 *current_segment;
	int                             segment_joint_index;
	Joint                           *segment_joint_pointer;
	int                             parent_index;
	CString                 video_object_name;
	LinearMatrix    segment_offset(LinearMatrix::Identity);
	Logical                 site_segment;

	int ii;
	for(ii=0;ii<segmentCount;ii++)
	{
		//
		//~~~~~~~~~~~~~~~~~~
		// Read segment name
		//~~~~~~~~~~~~~~~~~~
		//
		segment_stream >> segment_name >> parent_index;

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// read parent index & find parentSegment ptr
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		if(parent_index == -1)
		{
			parent_segment = NULL;
		}
		else
		{
			EntitySegment::SegmentTableIterator iterator(segmentTable);
			parent_segment =  iterator.GetNth(parent_index);
		}
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Determine if this segment is a site
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		segment_stream >> site_segment >> current_joint_type;

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Get the joint type & init the joint
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		if(current_joint_type != Joint::NULLJointType)
		{
			segment_joint_index = current_joint_index;
			segment_joint_pointer =
				new Joint(
					joint_subsystem,
					(Joint::JointType)current_joint_type
				);
         Register_Object(segment_joint_pointer);
			joint_subsystem->AddJoint(current_joint_index, segment_joint_pointer);

			++current_joint_index;
		}
		else
		{
	segment_joint_pointer = NULL;
			segment_joint_index = -1;
		}
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Read in the segment offset matrix
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		segment_stream >> segment_offset;

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Get The Primary Damage Zone
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		int primary_dzone_index;
		segment_stream >> primary_dzone_index;

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Create this segment and add
		// add it to the segment table
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		current_segment = new EntitySegment(
			segment_name,
			ii,          // my_index
			parent_index,
			segment_joint_index,
			segment_joint_pointer,
			segment_offset,
			parent_segment,
			site_segment,
			primary_dzone_index
		);
		Register_Object(current_segment);
		segmentTable.AddValue(current_segment, ii);
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Read in ALL VideoObjectNames
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		int             video_object_count;
		EntitySegment::SkeletonType skl_type;

		segment_stream >> video_object_count;
		Verify(video_object_count);

		int jj;
		for(jj=0;jj<video_object_count;++jj)
		{
			segment_stream >> skl_type >> video_object_name;
			if(video_object_name.Compare("None"))
			{
				current_segment->AddVideoObjectName(
					video_object_name,
					skl_type,
					DamageZone::ExistsGraphicState
				);
			}
		}

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Read in ALL DESTROYED VideoObjectNames
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		int destroyed_video_object_count;

		segment_stream >> destroyed_video_object_count;

		for(jj=0;jj<destroyed_video_object_count;++jj)
		{
			segment_stream >> skl_type >> video_object_name;
			if(video_object_name.Compare("None"))
			{
				current_segment->AddVideoObjectName(
					video_object_name,
					skl_type,
					DamageZone::DestroyedGraphicState
				);
			}
		}

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Read in DamageZone information
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		int damage_zone_count;
		int damage_zone_index;

		segment_stream >> damage_zone_count;
		if(damage_zone_count)
		{

			for(int dd=0;dd<damage_zone_count;++dd)
			{
				segment_stream >> damage_zone_index;
				current_segment->AddDamageZone(damage_zone_index, dd);
			}
		}
		//
		// Read in the Children Information
		//
		int children_count;
		int child_index;

		segment_stream >> children_count;

		if(children_count)
		{
			for(int jj=0;jj<children_count;++jj)
			{
				segment_stream >> child_index;
				current_segment->AddChildIndex(child_index, jj);
			}
		}
	}
	skl_resource->Unlock();

	//
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Initialize all the child Pointers for each segment
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//
	for(ii=0;ii<segmentCount;++ii)
	{
		EntitySegment::SegmentTableIterator iterator(segmentTable);
		InitializeChildPointers(iterator.GetNth(ii));
	}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	JointedMover::InitializeChildPointers(EntitySegment *current_segment)
{
	EntitySegment::IntegerTableIterator *iterator;
	EntitySegment::SegmentTableIterator segment_iterator(segmentTable);
	iterator = current_segment->MakeChildIndexTable();
	Register_Object(iterator);
	EntitySegment::IntegerPlug *current_index;
	int     child_count=0;
	while((current_index = iterator->ReadAndNext()) != NULL)
	{
		EntitySegment *child_segment;
		child_segment = segment_iterator.GetNth(current_index->GetItem());
		Check(child_segment);
		current_segment->AddChildPointer(child_segment, child_count);
		++child_count;
	}
	Unregister_Object(iterator);
	delete iterator;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointedMover::~JointedMover()
{
	EntitySegment::SegmentTableIterator iterator(segmentTable);
	iterator.DeletePlugs();
//
//---------------------------------------------------------------------
// This should be deleted by the entity subsystemArray[jointSubsystem]
//---------------------------------------------------------------------
//
#if 0
		JointSubsystem *joint_subsystem = jointSubsystem.GetCurrent();
		Unregister_Object(joint_subsystem);
		delete joint_subsystem;
#endif
}

//########################################################################
// DamageZone Support
//
ResourceDescription::ResourceID
	JointedMover::CreateDamageZoneStream(
		ResourceFile *resource_file,
		const char   *model_name,
		NotationFile *model_file,
		const ResourceDirectories *directories
	)
{
	Check(resource_file);
	Check_Pointer(model_name);
	Check(model_file);
	Check_Pointer(directories);

	//
	//-------------------
	// Find the .skl file
	//-------------------
	//
	const char* skl_entry;
	if (
		!model_file->GetEntry(
			"video",
			"skeleton",
			&skl_entry
			)
		)
	{
		DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush;
		return -1;
	}
	char *skl_filename =
		MakePathedFilename(directories->videoDirectory, skl_entry);
	Register_Pointer(skl_filename);

	NotationFile *skl_file = new NotationFile(skl_filename);
	Register_Object(skl_file);
	if (!skl_file->PageCount())
	{
		DEBUG_STREAM << skl_filename << " is empty or missing!\n" << std::flush;
Dump_And_Die_1:
		Unregister_Pointer(skl_filename);
		delete[] skl_filename;
		Unregister_Object(skl_file);
		delete skl_file;
		return ResourceDescription::NullResourceID;
	}

	//
	//-------------------
	// Find the .dmg file
	//-------------------
	//
	const char* dmg_entry;
	if (
		!model_file->GetEntry(
			"gamedata",
			"DamageZones",
			&dmg_entry
		)
	)
	{
		DEBUG_STREAM << model_name << " is missing .dmg file specification!\n" << std::flush;
		goto Dump_And_Die_1;
	}

	char *dmg_filename =
		MakePathedFilename(directories->modelDirectory, dmg_entry);
	Register_Pointer(dmg_filename);

	NotationFile *dmg_file = new NotationFile(dmg_filename);
	Register_Object(dmg_file);

	if (!dmg_file->PageCount())
	{
		DEBUG_STREAM << dmg_filename << " is empty or missing!\n" << std::flush;
Dump_And_Die_2:
		Unregister_Pointer(dmg_filename);
		delete[] dmg_filename;
		Unregister_Object(dmg_file);
		delete dmg_file;
		goto Dump_And_Die_1;
	}

	//
	// Get the number of Damage zones
	//
	int dzone_count;
	if (
		!skl_file->GetEntry(
			"ROOT",
			"DZoneCount",
			&dzone_count
		)
	)
	{
		DEBUG_STREAM << model_name << " is missing DZoneCount \n" << std::flush;
		goto Dump_And_Die_2;
	}

	DynamicMemoryStream	damage_zone_stream;

	//
	// write the Damage Zone Count Info from the
	// .dmg file to the stream
	//
	damage_zone_stream << dzone_count;

	//
	// Make an entry list of all the entries in the damage zone page
	//
	NameList *dzone_namelist =
			skl_file->MakeEntryList("DamageZones","dz_");
	Register_Object(dzone_namelist);
	
	if (dzone_namelist->EntryCount() == 0)
	{
		DEBUG_STREAM << "No dZones listed in DamageZones Page"<<std::endl << std::flush;
Dump_And_Die:
		Unregister_Pointer(skl_filename);
		delete skl_filename;
		Unregister_Object(skl_file);
		delete skl_file;
		Unregister_Pointer(dmg_filename);
		delete dmg_filename;
		Unregister_Object(dmg_file);
		delete dmg_file;
		return -1;
	}
	NameList::Entry *dzone_entry;
	char	current_dzone_name[32];

	int num_dzones_found = 0;

	dzone_entry = dzone_namelist->GetFirstEntry();

	while (dzone_entry)
	{
		++num_dzones_found;
		//
		// Get dzone name in .skl file
		//
		Str_Copy(
			current_dzone_name,
			dzone_entry->GetName(),
			sizeof(current_dzone_name)
			);

		//
		//  Create the stream for this damage zone
		//
		DamageZone::CreateStreamedDamageZone(
				 model_file,
				 model_name,
             skl_file,
				 current_dzone_name,
				 &damage_zone_stream,
				 dmg_file,
				 directories
		);

		//
		// Get next dzone entry in this segment page
		//
		dzone_entry = dzone_entry->GetNextEntry();
	}	// End while more dzone_entries

	if(dzone_count != num_dzones_found)
	{
		DEBUG_STREAM <<"DZoneCount != damage zones found in Page DamageZones"<<std::endl << std::flush;
		goto Dump_And_Die;
	}

	//
	//--------------------------------------------------------------------
	// Write the stream out to disk.  Size is equal to the byte difference
	// between stream and damage zone buffer
	//--------------------------------------------------------------------
	//
	ResourceDescription *new_res =
		resource_file->AddResourceMemoryStream(
			model_name,
			ResourceDescription::DamageZoneStreamResourceType,
			1,
			ResourceDescription::Preload,
			&damage_zone_stream
		);
	Check(new_res);
	//
	//		 Free mem
	//
	Unregister_Pointer(skl_filename);
	delete skl_filename;
	Unregister_Object(skl_file);
	delete skl_file;
	Unregister_Pointer(dmg_filename);
	delete dmg_filename;
	Unregister_Object(dmg_file);
	delete dmg_file;
	Unregister_Object(dzone_namelist);
   delete dzone_namelist;
	return  new_res->resourceID;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceDescription::ResourceID
	JointedMover::CreateSkeletonStream(
		ResourceFile *resource_file,
		const char              *model_name,
		NotationFile    *model_file,
		const ResourceDirectories *directories
	)
{
	Check_Pointer(model_name);
	Check(model_file);
	Check_Pointer(directories);

	//
	//-------------------
	// Find the .skl file
	//-------------------
	//
	const char      *entry_data;
	if (
		!model_file->GetEntry(
			"video",
			"skeleton",
			&entry_data
		)
	)
	{
		DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush;
		return -1;
	}
	//
	// Get the name of the SKL File
	//
	char *filename = MakePathedFilename(directories->videoDirectory, entry_data);
	Register_Pointer(filename);

	//
	//              Create a Notation file from the skl ASCII File
	//
	NotationFile *skl_file = new NotationFile(filename);
	Register_Object(skl_file);

	//
	// Get number of Segments(or Pages)
	//
	int segment_count =     skl_file->PageCount();

	//
	//       Do not count Lab_Only and DamageZones they are not included
	//
	if (skl_file->PageExists("LAB_ONLY"))
		--segment_count;
	if(skl_file->PageExists("DamageZones"))
		--segment_count;

	if (!segment_count)
	{
		DEBUG_STREAM << model_name << " has no Segments!\n" << std::flush;
Dump_And_Die:
		Unregister_Pointer(filename);
		delete  filename;
		Unregister_Object(skl_file);
		delete  skl_file;
		return -1;
	}
	int joint_count;
	if (
		!skl_file->GetEntry(
			"ROOT",
			"JointCount",
			&joint_count
		)
	)
	{
		DEBUG_STREAM << model_name << " is missing JointCount \n" << std::flush;
		goto Dump_And_Die;
	}
	DynamicMemoryStream segment_stream;

	//
	//              Write Segment Count to the stream
	//
	segment_stream << segment_count << joint_count;

	//
	//  Create a list of the pages and get the first entry
	//
	NameList *segment_namelist = skl_file->MakePageList();
	Register_Object(segment_namelist);
	NameList::Entry *segment_entry = segment_namelist->GetFirstEntry();
	int
		parent_index = -1,
		children_count;

	CString
		segment_page_name;

	EulerAngles
		rotation;

	Point3D
		translation;

	LinearMatrix
		base_offset(LinearMatrix::Identity);

	while(segment_entry)
	{
		segment_page_name = segment_entry->GetName();

		if(
			(strcmp(segment_page_name, "LAB_ONLY") == 0) ||
			(strcmp(segment_page_name,"DamageZones") ==0)
			)
		{
			segment_entry = segment_entry->GetNextEntry();
			continue;
		}

		segment_stream << segment_page_name;
		//
		// Get the information about the parent
		//
		const char *parent_name;
		if(
			skl_file->GetEntry(
				segment_page_name,
				"parent",
				&parent_name
			)
		)
		{
			parent_index = GetSegmentIndex(parent_name, skl_file);
			if(parent_index == -1)
			{
				DEBUG_STREAM<<model_file<<":"<<parent_name<<" Not Found!"<<std::endl << std::flush;
				goto Dump_And_Die;
			}
		}
		segment_stream << parent_index;

		//
		// Get the joint Type, NULL Joint Type (-1)  if no joint
		//
		const char *joint_name;
		int     joint_type = Joint::NULLJointType;
		Logical is_site(False);
		if(!skl_file->GetEntry(
			segment_page_name,
			"Type",
			&joint_name)
		)
		{
			if( strcmp("ROOT", segment_page_name) != 0)
			{
				is_site = True;
			}
		}
		else
		{
			if(strcmp(joint_name,"static")==0)
			{
				joint_type = Joint::StaticJointType;
			}
			else
			if(strcmp(joint_name, "hingex")==0)
			{
				joint_type = Joint::HingeXJointType;
			}
			else
			if(strcmp(joint_name,"hingey")==0)
			{
				joint_type = Joint::HingeYJointType;
			}
			else
			if(strcmp(joint_name,"hingez")==0)
			{
				joint_type = Joint::HingeZJointType;
			}
			else
			if(strcmp(joint_name,"ball")==0)
			{
				joint_type = Joint::BallJointType;
			}
			else
			if(strcmp(joint_name,"balltranslate")==0)
			{
				joint_type = Joint::BallTranslationJointType;
			}
		}
		segment_stream << is_site << joint_type;
		//
		// Get offsets
		//
		skl_file->GetEntry(segment_page_name,   "tranx" ,&translation.x);
		skl_file->GetEntry(segment_page_name,   "trany" ,&translation.y);
		skl_file->GetEntry(segment_page_name,   "tranz" ,&translation.z);
		skl_file->GetEntry(segment_page_name,   "pitch" ,&rotation.pitch.angle);
		skl_file->GetEntry(segment_page_name,   "yaw"           ,&rotation.yaw.angle);
		skl_file->GetEntry(segment_page_name,   "roll"  ,&rotation.roll.angle);

		base_offset = translation;
		base_offset = rotation;

		//
		// Write out the base offset
		//
		segment_stream << base_offset;

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Get the Primary Damage Zone
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		const char* skl_entry;
		if (!model_file->GetEntry(
				"video",
				"skeleton",
				&skl_entry
			)
		)
		{
			goto Dump_And_Die;
		}
		else
		{
			CString skl_filename =
				MakePathedFilename(directories->videoDirectory, skl_entry);

			NotationFile *skl_file = new NotationFile(skl_filename);
			Register_Object(skl_file);
			if (!skl_file->PageCount())
			{
				DEBUG_STREAM << skl_filename << " is empty or missing!"<<std::endl << std::flush;
				Unregister_Object(skl_file);
				delete skl_file;
				goto Dump_And_Die;
			}
			int primary_damage_zone;
			const char *dzone_name;
			if (skl_file->GetEntry(
					segment_page_name,
					"dzone",
					&dzone_name
				)
			)
			{
				primary_damage_zone = GetDamageZoneIndex(dzone_name, skl_file);
			}
			else
			{
				primary_damage_zone = -1;
			}
			segment_stream << primary_damage_zone;
			Unregister_Object(skl_file);
			delete skl_file;
		}


		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Read in ALL VideoObjectNames
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		if (!
			InitVideoObjectNames(
				&segment_stream,
				model_file,
				segment_page_name,
				directories
			)
		)
		{
			goto Dump_And_Die;
		}

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Read in ALL DESTROYED VideoObjectNames
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		if (!
			InitDestroyedVideoObjectNames(
				&segment_stream,
				model_file,
				segment_page_name,
				directories
			)
		)
		{
			goto Dump_And_Die;
		}

		//
		// Get the damageZones associated with this segment
		//
		NameList *damage_zone_list =
			skl_file->MakeEntryList(segment_entry->GetName(), "dzone");
		Register_Object(damage_zone_list);

		NameList::Entry *damage_zone_entry = damage_zone_list->GetFirstEntry();

		int damage_zone_count = damage_zone_list->EntryCount();

		segment_stream << damage_zone_count;

		if (damage_zone_count)
		{
			for (int dd=0;dd<damage_zone_count;++dd)
			{

				CString damage_zone_name;
				damage_zone_name = damage_zone_entry->GetChar();

				int damage_zone_index;
				damage_zone_index =
				GetDamageZoneIndex(damage_zone_name, skl_file);

				if(damage_zone_index == -1)
				{

					DEBUG_STREAM<<model_file<<":"<<damage_zone_name<<" Not Found!"<<std::endl << std::flush;
					goto Dump_And_Die;

				}
				segment_stream << damage_zone_index;
				damage_zone_entry = damage_zone_entry->GetNextEntry();
			}
		}
		Unregister_Object(damage_zone_list);
		delete damage_zone_list;

		//
		// Get the number of children, 0 if no children
		//
		NameList *children_list =
			skl_file->MakeEntryList(segment_entry->GetName(),"joint");
		Register_Object(children_list);
		NameList::Entry *child_entry = children_list->GetFirstEntry();
		children_count = 0;
		while(child_entry)
		{
			++children_count;
			child_entry = child_entry->GetNextEntry();
		}
		//
		// Write how many children
		//
		segment_stream << children_count;
		if(children_count)
		{
			child_entry = children_list->GetFirstEntry();
			//
			// Write out the segment index for every child
			//
			for(int cc=0;cc<children_count;++cc)
			{
				int child_index =  GetSegmentIndex(child_entry->GetChar(), skl_file);
				if(child_index == -1)
				{
					DEBUG_STREAM<<model_name<<":"<<child_entry->GetName()<<" Not Found!!"<<std::endl << std::flush;

					goto Dump_And_Die;
				}
				segment_stream << child_index;
				child_entry = child_entry->GetNextEntry();
			}
		}
		Unregister_Object(children_list);
		delete children_list;
		segment_entry = segment_entry->GetNextEntry();
	}

	//
	//--------------------------------------------------------------------
	// Write the stream out to disk.
	//--------------------------------------------------------------------
	//
   ResourceDescription *new_res =
		resource_file->AddResourceMemoryStream(
			model_name,
			ResourceDescription::SkeletonStreamResourceType,
			1,
			ResourceDescription::Preload,
			&segment_stream
		);
	Check(new_res);

	//
	//      Fre Mem
	//
	Unregister_Pointer(filename);
	delete  filename;
	Unregister_Object(skl_file);
	delete  skl_file;
	Unregister_Object(segment_namelist);
	delete segment_namelist;

	return  new_res->resourceID;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	JointedMover::InitDestroyedVideoObjectNames(
		MemoryStream					*mem_stream,
		NotationFile					*model_file,
		CString							page_name,
		const ResourceDirectories  *directories
	)
{
	Check(model_file);
	Check(mem_stream);

	NameList *skl_namelist = model_file->MakeEntryList("video","destroyed");
	Register_Object(skl_namelist);
	int entry_count = skl_namelist->EntryCount();
	*mem_stream << entry_count;

	if (!entry_count)
	{
		Unregister_Object(skl_namelist);
		delete skl_namelist;
		return True;
	}
	Verify(entry_count);

	NameList::Entry *skl_entry = skl_namelist->GetFirstEntry();
	while(skl_entry)
	{
		CString entry_name = skl_entry->GetName();
		EntitySegment::SkeletonType skeleton_type;
		if (! entry_name.Compare("destroyed") )
		{
			skeleton_type = EntitySegment::SkeletonType_N;
		}
		else if (! entry_name.Compare("destroyeds") )
		{
			skeleton_type = EntitySegment::SkeletonType_S;
		}
		else if (! entry_name.Compare("destroyedt") )
		{
			skeleton_type = EntitySegment::SkeletonType_T;
		}
		else if (! entry_name.Compare("destroyedo") )
		{
			skeleton_type = EntitySegment::SkeletonType_O;
		}
		else if (! entry_name.Compare("destroyeda") )
		{
			skeleton_type = EntitySegment::SkeletonType_A;
		}
		else if (! entry_name.Compare("destroyedb") )
		{
			skeleton_type = EntitySegment::SkeletonType_B;
		}
		else if (! entry_name.Compare("destroyedc") )
		{
			skeleton_type = EntitySegment::SkeletonType_C;
		}
		else if (! entry_name.Compare("destroyedd") )
		{
			skeleton_type = EntitySegment::SkeletonType_D;
		}
		*mem_stream << skeleton_type;

		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Get the video object filename
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		CString short_skl_filename = skl_entry->GetChar();
		CString skl_filename =
			MakePathedFilename(directories->videoDirectory, short_skl_filename);

		NotationFile *skl_file = new NotationFile(skl_filename);
		Register_Object(skl_file);
		if (!skl_file->PageCount())
		{
			DEBUG_STREAM << skl_filename << " is empty or missing!"<<std::endl << std::flush;
Dump_And_Die:
			Unregister_Object(skl_namelist);
			delete skl_namelist;
			Unregister_Object(skl_file);
			delete skl_file;
			return ResourceDescription::NullResourceID;
		}

		CString video_object_name;
		const char *c_name;
		if (skl_file->GetEntry(
				page_name,
				"Object",
				&c_name
			)
		)
		{
			video_object_name = c_name;
		}
		else
		{
			video_object_name = "None";
		}
		*mem_stream << video_object_name;

		Unregister_Object(skl_file);
		delete skl_file;

		skl_entry = skl_entry->GetNextEntry();
	}
	Unregister_Object(skl_namelist);
	delete skl_namelist;

	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	JointedMover::InitVideoObjectNames(
		MemoryStream					*mem_stream,
		NotationFile					*model_file,
		CString							page_name,
		const ResourceDirectories  *directories
	)
{
	Check(model_file);
	Check(mem_stream);

	NameList *skl_namelist = model_file->MakeEntryList("video","skeleton");
	Register_Object(skl_namelist);
	int entry_count = skl_namelist->EntryCount();

	Verify(entry_count);
	*mem_stream << entry_count;

	NameList::Entry *skl_entry = skl_namelist->GetFirstEntry();
	while(skl_entry)
	{
		CString entry_name = skl_entry->GetName();
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Write out the skeleton type
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		EntitySegment::SkeletonType skeleton_type;
		if (! entry_name.Compare("skeleton") )
		{
			skeleton_type = EntitySegment::SkeletonType_N;
		}
		else if (! entry_name.Compare("skeletons") )
		{
			skeleton_type = EntitySegment::SkeletonType_S;
		}
		else if (! entry_name.Compare("skeletont") )
		{
			skeleton_type = EntitySegment::SkeletonType_T;
		}
		else if (! entry_name.Compare("skeletono") )
		{
			skeleton_type = EntitySegment::SkeletonType_O;
		}
		else if (! entry_name.Compare("skeletona") )
		{
			skeleton_type = EntitySegment::SkeletonType_A;
		}
		else if (! entry_name.Compare("skeletonb") )
		{
			skeleton_type = EntitySegment::SkeletonType_B;
		}
		else if (! entry_name.Compare("skeletonc") )
		{
			skeleton_type = EntitySegment::SkeletonType_C;
		}
		else if (! entry_name.Compare("skeletond") )
		{
			skeleton_type = EntitySegment::SkeletonType_D;
		}
		*mem_stream << skeleton_type;
		//
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Get the video object filename
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//
		CString short_skl_filename = skl_entry->GetChar();
		CString skl_filename =
			MakePathedFilename(directories->videoDirectory, short_skl_filename);

		NotationFile *skl_file = new NotationFile(skl_filename);
		Register_Object(skl_file);
		if (!skl_file->PageCount())
		{
			DEBUG_STREAM << skl_filename << " is empty or missing!"<<std::endl << std::flush;
Dump_And_Die:
			Unregister_Object(skl_namelist);
			delete skl_namelist;
			Unregister_Object(skl_file);
			delete skl_file;
			return ResourceDescription::NullResourceID;
		}

		CString video_object_name;
		const char *c_name;
		if (skl_file->GetEntry(
				page_name,
				"Object",
				&c_name
			)
		)
		{
			video_object_name = c_name;
		}
		else
		{
			video_object_name = "None";
		}
		*mem_stream << video_object_name;

		Unregister_Object(skl_file);
		delete skl_file;

		skl_entry = skl_entry->GetNextEntry();
	}
   Unregister_Object(skl_namelist);
	delete skl_namelist;

	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	JointedMover::GetDamageZoneIndex(
		CString damage_zone_name,
		NotationFile *skl_file
	)
{
	int damage_zone_index = -1;

	NameList *name_list = skl_file->MakeEntryList("DamageZones", "dz_");
	Register_Object(name_list);

	if (name_list->EntryCount() == 0)
	{
		DEBUG_STREAM << "No dZones listed in DamageZones Page"<<std::endl << std::flush;
		Unregister_Object(name_list);
		delete name_list;
		return -1;
	}
	NameList::Entry *damage_zone = name_list->GetFirstEntry();
	while(damage_zone)
	{
		if(strcmp(damage_zone->GetName(), damage_zone_name) == 0)
		{
			Convert_From_Ascii(damage_zone->GetChar(), &damage_zone_index);
			Unregister_Object(name_list);
			delete name_list;
			return damage_zone_index;
		}
      damage_zone = damage_zone->GetNextEntry();
	}
	Unregister_Object(name_list);
	delete name_list;

	return damage_zone_index;

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	JointedMover::GetSegmentIndex(
		CString segment_name,
		NotationFile *skl_file
	)
{
	int     segment_index=-1;

	char page_name[128];
	NameList *page_list = skl_file->MakePageList();
	Register_Object(page_list);
	NameList::Entry *current_entry = page_list->GetFirstEntry();
	while(current_entry)
	{
		Str_Copy(
			page_name,
			current_entry->GetName(),
			sizeof(page_name)
		);
		if(
			(strcmp(page_name, "LAB_ONLY") == 0) ||
			(strcmp(page_name,"DamageZones") == 0)
			)
		{
			current_entry = current_entry->GetNextEntry();
			continue;
		}
		++segment_index;
		if((strcmp(page_name, segment_name) == 0))
		{
			Unregister_Object(page_list);
			delete page_list;
			return segment_index;
		}
		current_entry = current_entry->GetNextEntry();
	}
	Unregister_Object(page_list);
	delete page_list;
	return -1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
JointedMover*
	JointedMover::Make(JointedMover::MakeMessage *creation_message)
{
	return new JointedMover(creation_message, DefaultData);
}
//##########################################################################
//##########################    Animation    ###############################
//##########################################################################

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AnimationInstance::AnimationInstance(JointedMover *jointed_mover)
{
	Check_Pointer(this);
	Check(jointed_mover);

	lerpToZero = False;
	moverToAnimate = jointed_mover;
	jointSubToAnimate = jointed_mover->GetJointSubsystem();
	animationResource = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	AnimationInstance::SetAnimation(
		ResourceDescription::ResourceID animation_number,
		JointedMover::AnimationCallback finished_callback
	)
{
	Check_Pointer(this);
	finishedCallback = finished_callback;
	currentTime = 0.0f;
	currentFrame = 0;

	if (animationResource)
	{
		Check(animationResource);
		animationResource->Unlock();
	}
	animationResource =
		application->GetResourceFile()->FindResourceDescription(
			animation_number
		);
	Check(animationResource);
	animationResource->Lock();
	int *animation_data = (int*)animationResource->resourceAddress;
	Check_Pointer(animation_data);
	frameCount = animation_data[0];
	jointCount = animation_data[1];
	footStepThreshold = (float *)&animation_data[2];
	jointIndices = &animation_data[3];
	frameStart = (Scalar*)(jointIndices + jointCount);

	keyFrames = frameStart + frameCount;
	currentFrameTo = keyFrames;
	void* to;
	to = keyFrames;
	//
	//-----------------------------------
	// find the end of the keyframes data
	//-----------------------------------
	//
	for(int ii=0;ii<frameCount;++ii)
	for(int jj=0;jj<jointCount;++jj)
	{
		switch(jointSubToAnimate->GetJoint(jointIndices[jj])->GetJointType())
		{
			case Joint::BallTranslationJointType:
			{
				to = (void *)((char *)to+sizeof(EulerAngles));
				to = (void *)((char *)to+sizeof(Vector3D));
				break;
			}
			case Joint::HingeXJointType:
			case Joint::HingeYJointType:
			case Joint::HingeZJointType:
			{
				 to = (void *)((char *)to+sizeof(Hinge));
				break;
			}
			case Joint::BallJointType:
				to = (void *)((char *)to+sizeof(EulerAngles));
				break;
		}
	}

	//
	//
	// This should be contained in the keyframes
	//
	// keyJointPos = (Vector3D*)(keyFrames + jointCount*frameCount);

	rootTranslations = (Vector3D*)(to);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
AnimationInstance::~AnimationInstance()
{
	if (animationResource)
	{
		Check(animationResource);
		animationResource->Unlock();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	AnimationInstance::Animate(Scalar time_slice, Logical move_joints)
{
	Check(this);

	//
	//--------------------------------------------------------------------------
	// Find the frame to lerp towards.  As each frame boundary is crossed, set
	// the joints to the position of that boundary and adjust the remaining time
	// to lerp
	//--------------------------------------------------------------------------
	//
	int i;
	//
	//-----------------------------------------
	// This void * can point to any joint type
	//-----------------------------------------
	//
	void
		*to;


	Scalar frame_time = currentTime + time_slice;
	Scalar movement = 0.0f;

	//
	//-------------------------------------------------------------
	// to points to the place in the memory stream we left off at
	// last time we were on this loop
	//-------------------------------------------------------------
	//
	to = currentFrameTo;
	Joint *current_joint;

	while (currentFrame < frameCount)
	{
		if (frame_time < frameStart[currentFrame])
		{
			break;
		}
		for (i=0; i<jointCount; ++i)
		{
				current_joint = jointSubToAnimate->GetJoint(jointIndices[i]);
				Check(current_joint);
				switch(current_joint->GetJointType())
				{
					case Joint::BallTranslationJointType:
					{
						//
						//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
						// Only modify the joint if the value has changed
						//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
						//
						if (current_joint->GetEulerAngles() !=  *(EulerAngles*)to)
						{
							if (move_joints)
							{
								current_joint->SetRotation(*(EulerAngles*)to);
							}
						}
						to = (void *)((char *)to+sizeof(EulerAngles));

						if (current_joint->GetTranslation() !=  *(Point3D*)to)
						{
							if (move_joints)
							{
								current_joint->SetTranslation(*(Point3D*)to);
							}
						}

						to = (void *)((char *)to+sizeof(Vector3D));
						break;
					}
					case Joint::HingeXJointType:
					case Joint::HingeYJointType:
					case Joint::HingeZJointType:
					{
						if (current_joint->GetHinge() !=  *(Hinge*) to)
						{
							if (move_joints)
							{
								current_joint->SetHinge(*(Hinge*) to);
							}
						}
						to = (void *)((char *)to+sizeof(Hinge));
						break;
					}
					case Joint::BallJointType:

						if (current_joint->GetEulerAngles() !=  *(EulerAngles*)to)
						{
							if (move_joints)
							{
								current_joint->SetRotation(*(EulerAngles*)to);
							}
						}
						to = (void *)((char *)to+sizeof(EulerAngles));
						break;
					case Joint::StaticJointType:
						break;
				}
		}
		movement +=
			(frameStart[currentFrame] - currentTime)
			 * rootTranslations[currentFrame].z;
		currentTime = frameStart[currentFrame];
		++currentFrame;
	}

	//
	//----------------------------------------------------------------------
	// We we have run off the end of the animation, trigger the callback and
	// return
	//----------------------------------------------------------------------
	//
	if (currentFrame == frameCount)
	{

		movement +=
			(moverToAnimate->*finishedCallback)(
				animationResource->resourceID,
				frame_time - frameStart[frameCount - 1],
				move_joints
			);
		return movement;
	}

	currentFrameTo = to;
	//
	//-------------------------------------------------------
	// Set up to lerp the rotations between to two key frames
	//-------------------------------------------------------
	//
	Verify(!Small_Enough(frameStart[currentFrame] - currentTime));
	Scalar delta =
		(frame_time - currentTime)/(frameStart[currentFrame] - currentTime);

	for (i=0; i<jointCount; ++i)
	{
	  current_joint =
				jointSubToAnimate->GetJoint(jointIndices[i]);
	  Check(current_joint);
		switch(current_joint->GetJointType())
		{
			case Joint::BallTranslationJointType:
			{
				Point3D	 new_trans;
				EulerAngles new_angles;

				new_angles.Lerp(
					current_joint->GetEulerAngles(),
					*(EulerAngles*)to,
					delta
				);
				if (current_joint->GetEulerAngles() != new_angles)
				{
					if (move_joints)
					{
						current_joint->SetRotation(new_angles);
					}
				}
				to = (void *)((char *)to+sizeof(EulerAngles));

				new_trans.Lerp(
					current_joint->GetTranslation(),
					*(Vector3D*)to,
					delta
				);
				if (current_joint->GetTranslation() != new_trans)
				{
					if (move_joints)
					{
						current_joint->SetTranslation(new_trans);
					}
				}
				to = (void *)((char *)to+sizeof(Vector3D));
				break;
			}
			case    Joint::HingeZJointType:
			case    Joint::HingeYJointType:
			case    Joint::HingeXJointType:
			{
				Hinge new_hinge;
				new_hinge.Lerp(
						current_joint->GetHinge(),
						*(Hinge*)to,
						delta
					);
				if (current_joint->GetRadians() != new_hinge.rotationAmount)
				{
					if (move_joints)
					{
						current_joint->SetRotation(new_hinge.rotationAmount);
					}
				}
				to = (void *)((char *)to+sizeof(Hinge));

				break;
			}
			case Joint::BallJointType:
			{
				EulerAngles new_angles;
				new_angles.Lerp(
						current_joint->GetEulerAngles(),
						*(EulerAngles*)to,
						delta
					);
				if (current_joint->GetEulerAngles() != new_angles)
				{
					if (move_joints)
					{
						current_joint->SetRotation(new_angles);
					}
				}

				to = (void *)((char *)to+sizeof(EulerAngles));

				break;
			}
			case Joint::StaticJointType:
				break;
		}
	}
	movement += (frame_time - currentTime) * rootTranslations[currentFrame].z;
	currentTime = frame_time;
	return movement;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Logical
	AnimationInstance::LerpToPose(Scalar time_slice, Scalar time_to_lerp, Logical move_joints)
{
	Check(this);

	int i;
	//
	//-----------------------------------------
	// This void * can point to any joint type
	//-----------------------------------------
	//
	void
		*to;


	if (!lerpToZero)
	{
		lerpToZero = True;
		deltaTime = time_to_lerp;
	}

	Scalar delta = time_slice / deltaTime;


	//
	//-------------------------------------------------------------
	// to points to the place in the memory stream we left off at
	// last time we were on this loop
	//-------------------------------------------------------------
	//

	to = currentFrameTo;
	Joint *current_joint;

	if (move_joints)
	{
		for (i=0; i<jointCount; ++i)
		{
		  current_joint =
					jointSubToAnimate->GetJoint(jointIndices[i]);
		  Check(current_joint);
			switch(current_joint->GetJointType())
			{
				case Joint::BallTranslationJointType:
				{
					Point3D	 new_trans;
					EulerAngles new_angles;

					new_angles.Lerp(
						current_joint->GetEulerAngles(),
						*(EulerAngles*)to,
						delta
					);
					if (current_joint->GetEulerAngles() != new_angles)
					{
						current_joint->SetRotation(new_angles);
					}
					to = (void *)((char *)to+sizeof(EulerAngles));

					new_trans.Lerp(
						current_joint->GetTranslation(),
						*(Vector3D*)to,
						delta
					);
					if (current_joint->GetTranslation() != new_trans)
					{
						current_joint->SetTranslation(new_trans);
					}
					to = (void *)((char *)to+sizeof(Vector3D));
					break;
				}
				case    Joint::HingeZJointType:
				case    Joint::HingeYJointType:
				case    Joint::HingeXJointType:
				{
					Hinge new_hinge;
					new_hinge.Lerp(
							current_joint->GetHinge(),
							*(Hinge*)to,
							delta
						);
					if (current_joint->GetRadians() != new_hinge.rotationAmount)
					{
						current_joint->SetRotation(new_hinge.rotationAmount);
					}
					to = (void *)((char *)to+sizeof(Hinge));

					break;
				}
				case Joint::BallJointType:
				{
					EulerAngles new_angles;
					new_angles.Lerp(
							current_joint->GetEulerAngles(),
							*(EulerAngles*)to,
							delta
						);
					if (current_joint->GetEulerAngles() != new_angles)
					{
						current_joint->SetRotation(new_angles);
					}

					to = (void *)((char *)to+sizeof(EulerAngles));

					break;
				}
				case Joint::StaticJointType:
					break;
			}
		}

	}

	deltaTime -= time_slice;
	if (deltaTime <= 0.0f)
	{
		lerpToZero = False;
		return False;
	}
	return True;

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

Logical
	AnimationInstance::LerpToRest(Scalar time_slice, Scalar time_to_lerp, Logical move_joints)
{


	if (!lerpToZero)
	{
		lerpToZero = True;
		deltaTime = time_to_lerp;
	}

	Scalar delta = time_slice / deltaTime;

	if (move_joints)
	{
		EulerAngles zero_euler(0.0f, 0.0f, 0.0f);
		Hinge zero_hingeX(0.0f, X_Axis);
		Hinge zero_hingeY(0.0f, Y_Axis);
		Hinge zero_hingeZ(0.0f, Z_Axis);
		Vector3D zero_vector(0.0f, 0.0f, 0.0f);

		for (int i=0; i<jointCount; ++i)
		{
			Joint  *current_joint =
					jointSubToAnimate->GetJoint(jointIndices[i]);
			Check(current_joint);
			switch(current_joint->GetJointType())
			{
				case Joint::BallTranslationJointType:
				{
					EulerAngles new_angles;
					new_angles.Lerp(
						current_joint->GetEulerAngles(),
						zero_euler,
						delta
					);
					if (current_joint->GetEulerAngles() != new_angles)
					{
						current_joint->SetRotation(new_angles);
					}

					Point3D	new_trans;

					new_trans.Lerp(
						current_joint->GetTranslation(),
						zero_vector,
						delta
					);
					if (current_joint->GetTranslation() != new_trans)
					{
						current_joint->SetTranslation(new_trans);
					}
					break;
				}
				case    Joint::HingeXJointType:
				{
					Hinge new_hinge;

					new_hinge.Lerp(
							current_joint->GetHinge(),
							zero_hingeX,
							delta
						);
					if(current_joint->GetRadians() != new_hinge.rotationAmount)
					{
						current_joint->SetRotation(new_hinge.rotationAmount);
					}
					break;

				}
				case    Joint::HingeYJointType:
				{
					Hinge new_hinge;
					new_hinge.Lerp(
							current_joint->GetHinge(),
							zero_hingeY,
							delta
						);
					if(current_joint->GetRadians() != new_hinge.rotationAmount)
					{
						current_joint->SetRotation(new_hinge.rotationAmount);
					}
					break;
				}
				case    Joint::HingeZJointType:
				{
					Hinge new_hinge;
					new_hinge.Lerp(
							current_joint->GetHinge(),
							zero_hingeZ,
							delta
						);
					if(current_joint->GetRadians() != new_hinge.rotationAmount)
					{
						current_joint->SetRotation(new_hinge.rotationAmount);
					}
					break;
				}
				case Joint::BallJointType:
				{
					EulerAngles new_angles;

					new_angles.Lerp(
							current_joint->GetEulerAngles(),
							zero_euler,
							delta
						);
					if (current_joint->GetEulerAngles() != new_angles)
					{
						current_joint->SetRotation(new_angles);
					}

					break;
				}
				case Joint::StaticJointType:
					break;
			}
		}
	}
	deltaTime -= time_slice;
	if (deltaTime <= 0.0f)
	{
		lerpToZero = False;
		return False;
	}
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

void
	AnimationInstance::SnapToRest(Logical move_joints)
{

	if (!move_joints)
	{
		return;
	}

	EulerAngles zero_euler(0.0f, 0.0f, 0.0f);
	Hinge zero_hingeX(0.0f, X_Axis);
	Hinge zero_hingeY(0.0f, Y_Axis);
	Hinge zero_hingeZ(0.0f, Z_Axis);
	Point3D zero_point(0.0f, 0.0f, 0.0f);

	for (int i=0; i<jointCount; ++i)
	{
		Joint   *current_joint =
				jointSubToAnimate->GetJoint(jointIndices[i]);
		Check(current_joint);
		switch(current_joint->GetJointType())
		{
			case Joint::BallTranslationJointType:
			{
				current_joint->SetRotation(zero_euler);
				current_joint->SetTranslation(zero_point);
				break;
			}
			case    Joint::HingeXJointType:
			{
				current_joint->SetHinge(zero_hingeX);
				break;
			}
			case    Joint::HingeYJointType:
			{
				current_joint->SetHinge(zero_hingeY);
				break;
			}
			case    Joint::HingeZJointType:
			{
				current_joint->SetHinge(zero_hingeZ);
				break;
			}
			case Joint::BallJointType:
			{
				current_joint->SetRotation(zero_euler);
				break;
			}
			case Joint::StaticJointType:
				break;
		}
	}
}
//##########################################################################
//##############    Sketon Notation file utilities    ####################
//##########################################################################


int
	Get_Active_Joint_Count(NotationFile *skl_file)
{
	NameList *namelist = skl_file->MakePageList("joint");
	Register_Object(namelist);
	NameList::Entry *entry = namelist->GetFirstEntry();

	int count = 0;

	while (entry)
	{
		++count;
		entry = entry->GetNextEntry();
	}

	Unregister_Object(namelist);
	delete namelist;

	return count;
}

Logical
	JointedMover::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}
