
#define WIN32_LEAN_AND_MEAN

#include "msDexiExporter.hpp"
#include "tinyxml/tinyxml.h"
#include "msLib/msLib.h"

#pragma warning (push, 3)
#include <windows.h>
#include <commdlg.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>

#include <algorithm>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <string>
#pragma warning (pop)

#pragma warning (disable : 4100)

static HINSTANCE s_DllInstance = 0;
static HANDLE StdOut = 0;
static bool consoleActive = false;
const char* const cDexiPlugin::sMenuTitle = "Destiny3D DEXI...";
const char* const cDexiPlugin::sLongTitle = "Destiny3D DEXI Exporter";
const double fuzzVal = 0.00001;

BOOL APIENTRY DllMain( HANDLE hModule, 
					  DWORD  ul_reason_for_call, 
					  LPVOID lpReserved
					  )
{
    switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		// this is needed for loading the resources for the options dialog
		s_DllInstance = (HINSTANCE)hModule;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
    }
    return TRUE;
}


cMsPlugIn*
CreatePlugIn ()
{
    return new cDexiPlugin ();
}


cDexiPlugin::cDexiPlugin ()
{
}


cDexiPlugin::~cDexiPlugin ()
{
}


int
cDexiPlugin::GetType ()
{
    return cMsPlugIn::eTypeExport;
}


const char*
cDexiPlugin::GetTitle ()
{
    return sMenuTitle;
}


static void consoleOut(const char* format, ...)
{
	if (!consoleActive)
		return;

	static char buffer[2048];
	va_list arglist;
	DWORD written;
	
	va_start(arglist,format);
	//	vprintf(text, arglist);
	_vsnprintf(buffer, 2048, format, arglist);
	va_end(arglist);
	buffer[2047] = '\0';
	
	WriteConsole(StdOut, buffer, strlen(buffer), &written, NULL);
	
	return;
}

static void setFloat(TiXmlElement* pElem, const char* name, double val)
{
	char buffer[50];
	if (-fuzzVal <= val && val <= fuzzVal)
		val = 0.0;
	sprintf(buffer, "%g", val);
	pElem->SetAttribute(name, buffer);
}

static void addColorElem(TiXmlElement* parentElem, float r, float g, float b, float* a = NULL)
{
	TiXmlElement* colorElem = parentElem->InsertEndChild(TiXmlElement("color"))->ToElement();
	colorElem->SetAttribute("float", 1);
	setFloat(colorElem, "r", r);
	setFloat(colorElem, "g", g);
	setFloat(colorElem, "b", b);
	if (a)
		setFloat(colorElem, "a", *a);
}

static void addPosElem(TiXmlElement* parentElem, float x, float y, float z)
{
	TiXmlElement* colorElem = parentElem->InsertEndChild(TiXmlElement("position"))->ToElement();
	setFloat(colorElem, "x", x);
	setFloat(colorElem, "y", y);
	setFloat(colorElem, "z", z);
}

static void addNormalElem(TiXmlElement* parentElem, float nx, float ny, float nz)
{
	TiXmlElement* colorElem = parentElem->InsertEndChild(TiXmlElement("normal"))->ToElement();
	setFloat(colorElem, "nx", nx);
	setFloat(colorElem, "ny", ny);
	setFloat(colorElem, "nz", nz);
}

static void addTexCoordElem(TiXmlElement* parentElem, float u, float* v = NULL, float* w = NULL)
{
	TiXmlElement* colorElem = parentElem->InsertEndChild(TiXmlElement("texcoord"))->ToElement();
	setFloat(colorElem, "u", u);
	setFloat(colorElem, "v", v ? *v : 0);
	setFloat(colorElem, "w", w ? *w : 0);
}

static void addRotationFromEuler(TiXmlElement* parentElem, float fPitch, float fYaw, float fRoll)
{
	double X,Y,Z,W;
	const double fSinPitch(sin(fPitch*0.5F));
	const double fCosPitch(cos(fPitch*0.5F));
	const double fSinYaw(sin(fYaw*0.5F));
	const double fCosYaw(cos(fYaw*0.5F));
	const double fSinRoll(sin(fRoll*0.5F));
	const double fCosRoll(cos(fRoll*0.5F));
	const double fCosPitchCosYaw(fCosPitch*fCosYaw);
	const double fSinPitchSinYaw(fSinPitch*fSinYaw);
	X = fSinRoll * fCosPitchCosYaw     - fCosRoll * fSinPitchSinYaw;
	Y = fCosRoll * fSinPitch * fCosYaw + fSinRoll * fCosPitch * fSinYaw;
	Z = fCosRoll * fCosPitch * fSinYaw - fSinRoll * fSinPitch * fCosYaw;
	W = fCosRoll * fCosPitchCosYaw     + fSinRoll * fSinPitchSinYaw;
	TiXmlElement* rotElem = parentElem->InsertEndChild(TiXmlElement("rotation"))->ToElement();
	setFloat(rotElem, "x", X);
	setFloat(rotElem, "y", Y);
	setFloat(rotElem, "z", Z);
	setFloat(rotElem, "w", W);
}

struct vector3
{
public:
	float x,y,z;
	vector3() { }
	vector3(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { }
	vector3(float f[3]) : x(f[0]), y(f[1]), z(f[2]) { }
	inline bool operator==(const vector3& ref) const
	{
		if (ref.x - fuzzVal <= x && x <= ref.x + fuzzVal &&
			ref.y - fuzzVal <= y && y <= ref.y + fuzzVal &&
			ref.z - fuzzVal <= z && z <= ref.z + fuzzVal)
			return true;
		return false;
	}
};
struct fullvertex
{
public:
	int posidx;
	vector3 norm;
	vector3 texcoord;
	int bone;
	fullvertex() {}
	fullvertex(int p, float n[3], float t[3], int b) : posidx(p), norm(n), texcoord(t), bone(b) {}
	fullvertex(int p,
	           float nx, float ny, float nz,
	           float tx, float ty, float tz,
			   int b)
		: posidx(p), norm(nx, ny, nz), texcoord(tx, ty, tz), bone(b) {}
	inline bool operator==(const fullvertex& ref) const
	{
		return ((posidx == ref.posidx) && (bone == ref.bone) && (norm == ref.norm) && (texcoord == ref.texcoord));
	}
};
typedef std::deque<vector3> vectorList_t;
typedef std::deque<fullvertex> vertexList_t;
typedef std::deque<int> intList_t;
typedef std::set<std::string> stringSet_t;
typedef std::map< int, std::deque<int> > int2IntVecMap_t;
typedef std::map< std::pair<int,int>, int> intPair2IntMap_t;


int
cDexiPlugin::Execute (msModel *pModel)
{
    if (!pModel)
        return -1;

    // check if we have something to export
    if (msModel_GetMeshCount (pModel) == 0)
    {
        ::MessageBox (NULL, "The model is empty!  Nothing exported!", sLongTitle, MB_OK | MB_ICONWARNING);
        return 0;
    }

	// get the export options
	DexiOptions options;
	if (!DoDexiOptionDialog(s_DllInstance, &options))
		return 0; // clicked cancel

	AllocConsole();
	StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
	consoleActive = true;
	consoleOut("** DO NOT CLOSE THIS CONSOLE WINDOW **\n\n");

	consoleOut("%s", "Beginning export process...\n");

	// do "insert animation" only on an actor
	if (options.Target != DexiOptions::Target_Actor)
		options.ExportAnimation = false;

	// create our XML document that will contain the entire export info
	TiXmlDocument* xmlDoc = new TiXmlDocument(options.FileName);

	// we just want to add a little more data here
	if (options.AnimationOnly)
	{
		consoleOut("%s", "Loading existing file...\n");
		// if the file didn't exist, we'll create it.
		if (!xmlDoc->LoadFile())
		{
			::MessageBox (NULL, "No Actor to add animation to!", sLongTitle, MB_OK | MB_ICONWARNING);
			delete xmlDoc;
			return 0;
		}
	}

	TiXmlElement* skeletons_elem = NULL;
	TiXmlElement* animations_elem = NULL;
	TiXmlElement* actordef_elem = NULL;
	// a couple of bookkeeping structures to make mappings between sub-elements
	int2IntVecMap_t boneMap; // (bone index) => (index list)
	intPair2IntMap_t vertexMap; // map from (index, mesh) to (newindex)
	std::deque< std::pair<intList_t,vertexList_t> > vertexList;

	if (options.AnimationOnly)
	{
		consoleOut("%s", "Finding existing elements...\n");
		// open up existing elements, and add onto them
		TiXmlElement* destiny3d_root = xmlDoc->FirstChildElement("destiny3d_content");
		if (destiny3d_root)
		{
			skeletons_elem = destiny3d_root->FirstChildElement("skeletons");
			animations_elem = destiny3d_root->FirstChildElement("animations");
			actordef_elem = destiny3d_root->FirstChildElement("actordef");
		}
		if (!destiny3d_root || !skeletons_elem || !animations_elem || !actordef_elem)
		{
			::MessageBox (NULL, "File does not contain a Destiny3D Actor!  Nothing exported.", sLongTitle, MB_OK | MB_ICONWARNING);
			delete xmlDoc;
			return 0;
		}
	}
	else
	{
		consoleOut("%s", "Creating document...\n");
		xmlDoc->InsertEndChild(TiXmlDeclaration("1.0", NULL, NULL));
		// create the destiny3d root element
		TiXmlElement* destiny3d_root = xmlDoc->InsertEndChild(TiXmlElement("destiny3d_content"))->ToElement();
		destiny3d_root->SetAttribute("version", "0.1");
		// set the appropriate target
		switch (options.Target)
		{
		default:
			::MessageBox (NULL, "Invalid export target!  Treating as an actor.", sLongTitle, MB_OK | MB_ICONWARNING);
			// intentional fall-through
		case DexiOptions::Target_Actor:
			destiny3d_root->SetAttribute("target", "actor");
			break;
		case DexiOptions::Target_DWorld:
			destiny3d_root->SetAttribute("target", "dworld");
			break;
		}

		// children of destiny3d_content root (in order):
		// bitmaps, surfaces, vertices, skeletons, animations, meshes, scenedef, actordef
		
		TiXmlElement* bitmaps_elem = destiny3d_root->InsertEndChild(TiXmlElement("bitmaps"))->ToElement();
		TiXmlElement* surfaces_elem = destiny3d_root->InsertEndChild(TiXmlElement("surfaces"))->ToElement();
		TiXmlElement* vertices_elem = destiny3d_root->InsertEndChild(TiXmlElement("vertices"))->ToElement();
		              skeletons_elem = destiny3d_root->InsertEndChild(TiXmlElement("skeletons"))->ToElement();
		              animations_elem = destiny3d_root->InsertEndChild(TiXmlElement("animations"))->ToElement();
		TiXmlElement* meshes_elem = destiny3d_root->InsertEndChild(TiXmlElement("meshes"))->ToElement();
		TiXmlElement* scenedef_elem = NULL;
		if (options.Target == DexiOptions::Target_DWorld)
			scenedef_elem = destiny3d_root->InsertEndChild(TiXmlElement("scenedef"))->ToElement();
		if (options.Target == DexiOptions::Target_Actor)
			actordef_elem = destiny3d_root->InsertEndChild(TiXmlElement("actordef"))->ToElement();
		
		if (pModel->nNumMaterials > 0)
		{
			consoleOut("%s", "Handling materials...\n");
			// keep track of textures we've discovered
			stringSet_t diffuseTexSet;
			// construct a surface for each material
			for (int k = 0; k < pModel->nNumMaterials; ++k)
			{
				msMaterial* pMat = &(pModel->pMaterials[k]);
				// surface needed
				TiXmlElement* surfaceElem = surfaces_elem->InsertEndChild(TiXmlElement("surface"))->ToElement();
				surfaceElem->SetAttribute("name", pMat->szName);
				// <material> is child of <surface>
				TiXmlElement* materialElem = surfaceElem->InsertEndChild(TiXmlElement("material"))->ToElement();
				setFloat(materialElem, "power", pMat->fShininess);
				addColorElem(materialElem->InsertEndChild(TiXmlElement("diffuse"))->ToElement(),
					pMat->Diffuse[0], pMat->Diffuse[1], pMat->Diffuse[2]);
				addColorElem(materialElem->InsertEndChild(TiXmlElement("specular"))->ToElement(),
					pMat->Specular[0], pMat->Specular[1], pMat->Specular[2]);
				addColorElem(materialElem->InsertEndChild(TiXmlElement("ambient"))->ToElement(),
					pMat->Ambient[0], pMat->Ambient[1], pMat->Ambient[2]);
				addColorElem(materialElem->InsertEndChild(TiXmlElement("emissive"))->ToElement(),
					pMat->Emissive[0], pMat->Emissive[1], pMat->Emissive[2]);
				// <textures> is child of <surface>
				TiXmlElement* texturesElem = surfaceElem->InsertEndChild(TiXmlElement("textures"))->ToElement();
				// <texture> is child of <textures>
				TiXmlElement* texElem = texturesElem->InsertEndChild(TiXmlElement("texture"))->ToElement();
				texElem->SetAttribute("stage", 0);
				texElem->SetAttribute("textype", "2D");
				// <bitmap_name> is child of <texture>
				TiXmlElement* bmpElem = texElem->InsertEndChild(TiXmlElement("bitmap_name"))->ToElement();
				TiXmlText* bmpText = bmpElem->InsertEndChild(TiXmlText(pMat->szDiffuseTexture))->ToText();
				// TODO: szAlphaTexture
				// make sure we've got this bitmap filename entered
				std::string str = bmpText->Value();
				if (diffuseTexSet.find(str) == diffuseTexSet.end())
				{
					diffuseTexSet.insert(str);
					// <bitmap> element, under a different main element
					TiXmlElement* bmpElem = bitmaps_elem->InsertEndChild(TiXmlElement("bitmap"))->ToElement();
					bmpElem->SetAttribute("name", str.c_str());
					TiXmlElement* fileElem = bmpElem->InsertEndChild(TiXmlElement("file"))->ToElement();
					fileElem->SetAttribute("name", str.c_str());
				}
			}
		}
		if (pModel->nNumMeshes > 0)
		{
			consoleOut("%s", "Handling meshes...\n");
			int k;
			// sort meshes by number of verts
			std::vector<std::pair<msMesh*,int> > meshMap;
			for (k = 0; k < pModel->nNumMeshes; ++k)
			{
				std::vector<std::pair<msMesh*,int> >::iterator it;
				for (it = meshMap.begin(); it != meshMap.end(); ++it)
				{
					if (it->second >= pModel->pMeshes[k].nNumVertices)
						break;
				}
				std::pair<msMesh*,int> newPair(&pModel->pMeshes[k], pModel->pMeshes[k].nNumVertices);
				meshMap.insert(it, newPair);
			}
			// now compile the mesh information
			// <vertexpool> element
			TiXmlElement* vertPoolElem = vertices_elem->InsertEndChild(TiXmlElement("vertexpool"))->ToElement();
			vertPoolElem->SetAttribute("name", "main_vpool");
			vertPoolElem->SetAttribute("num_pos", 0); // will be modified later
			vertPoolElem->SetAttribute("num_vert", 0);// will be modified later
			// <trimeshdef> element
			TiXmlElement* meshdefElem = meshes_elem->InsertEndChild(TiXmlElement("trimeshdef"))->ToElement();
			meshdefElem->SetAttribute("name", "main_mesh");
			meshdefElem->SetAttribute("vertexpool", "main_vpool");
			int baseVert = 0;
			TiXmlElement* vposElem = NULL;
			// keep track of duplicate position entries
			vectorList_t vposList;
			for (k = 0; k < pModel->nNumMeshes; ++k)
			{
				msMesh* pMesh = meshMap[k].first;
				consoleOut("  Mesh %d, '%s'. ", k, pMesh->szName);
				// we've compiled the vertex data, now create a triangle mesh
				// <trimeshsub> is child of <trimeshdef>
				TiXmlElement* submeshElem = meshdefElem->InsertEndChild(TiXmlElement("trimeshsub"))->ToElement();
				submeshElem->SetAttribute("name", pMesh->szName);
				submeshElem->SetAttribute("surface", pModel->pMaterials[pMesh->nMaterialIndex].szName);
				submeshElem->SetAttribute("winding", "clockwise"); // MS3D uses clockwise faces
				submeshElem->SetAttribute("facecount", pMesh->nNumTriangles);
				int i;
				consoleOut("Vertices: ");
				std::deque<int> vertIndexMap;
				int numEntered = 0;
				for (i = 0; i < pMesh->nNumVertices; ++i)
				{
					int vposIdx;
					msVertex* pVert = &(pMesh->pVertices[i]);
					vector3 vpos(pVert->Vertex);
					vectorList_t::iterator it2 = std::find(vposList.begin(), vposList.end(), vpos);
					if (it2 == vposList.end())
					{
						// not a duplicate, enter it
						vposIdx = vposList.size();
						vposList.push_back(vpos);
						if (vposElem)
							vposElem = vertPoolElem->InsertAfterChild(vposElem, TiXmlElement("vert_pos"))->ToElement();
						else
							vposElem = vertPoolElem->InsertEndChild(TiXmlElement("vert_pos"))->ToElement();
						addPosElem(vposElem, vpos.x, vpos.y, vpos.z);
						vposElem->SetAttribute("idx", vposIdx);
						++numEntered;
					}
					else
					{
						// duplicate, grab the index
						vposIdx = it2 - vposList.begin();
					}
					vertIndexMap.push_back(vposIdx);
				}
				consoleOut("%d. Faces: %d...\n", numEntered, pMesh->nNumTriangles);
				int meshVertNum = 0;
				// explode the mesh into a single data stream
				for (i = 0; i < pMesh->nNumTriangles; ++i)
				{
					msTriangle* pTri = &(pMesh->pTriangles[i]);
					// <triface> is child of <trimeshsub>
					TiXmlElement* faceElem = submeshElem->InsertEndChild(TiXmlElement("triface"))->ToElement();
					for (int n = 0; n < 3; ++n)
					{
						int vposIdx = vertIndexMap[pTri->nVertexIndices[n]];
						if (vposIdx >= (int)vertexList.size())
							vertexList.resize(vposIdx+1);
						msVertex* pVert = &(pMesh->pVertices[pTri->nVertexIndices[n]]);
						msVec3 uvw = {
							pVert->u,
							pVert->v,
							0
						};
						// explode the vertex data into a single vertex structure
						// combines position, normal, texcoord, and bone mapping
						fullvertex vert(vposIdx,
										pMesh->pNormals[pTri->nNormalIndices[n]],
										uvw, pVert->nBoneIndex);
						// now we need to search and see if this vertex is a duplicate
						intList_t& iDeqRef = vertexList[vposIdx].first;
						vertexList_t& vDeqRef = vertexList[vposIdx].second;
						vertexList_t::iterator it = std::find(vDeqRef.begin(), vDeqRef.end(), vert);
						int vertIdx;
						if (it == vDeqRef.end())
						{
							// not a duplicate, enter it
							vertIdx = baseVert + meshVertNum;
							iDeqRef.push_back(vertIdx);
							vDeqRef.push_back(vert);
							// <vertex> element is child of <vertexpool>
							TiXmlElement* vertElem = vertPoolElem->InsertEndChild(TiXmlElement("vertex"))->ToElement();
							vertElem->SetAttribute("idx", vertIdx);
							vertElem->SetAttribute("posidx", vposIdx);
							addTexCoordElem(vertElem, pVert->u, &pVert->v);
							addNormalElem(vertElem, vert.norm.x, vert.norm.y, vert.norm.z);
							// remember the bone index for this vertex, we'll need it later
							boneMap[vert.bone].push_back(vertIdx);
							meshVertNum += 1;
						}
						else
						{
							// duplicate, grab the index
							vertIdx = iDeqRef[it - vDeqRef.begin()];
						}
						const char buf[3] = {'v', char('0'+n), '\0'};
						faceElem->SetAttribute(buf, vertIdx);
					}
				}
				// add to these for the next loop
				baseVert += meshVertNum;
			}
			// now we know how many positions there really were
			vertPoolElem->SetAttribute("num_pos", vposList.size());
			vertPoolElem->SetAttribute("num_vert", baseVert);
			if (scenedef_elem)
			{
				TiXmlElement* instanceElem = scenedef_elem->InsertEndChild(TiXmlElement("trimeshinstance"))->ToElement();
				instanceElem->SetAttribute("meshdef", "main_mesh");
				instanceElem->SetAttribute("instanced", 0);
			}
		}
	}
	
	if (pModel->nNumBones > 0)
	{
		consoleOut("%s", "Handling skeleton & animations...\n");
		TiXmlElement* skelElem = NULL;
		if (options.AnimationOnly)
		{
			// discover the skeleton we're looking for
			std::string skelName = "main_skel";
			skelElem = skeletons_elem->FirstChildElement("skeleton");
			while (skelElem != NULL)
			{
				if (skelName == skelElem->Attribute("name"))
					break;
				skelElem = skelElem->NextSiblingElement("skeleton");
			}
		}
		if (skelElem == NULL)
		{
			skelElem = skeletons_elem->InsertEndChild(TiXmlElement("skeleton"))->ToElement();
			skelElem->SetAttribute("name", "main_skel");
		}
		TiXmlElement* animElem = NULL;
		if (options.ExportAnimation)
		{
			// look if this animation already exists. in a fresh document it won't.
			std::string animName = options.AnimName;
			animElem = animations_elem->FirstChildElement("animation");
			while (animElem != NULL)
			{
				if (animName == animElem->Attribute("name"))
				{
					if (!options.OverwriteAnim)
					{
						::MessageBox (NULL, "Animation already exists!", sLongTitle, MB_OK | MB_ICONWARNING);
						delete xmlDoc;
						return 0;
					}
					// overwrite existing values, clear out the old ones
					animElem->Clear();
					break;
				}
				animElem = animElem->NextSiblingElement("animation");
			}
			if (animElem == NULL)
			{
				animElem = animations_elem->InsertEndChild(TiXmlElement("animation"))->ToElement();
				animElem->SetAttribute("name", options.AnimName);
			}
		}
		for (int k = 0; k < pModel->nNumBones; ++k)
		{
			msBone* pBone = &(pModel->pBones[k]);
			if (!options.AnimationOnly)
			{
				// build the bone
				TiXmlElement* boneElem = skelElem->InsertEndChild(TiXmlElement("bone"))->ToElement();
				boneElem->SetAttribute("name", pBone->szName);
				boneElem->SetAttribute("parent", pBone->szParentName);
				if (pBone->szParentName[0] == '\0')
					boneElem->SetAttribute("parent", "root");
				addPosElem(boneElem, pBone->Position[0], pBone->Position[1], pBone->Position[2]);
				addRotationFromEuler(boneElem, pBone->Rotation[0], pBone->Rotation[1], pBone->Rotation[2]);
				int2IntVecMap_t::iterator it = boneMap.find(k);
				if (it != boneMap.end())
				{
					std::deque<int>& vecref = it->second;
					for (size_t i = 0; i < vecref.size(); ++i)
					{
						TiXmlElement* bVertElem = boneElem->InsertEndChild(TiXmlElement("bone_vert"))->ToElement();
						bVertElem->SetAttribute("idx", vecref[i]);
						bVertElem->SetAttribute("weight", "1.0");
					}
				}
			}
			if (options.ExportAnimation)
			{
				// build the animation
				if (pBone->nNumPositionKeys == pBone->nNumRotationKeys)
				{
					for (int i = 0; i < pBone->nNumPositionKeys; ++i)
					{
						msPositionKey* pPosKey = &(pBone->pPositionKeys[i]);
						msRotationKey* pRotKey = &(pBone->pRotationKeys[i]);
						if (pPosKey->fTime != pRotKey->fTime)
							continue;
						// find the corresponding animation keyframe
						TiXmlElement* keyframeElem;
						for(keyframeElem = animElem->FirstChildElement("anim_keyframe");
							keyframeElem != NULL;
							keyframeElem = keyframeElem->NextSiblingElement("anim_keyframe"))
						{
							const char* timeVal = keyframeElem->Attribute("time");
							if (timeVal && atof(timeVal) == pPosKey->fTime)
								break;
						}
						if (!keyframeElem)
						{
							// assumes the keys occur in ascending time order
							keyframeElem = animElem->InsertEndChild(TiXmlElement("anim_keyframe"))->ToElement();
							setFloat(keyframeElem, "time", pPosKey->fTime);
						}
						TiXmlElement* aBoneElem = keyframeElem->InsertEndChild(TiXmlElement("anim_bone"))->ToElement();
						aBoneElem->SetAttribute("bone_name", pBone->szName);
						addPosElem(aBoneElem, pPosKey->Position[0], pPosKey->Position[1], pPosKey->Position[2]);
						addRotationFromEuler(aBoneElem, pRotKey->Rotation[0], pRotKey->Rotation[1], pRotKey->Rotation[2]);
					}
				}
			}
		}
	}
	if (actordef_elem && pModel->nNumMeshes > 0 && pModel->nNumBones > 0)
	{
		consoleOut("%s", "Generating actordef element...\n");
		if (!options.AnimationOnly)
		{
			actordef_elem->SetAttribute("name", "main_actor");
			actordef_elem->SetAttribute("mesh_name", "main_mesh");
			actordef_elem->SetAttribute("skel_name", "main_skel");
		}
		if (options.ExportAnimation)
		{
			TiXmlElement* animElem = NULL;
			std::string animName = options.AnimName;
			animElem = animations_elem->FirstChildElement("anim_name");
			while (animElem != NULL)
			{
				if (animName == animElem->Attribute("name"))
				{
					break;
				}
				animElem = animElem->NextSiblingElement("anim_name");
			}
			if (animElem == NULL)
			{
				TiXmlElement* animElem = actordef_elem->InsertEndChild(TiXmlElement("anim_name"))->ToElement();
				animElem->InsertEndChild(TiXmlText(options.AnimName));
			}
		}
	}

	consoleOut("%s", "Finished export, saving file...\n");
	if (!xmlDoc->SaveFile())
	{
        ::MessageBox (NULL, "Error saving file!", sLongTitle, MB_OK | MB_ICONWARNING);
		delete xmlDoc;
		return 0;
	}

	delete xmlDoc;
	FreeConsole();
	
    return 0;
}
