// Copyright (C) 2002-2004 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in Irrlicht.h

#include "CXAnimationPlayer.h"
#include "ISceneNode.h"
#include "IVideoDriver.h"
#include "os.h"
#include "SMeshBuffer.h"
#include "IMeshManipulator.h"

#include <stdio.h>

namespace irr
{
namespace scene
{

//! constructor
CXAnimationPlayer::CXAnimationPlayer(CXFileReader* reader,
									 video::IVideoDriver* driver,
									 IMeshManipulator* manip,
									 const c8* filename)
: Reader(reader), Driver(driver), AnimatedMesh(0),
Manipulator(manip), CurrentAnimationTime(0.0f), LastAnimationTime(1.0f),
	CurrentAnimationSet(0), IsAnimatedSkinnedMesh(false), DebugSkeletonCrossSize(1.0f)
{
	FileName = filename;

	if (!Reader)
		return;

	if (Driver)
		Driver->grab();

	if (Manipulator)
		Manipulator->grab();

	Reader->grab();

	createAnimationData();
}



//! destructor
CXAnimationPlayer::~CXAnimationPlayer()
{
	if (Reader)
		Reader->drop();

	if (Driver)
		Driver->drop();

	if (AnimatedMesh)
		AnimatedMesh->drop();

	if (Manipulator)
		Manipulator->drop();
}



//! Gets the frame count of the animated mesh.
s32 CXAnimationPlayer::getFrameCount()
{
	return IsAnimatedSkinnedMesh ? (s32)LastAnimationTime : 1;
}



//! Returns the IMesh interface for a frame.
IMesh* CXAnimationPlayer::getMesh(s32 frame, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
	if (!IsAnimatedSkinnedMesh)
		return &OriginalMesh;

	CurrentAnimationTime = (f32)frame;
	animateSkeleton();
	modifySkin();
	updateBoundingBoxFromAnimation();
	return AnimatedMesh;
}



//! Returns an axis aligned bounding box of the mesh.
const core::aabbox3d<f32>& CXAnimationPlayer::getBoundingBox() const
{
	return Box;
}



//! Returns the type of the animated mesh.
EANIMATED_MESH_TYPE CXAnimationPlayer::getMeshType() const
{
	return EAMT_X;
}



void CXAnimationPlayer::createAnimationData()
{
	// get joints from x-file
	createJointData(&Reader->getRootFrame(), -1);

	createMeshData();

	if (IsAnimatedSkinnedMesh && AnimatedMesh)
	{
		animateSkeleton();
		modifySkin();	
		updateBoundingBoxFromAnimation();
		DebugSkeletonCrossSize = AnimatedMesh->getBoundingBox().getExtend().X / 20.0f;
	}
	else
		DebugSkeletonCrossSize = OriginalMesh.getBoundingBox().getExtend().X / 20.0f;
}


void CXAnimationPlayer::createMeshData()
{
	// create mesh
	addFrameToMesh(&Reader->getRootFrame());

	// recalulate box
	OriginalMesh.recalculateBoundingBox();

	// sort weights in joints
	for (s32 j=0; j<(s32)Joints.size(); ++j)
		Joints[j].Weights.sort();

	// copy mesh
	AnimatedMesh = Manipulator->createMeshCopy(&OriginalMesh);

	// create and link animation data
	prepareAnimationData();

	// first animation
	animateSkeleton();
	modifySkin();
}


void CXAnimationPlayer::addFrameToMesh(CXFileReader::SXFrame* frame)
{
	// go through all meshes
	for (u32 m=0; m<frame->Meshes.size(); ++m)
	{
		// create mesh buffer for every material
		if (frame->Meshes[m].MaterialList.Materials.empty())
			os::Printer::log("Mesh without material found in x file.", ELL_WARNING);

		for (u32 mt=0; mt<frame->Meshes[m].MaterialList.Materials.size(); ++mt)
		{
			// create buffer
			SMeshBuffer *buf = new SMeshBuffer();
			OriginalMesh.addMeshBuffer(buf);
			buf->drop();

			// new weights buffer
			Weights.push_back( core::array<SVertexWeight>() );

			// create material
			buf->Material = getMaterialFromXMaterial(
				frame->Meshes[m].MaterialList.Materials[mt]);

			// add all faces of this material
			addFacesToBuffer(OriginalMesh.MeshBuffers.size()-1,
				frame->Meshes[m], mt, frame);
			buf->recalculateBoundingBox();
		}
	}

	// add child frames
	for (u32 c=0; c<frame->ChildFrames.size(); ++c)
		addFrameToMesh(&frame->ChildFrames[c]);
}



video::SMaterial CXAnimationPlayer::getMaterialFromXMaterial(CXFileReader::SXMaterial& xmat)
{
	video::SMaterial mat;
	mat.EmissiveColor = xmat.Emissive.toSColor();
	mat.DiffuseColor = xmat.FaceColor.toSColor();
	mat.SpecularColor = xmat.Specular.toSColor();
	mat.Shininess = xmat.Power;
	
	//if (mat.Shininess > core::ROUNDING_ERROR)
	//	mat. TODO: enable specular highlights

	if (xmat.TextureFileName.size() != 0)
	{
		mat.Texture1 = Driver->getTexture(getTextureFileName(xmat.TextureFileName).c_str());
		if (mat.Texture1 == 0)
			mat.Texture1 = Driver->getTexture(xmat.TextureFileName.c_str());
	}

	return mat;
}



void CXAnimationPlayer::addFacesToBuffer(s32 meshbuffernr, CXFileReader::SXMesh& mesh, s32 matnr, CXFileReader::SXFrame* frame)
{
	scene::SMeshBuffer* buf = (SMeshBuffer*)OriginalMesh.MeshBuffers[meshbuffernr];

	s32 vcnt = mesh.Vertices.size();
	s32 tcnt = mesh.TextureCoords.size();
	s32 ncnt = mesh.Normals.size();
	s32 fcnt = mesh.Indices.size();

	// precompute which joint belongs to which weight array
	core::array< s32 > jointNumberWeightNumberMap;
	for (s32 w=0; w<(s32)mesh.SkinWeights.size(); ++w)
	{
		s32 jnr = getJointNumberFromName(mesh.SkinWeights[w].TransformNodeName);
		if (jnr == -1)
			os::Printer::log("Unknown Joint referenced in x file", 
				mesh.SkinWeights[w].TransformNodeName.c_str(), ELL_WARNING);
		else
		{
			Joints[jnr].MatrixOffset = mesh.SkinWeights[w].MatrixOffset;
			IsAnimatedSkinnedMesh = true;
		}

		jointNumberWeightNumberMap.push_back(jnr);
	}


	video::S3DVertex v;
	v.Color = 0xFFFFFFFF;

	// add only those with material matnr
	for (s32 i=0; i<(s32)mesh.MaterialList.FaceIndices.size(); ++i)
		if (mesh.MaterialList.FaceIndices[i] == matnr)
		{
			// add face number i
			for (s32 f=0; f<3; ++f)
			{
				s32 idxidx = i*3+f;
				s32 idx = mesh.Indices[idxidx];	

				v.Pos = mesh.Vertices[idx];

				if (tcnt) v.TCoords = mesh.TextureCoords[idx];
				if (ncnt) v.Normal = mesh.Normals[mesh.NormalIndices[idxidx]];

				s32 nidx = buf->Vertices.linear_reverse_search(v);
				bool alreadyIn = (nidx != -1);

				if (!alreadyIn)
				{
					nidx = buf->Vertices.size();
					buf->Indices.push_back(nidx);
					buf->Vertices.push_back(v);		
					Weights[meshbuffernr].push_back(SVertexWeight());
				}
				else
					buf->Indices.push_back(nidx);

				bool isWeighted = alreadyIn;

				// add weight data of this vertex to all joints
				if (!alreadyIn)
				for (s32 w=0; w<(s32)mesh.SkinWeights.size(); ++w)
				{
					s32 vertexinweights = mesh.SkinWeights[w].Weights.binary_search(CXFileReader::SXWeight(idx));
					
					if (vertexinweights != -1)
					{
						s32 jnr = jointNumberWeightNumberMap[w];

                        if (jnr != -1)
						{
							// weight per joint
							Joints[jnr].Weights.push_back(SWeightData(
								meshbuffernr,
								nidx,
								mesh.SkinWeights[w].Weights[vertexinweights].Weight));

							// weight per vertex
							SVertexWeight& weight = Weights[meshbuffernr].pointer()[nidx];
							weight.add(mesh.SkinWeights[w].Weights[vertexinweights].Weight, jnr);

							isWeighted = true;
						}
					}
				} // end for all weights

				// if this vertex does not have a weight, create a virtual new 
				// joint and attach it to this one
				if (!isWeighted)
					addVirtualWeight(meshbuffernr, nidx, mesh, frame);

			} // end for all faces

		} // end for all materials

	// generate missing normals
	if (!ncnt)
	{
		video::S3DVertex* v = &buf->Vertices[0];
		fcnt = buf->Indices.size();
		for (s32 u=0; u<fcnt; u+=3)
		{
			core::plane3d<f32> p(v[buf->Indices[u+0]].Pos,
				v[buf->Indices[u+1]].Pos, v[buf->Indices[u+2]].Pos);
			p.Normal.normalize();
			v[buf->Indices[u+0]].Normal = p.Normal;
			v[buf->Indices[u+1]].Normal = p.Normal;
			v[buf->Indices[u+2]].Normal = p.Normal;
		}
	}

	// transform vertices and normals
	/*core::matrix4 mat = frame->LocalMatrix;
	mat.makeInverse();

	vcnt = buf->Vertices.size();
	for (s32 u=0; u<vcnt; ++u)
	{
		mat.transformVect(buf->Vertices[u].Pos);
		mat.inverseRotateVect(buf->Vertices[u].Normal);
	}*/
}


void CXAnimationPlayer::addVirtualWeight(s32 meshbuffernr, s32 vtxidx, 
										CXFileReader::SXMesh& mesh,
										CXFileReader::SXFrame* frame)
{
	// find original joint of vertex

	s32 jnr = getJointNumberFromName(frame->Name);
	if (jnr == -1)
		return;

	// weight per joint
	Joints[jnr].Weights.push_back(SWeightData(
		meshbuffernr,
		vtxidx,
		1.0f));

	// weight per vertex
	SVertexWeight& weight = Weights[meshbuffernr].pointer()[vtxidx];
	weight.add(1.0f, jnr);
}



s32 CXAnimationPlayer::getJointNumberFromName(core::stringc& name)
{
	for (s32 i=0; i<(s32)Joints.size(); ++i)
		if (Joints[i].Name == name)
			return i;

	return -1;
}



core::stringc CXAnimationPlayer::getTextureFileName(core::stringc texture)
{
	s32 idx = -1;
	idx = FileName.findLast('/');

    if (idx == -1)
		idx = FileName.findLast('\\');

	if (idx == -1)
		return core::stringc();

	core::stringc p = FileName.subString(0, idx+1);
	p.append(texture);
	return p;
}



void CXAnimationPlayer::createJointData(CXFileReader::SXFrame* f, s32 JointParent)
{
	// add joint

	s32 index = Joints.size();
	Joints.push_back(SJoint());
	SJoint& j = Joints.getLast();

	j.Parent = JointParent;
	j.GlobalMatrix = f->GlobalMatrix;
	j.LocalMatrix = f->LocalMatrix;
	j.AnimatedMatrix = j.GlobalMatrix;
	j.LocalAnimatedMatrix = j.LocalMatrix;
	j.CombinedAnimationMatrix = j.AnimatedMatrix * j.MatrixOffset;
	j.IsVirtualJoint = false;

	j.Name = f->Name;

	// add all children
	for (s32 i=0; i<(s32)f->ChildFrames.size(); ++i)
		createJointData(&f->ChildFrames[i], index);
}


//! Returns a pointer to a transformation matrix
core::matrix4* CXAnimationPlayer::getMatrixOfJoint(s32 jointNumber, s32 frame)
{
	if (jointNumber < 0 || jointNumber >= (s32)Joints.size())
		return 0;

	return &Joints[jointNumber].CombinedAnimationMatrix;
}


//! Gets joint count.
s32 CXAnimationPlayer::getJointCount() const
{
	return Joints.size();
}


//! Gets the name of a joint.
const c8* CXAnimationPlayer::getJointName(s32 number) const
{
	if (number<0 || number>=(s32)Joints.size())
		return 0;

	return Joints[number].Name.c_str();
}


//! Gets a joint number from its name
s32 CXAnimationPlayer::getJointNumber(const c8* name) const
{
	for (s32 i=0; i<(s32)Joints.size(); ++i)
		if (Joints[i].Name == name)
			return i;

	return -1;
}


//! Returns a pointer to list of points containing the skeleton.
const core::array<core::vector3df>* CXAnimationPlayer::getDrawableSkeleton(s32 frame)
{
	DebugSkeleton.clear();
	
	f32 k = DebugSkeletonCrossSize;
	f32 p = 0.0f;
	
	for (s32 i=0; i<(s32)Joints.size(); ++i)
	{
		core::vector3df start(p,p,p);
		core::vector3df end(p,p,p);

		Joints[i].AnimatedMatrix.transformVect(start);

		DebugSkeleton.push_back(start);
		DebugSkeleton.push_back(start + core::vector3df(0,k,0));
		DebugSkeleton.push_back(start);
		DebugSkeleton.push_back(start + core::vector3df(0,-k,0));
		DebugSkeleton.push_back(start);
		DebugSkeleton.push_back(start + core::vector3df(k,0,0));
		DebugSkeleton.push_back(start);
		DebugSkeleton.push_back(start + core::vector3df(-k,0,0));
		DebugSkeleton.push_back(start);
		DebugSkeleton.push_back(start + core::vector3df(0,0,k));
		DebugSkeleton.push_back(start);
		DebugSkeleton.push_back(start + core::vector3df(0,0,-k));

		if (Joints[i].Parent != -1)
		{
			Joints[Joints[i].Parent].AnimatedMatrix.transformVect(end);

			DebugSkeleton.push_back(end);
			DebugSkeleton.push_back(start);
		}
	}

	return &DebugSkeleton;
}


//! animates the skeleton based on the animation data
void CXAnimationPlayer::animateSkeleton()
{
	s32 i;

	if (!AnimationSets.empty())
	{

		// reset joints
		for (s32 jii=0; jii<(s32)Joints.size(); ++jii)
		{
			Joints[jii].LocalAnimatedMatrix.makeIdentity();
			Joints[jii].WasAnimatedThisFrame = false;
		}

		SXAnimationSet& currentSet = AnimationSets[CurrentAnimationSet];

		// go through all animation tracks
		for (i=0; i<(s32)currentSet.Animations.size(); ++i)
		{
			SJoint& joint = Joints[currentSet.Animations[i].jointNr];

			// find indexes to interpolate between
			s32 idx1 = -1;
			s32 idx2 = -1;

			for (int t=0; t<(s32)currentSet.Animations[i].Times.size()-1; ++t)
				if (currentSet.Animations[i].Times[t] <= CurrentAnimationTime &&
					currentSet.Animations[i].Times[t+1] >= CurrentAnimationTime)
				{
					idx1 = t; 
					idx2 = (t+1) % currentSet.Animations[i].Times.size();
					break;
				}

			if (idx1 == -1 || idx2 == -1)
				continue;

			// calculate interpolation factor
			f32 factor = (CurrentAnimationTime - currentSet.Animations[i].Times[idx1]) /
				(currentSet.Animations[i].Times[idx2] - currentSet.Animations[i].Times[idx1]);

			// animate it
			switch(currentSet.Animations[i].keyType)
			{
			case 0: // rotation
				{	
					// with this code, rotations are not 100% ok, they are 
					// mirrored.
					core::quaternion q;
					q.Slerp(currentSet.Animations[i].Quaternions[idx1],
							currentSet.Animations[i].Quaternions[idx2], 
							factor);

					joint.LocalAnimatedMatrix *= q.getMatrix();
					joint.WasAnimatedThisFrame = true;
				}
				break;
			case 1: // scale
				{
					core::matrix4 mat1, mat2;
					mat1.setScale(currentSet.Animations[i].Vectors[idx1]);
					mat2.setScale(currentSet.Animations[i].Vectors[idx2]);

					joint.LocalAnimatedMatrix *= mat1.interpolate(mat2, factor);
					joint.WasAnimatedThisFrame = true;
				}
				break;
			case 2: // position
				{
					core::matrix4 mat1, mat2;
					mat1.setTranslation(currentSet.Animations[i].Vectors[idx1]);
					mat2.setTranslation(currentSet.Animations[i].Vectors[idx2]);

					joint.LocalAnimatedMatrix *= mat1.interpolate(mat2, factor);
					joint.WasAnimatedThisFrame = true;
				}
				break;
			case 4:
			case 3: // matrix
				{
					joint.LocalAnimatedMatrix = 
						currentSet.Animations[i].Matrices[idx1].interpolate(
						currentSet.Animations[i].Matrices[idx2], factor);

					joint.WasAnimatedThisFrame = true;
				}
				break;
			}

		}
	}


	// update all joints
	for (s32 ji=0; ji<(s32)Joints.size(); ++ji)
	{
		if (!Joints[ji].WasAnimatedThisFrame)
			Joints[ji].LocalAnimatedMatrix = Joints[ji].LocalMatrix;

        Joints[ji].AnimatedMatrix = Joints[ji].LocalAnimatedMatrix;

		if (Joints[ji].Parent != -1)
			Joints[ji].AnimatedMatrix = Joints[Joints[ji].Parent].AnimatedMatrix * Joints[ji].AnimatedMatrix;

		Joints[ji].CombinedAnimationMatrix = Joints[ji].AnimatedMatrix * 
			Joints[ji].MatrixOffset;
	}
}


//! modifies the skin based on the animated skeleton
void CXAnimationPlayer::modifySkin()
{
	// set animated vertices to zero
	for (s32 k=0; k<(s32)Joints.size(); ++k)
	{
		for (s32 w=0; w<(s32)Joints[k].Weights.size(); ++w)
		{
			SWeightData& wd = Joints[k].Weights[w];

#ifdef _XREADER_DEBUG
			if (wd.buffer >= AnimatedMesh->getMeshBufferCount() ||
				wd.vertex >= AnimatedMesh->getMeshBuffer(wd.buffer)->getVertexCount())
				os::Printer::log("CXAnimationPlayer: Invalid Weights");
#endif
			
			video::S3DVertex* nv = (video::S3DVertex*)AnimatedMesh->getMeshBuffer(wd.buffer)->getVertices();
            nv[wd.vertex].Pos.set(0,0,0);
		}
	}


#ifdef _XREADER_DEBUG
	bool somethingIsWrong = false;

	// check if all vertices are set to zero
	for (s32 mb=0; mb<AnimatedMesh->getMeshBufferCount(); ++mb)
	{
		video::S3DVertex* v = (video::S3DVertex*)AnimatedMesh->getMeshBuffer(mb)->getVertices();
		s32 c = AnimatedMesh->getMeshBuffer(mb)->getVertexCount();
		for (s32 vt=0; vt<c; ++vt)
			if (v[vt].Pos != core::vector3df(0,0,0))
			{
				char tmp[255];
				sprintf(tmp, "CXAnimationPlayer: Vertex not null. Buf:%d Vtx:%d", mb, vt);
				// this can happen if something is wrong with the vertex weights,
				// not all vertices have a weight attached.
				os::Printer::log(tmp);
				somethingIsWrong = true;
			}
	}

	if (somethingIsWrong)
	{
		// write out mesh informations
		char tmp[255];
		sprintf(tmp, "CXAnimationPlayer: Meshbuffers:%d", AnimatedMesh->getMeshBufferCount());
		os::Printer::log(tmp);

		for (s32 mb=0; mb<AnimatedMesh->getMeshBufferCount(); ++mb)
		{
			sprintf(tmp, "CXAnimationPlayer: Meshbuffer #%d: %d vertices", mb, AnimatedMesh->getMeshBuffer(mb)->getVertexCount());	
			os::Printer::log(tmp);
		}
	}
#endif

	// transform vertices
	for (s32 mb=0; mb<AnimatedMesh->getMeshBufferCount(); ++mb)
	{
		video::S3DVertex* av = (video::S3DVertex*)AnimatedMesh->getMeshBuffer(mb)->getVertices();
		video::S3DVertex* ov = (video::S3DVertex*)OriginalMesh.getMeshBuffer(mb)->getVertices();
		s32 c = AnimatedMesh->getMeshBuffer(mb)->getVertexCount();
		for (s32 vt=0; vt<c; ++vt)
		{
			core::vector3df vtmp;
			core::vector3df orig = ov[vt].Pos;
			SVertexWeight& weight = Weights[mb].pointer()[vt];

			av[vt].Pos = core::vector3df(0,0,0);

			/*if (weight.weightCount == 0)
			{
				av[vt].Pos = ov[vt].Pos;
			}
			else*/
			for (int w=0; w<weight.weightCount; ++w)
			{
				Joints[weight.joint[w]].CombinedAnimationMatrix.transformVect(
					orig, vtmp);

				/*core::matrix4 mat;
				mat = Joints[weight.joint[w]].GlobalMatrix;
				mat *= Joints[weight.joint[w]].MatrixOffset;
				mat.transformVect(orig, vtmp);*/

				vtmp *= weight.weight[w];
				av[vt].Pos += vtmp;
			}

			
		}
	}

	// transform vertices
	/*core::vector3df vtmp;
	for (s32 k=0; k<(s32)Joints.size(); ++k)
	{

		for (s32 w=0; w<(s32)Joints[k].Weights.size(); ++w)
		{
			SWeightData& wd = Joints[k].Weights[w];
			video::S3DVertex* nv = (video::S3DVertex*)AnimatedMesh->getMeshBuffer(wd.buffer)->getVertices();
			video::S3DVertex* ov = (video::S3DVertex*)OriginalMesh.getMeshBuffer(	wd.buffer)->getVertices();

			core::matrix4 m = Joints[k].XFrame->GlobalMatrix;
			m *= Joints[k].MatrixOffset;

			m.transformVect(ov[wd.vertex].Pos, vtmp);

			vtmp *= wd.weight;
			nv[wd.vertex].Pos += vtmp;
			
		}
	}*/

}


//! prepares animation data which was read in from the .x file
void CXAnimationPlayer::prepareAnimationData()
{
    s32 animationSetCount = Reader->getAnimationSetCount();
	for (int i=0; i<animationSetCount; ++i)
	{
		AnimationSets.push_back(SXAnimationSet());
		SXAnimationSet& mySet = AnimationSets.getLast();

		CXFileReader::SXAnimationSet& readerSet = Reader->getAnimationSet(i);
		mySet.AnimationName = readerSet.AnimationName;

		// trough all animations
		for (int a=0; a<(s32)readerSet.Animations.size(); ++a)
		{
			// trough all keys
			for (int k=0; k<(s32)readerSet.Animations[a].Keys.size(); ++k)
			{
				// link with joint
				int jntnr = getJointNumberFromName(readerSet.Animations[a].FrameName);

				if (jntnr == -1)
				{
					os::Printer::log(
						"CXAnimationPlayer: Animationtrack without corresponding joint found", 
						readerSet.Animations[a].FrameName.c_str());
					continue;
				}

				// add new track
				mySet.Animations.push_back(SXAnimationTrack());
				SXAnimationTrack& myTrack = mySet.Animations.getLast();
				myTrack.jointNr = jntnr;
				IsAnimatedSkinnedMesh = true;

				// copy track
				s32 keyCount = (s32)readerSet.Animations[a].Keys[k].numberOfKeys;
				s32 type = readerSet.Animations[a].Keys[k].keyType;
				s32 i;
				myTrack.keyType = type;

		#ifdef _XREADER_DEBUG
				char tmp[255];
				sprintf(tmp, "adding track %s with %d keys, type:%d", readerSet.Animations[a].FrameName.c_str(), keyCount, type);
				os::Printer::log(tmp);
		#endif

				switch(type)
				{
				case 0: // quaternion
					for (i=0; i<keyCount; ++i)
						myTrack.Quaternions.push_back(readerSet.Animations[a].Keys[k].getQuaternion(i));
					break;
				case 1: // scale
				case 2: // position
					for (i=0; i<keyCount; ++i)
						myTrack.Vectors.push_back(readerSet.Animations[a].Keys[k].getVector(i));
					break;
				case 4:
				case 3: // matrix
					for (i=0; i<keyCount; ++i)
						myTrack.Matrices.push_back(readerSet.Animations[a].Keys[k].getMatrix(i));
					break;
				}

				// copy times
				for (i=0; i<keyCount; ++i)
					myTrack.Times.push_back((f32)readerSet.Animations[a].Keys[k].time[i]);

				if (myTrack.Times.getLast() > LastAnimationTime)
					LastAnimationTime = myTrack.Times.getLast();
			}
			
		}

		// sort animation tracks
		mySet.Animations.sort();
	}
}

void CXAnimationPlayer::updateBoundingBoxFromAnimation()
{
	if (!Joints.size())
		return;

	bool first = true;

	for (s32 i=1; i<(s32)Joints.size(); ++i)
		if (!Joints[i].Weights.empty())
		{
			core::vector3df p(0,0,0);
			Joints[i].AnimatedMatrix.transformVect(p);

			if (first)
				Box.reset(p);
			else
				Box.addInternalPoint(p);

			first = false;
		}

	AnimatedMesh->BoundingBox = Box;

}

//! Returns amount of animations in .X-file.
s32 CXAnimationPlayer::getAnimationCount() const
{
	return AnimationSets.size();
}

//! Returns the name of an animation. 
const c8* CXAnimationPlayer::getAnimationName(s32 idx) const
{
	if (idx < 0 || idx >= (s32)AnimationSets.size())
		return 0;

	return AnimationSets[idx].AnimationName.c_str();
}


//! Sets an animation as animation to play back.
void CXAnimationPlayer::setCurrentAnimation(s32 idx)
{
	if (idx < 0 || idx >= (s32)AnimationSets.size())
		return;

	CurrentAnimationSet = idx;
}


//! Sets an animation as animation to play back.
bool CXAnimationPlayer::setCurrentAnimation(const c8* name)
{
	for (s32 i=0; i<(s32)AnimationSets.size(); ++i)
		if (AnimationSets[i].AnimationName == name)
		{
			CurrentAnimationSet = i;
			return true;
		}

	return false;
}


} // end namespace scene
} // end namespace irr

