#pragma once

#include <fstream>
#include <map>
#include <vector>
#include <string>
#include "GasVertex.h"
#include "GasMaterial.h"
#include "GasBatch.h"
#include "GasNode.h"
#include "GasBone.h"
#include "AnimationHeader.h"

using namespace std;

class Exporter : public SceneExport
{
public:
	Exporter();
	~Exporter();

	/** Exports the scene. */
	int 			DoExport( const TCHAR* name, ExpInterface *ei, Interface* i, 
						BOOL suppressprompts=FALSE, DWORD options=0 );

	/** Show DLL's "About..." box */
	void			ShowAbout( HWND hwnd );

	/** Number of extensions supported */
	int				ExtCount();
	
	/** Extension #n (i.e. "3DS") */
	const TCHAR*	Ext( int n );					
	
	/** Long ASCII description (i.e. "Autodesk 3D Studio File") */
	const TCHAR*	LongDesc();
	
	/** Short ASCII description (i.e. "3D Studio") */
	const TCHAR*	ShortDesc();
	
	/** ASCII Author name */
	const TCHAR*	AuthorName();
	
	/** ASCII Copyright message */
	const TCHAR*	CopyrightMessage();
	
	/** Other message #1 */
	const TCHAR*	OtherMessage1();
	
	/** Other message #2 */
	const TCHAR*	OtherMessage2();
	
	/** Version number * 100 (i.e. v3.01 = 301) */
	unsigned int	Version();
	
	/** Returns TRUE if option supported. */
	BOOL			SupportsOptions( int ext, DWORD options );

	/** Processes the given node to collect mesh, material, and animation data
		Recursively collects data for all the node's children as well
	*/
	int ProcessNode(IGameNode* node, int index);

	/**
		Processes a single mesh to collect material, vertex, and normal data
	*/
	void ProcessMesh(IGameNode* node, GasNode* gasNode);

	/**
		Record animation data at the given time
		Stores data in gasNodes[ID]
	*/
	void GetTreeAnimAt(int ID, int currTime);

	/**
		Creates list of all children without processing them
		(Can collect each node's data again in a second pass
		 to avoid recomputing tranform data needlessly)
	*/
	int BuildNodeList(IGameNode* node);

	/**
		Get the length of the animation
	*/
	void GetAnimationTime(IGameScene* scene);

private:
	IGameScene*		m_igame;
	ofstream		file;

	void			deinit();

	Exporter( const Exporter& );
	Exporter& operator=( const Exporter& );

	int currTime;

	AnimationHeader animHeader;
	std::map<std::string, GasMaterial> materialMap;	//map of all unique materials in scene
	std::vector<GasBatchStr> batches;				
	std::vector<GasNode*> gasnodes;					
	std::vector<IGameNode*> gamenodes;				

	/**
		Get the ibo index for the given vert in the batch vertex list
		Returns the index if found, -1 if not in the list
	*/
	int GetListIndex(GasVertex& vert, GasBatchStr& batch);
	int GetListIndex(GasSkinVertex& vert, GasBatchStr& batch);

	/**
		Gets the name of the texture without the filepath
		Texture name limited to 64 characters to save space
	*/
	void GetTextureName(std::string filepath, char* arr);

	/**
		Get the index of the specified material in the material list
		Returns either the index or -1 if not found
	*/
	int GetMaterialID(std::string materialName);

	/**
		Gets the node's transformation matrix (without any parent transformations)
	*/
	GMatrix GetMatrix(IGameNode* node, TimeValue time);

	void Transpose(GMatrix& mat);

	/**
		Get the node index in the node list
		Returns the index if found, -1 if not in the list
	*/
	int GetGlobalNodeIndex(IGameNode* node);
	int GetLocalNodeListIndex(IGameNode* node, std::vector<GasBone>& bones);

	/**
		Finds the 4 bones with the greatest weights and stores them in weights and bones 
		(last weight not recorded, but can be recomputed as 1 - (otherWeightTotal)
	*/
	void GetBonesAndWeights(std::vector< std::pair<float, int> >& weightList,
							Point3& weights, Point4& bones);

	/**
		Print out all collected information as a binary file

		Data is printed as
		VersionNumber
		Header:
			# of materials, offset to get to first material
			# of batches, offset to get to first batch
			# of nodes, offset to get to first node
			# of animations, offset to get to first animation
		All materials printed sequentially
		All batched printed sequentially
		All nodes printed sequentially
	*/
	void PrintHeader();			//describes layout of entire file
	void PrintMaterials();		//lists all materials
	void PrintVertices();		//lists all vertices
	void PrintBatches();		//lists all batches
	void PrintNodes();			//lists all nodes
	void PrintEOF();			//end the file

	/**
		Helper function to compute how large batches will be when written
		as binary data
	*/
	int ComputeBatchesSize();

	/** To reduce the final file size, only record trans/rot/scale info
		if there is an animation playing. No need to record matrices for
		times when the animation isn't playing
	*/
	bool ShouldExportTranslations(int nodeIndex);
	bool ShouldExportRotations(int nodeIndex);
	bool ShouldExportScales(int nodeIndex);
};
#include "stdafx.h"
#include "Exporter.h"
#include "Utils.h"
#include "TextureMapIndexConstants.h"
#include "GasBone.h"

using namespace std;

Exporter::Exporter() :
	m_igame( 0 )
{
}

Exporter::~Exporter()
{
	while(gasnodes.size() > 0)
	{
		delete gasnodes[gasnodes.size() - 1];
		gasnodes.pop_back();
	}

	deinit();
}

void Exporter::deinit()
{
	if ( m_igame != 0 )
	{
		m_igame->ReleaseIGame();
		m_igame = 0;
	}
}

int Exporter::DoExport( const TCHAR* name, ExpInterface* ei, Interface* i, BOOL suppressprompts, DWORD options )
{
	try
	{
		const bool exportselected = 0 != (options & SCENE_EXPORT_SELECTED); // export only selected objects

		file.open(name, ios::binary);
		if(!file.is_open())
		{
			throw std::exception( "Failed to open file for writing" );
		}
		
		// initialize IGame
		m_igame = GetIGameInterface();
		if ( !m_igame )
			throw std::exception( "Failed to initialize IGame interface" );
		
		// Switch the coordinate system so that Y is up and Z is forward
		// to match the target engine's coordinate system
		IGameConversionManager* cm = GetConversionManager();
		cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
		
		m_igame->InitialiseIGame( exportselected );

		GetAnimationTime(m_igame);
		currTime = animHeader.StartTime;
		
		int num = m_igame->GetTopLevelNodeCount();

		//get all nodes in scene (in order!)
		GasNode* root = new GasNode();
		root->ID = 0;
		GetCharArrayFromString("", root->name);
		gasnodes.push_back(root);
		gamenodes.push_back(0);
		int ID = 0;

		for(int i = 0; i < num; ++i)
		{
			IGameNode* node = m_igame->GetTopLevelNode(i);
			
			ID = BuildNodeList(node);
			root->childIDs.push_back(ID);
		}

		//get all DATA in the scene (in order!)
		int newID = 0;
		for(int i = 0; i < num; ++i)
		{
			IGameNode* node = m_igame->GetTopLevelNode(i);
			newID = ProcessNode(node, newID + 1);
		}

		//get data for time 0 (starting values)
		GetTreeAnimAt(0, 0);

		//get real animation data
		for(int i = animHeader.StartTime; i <= animHeader.EndTime; i += animHeader.FrameRate)
		{
			GetTreeAnimAt(0, i);
		}

		// write all info into a binary file for the engine
		PrintHeader();
		PrintMaterials();
		PrintBatches();
		PrintNodes();
		
		file.close();

		// release initialized stuff
		deinit();
	}
	catch ( std::exception& e )
	{
		TSTR tstr( e.what() );
		MessageBox( i->GetMAXHWnd(), tstr, _T("Export Error"), MB_OK|MB_ICONERROR );
		deinit();
	}
	return TRUE;
}

void Exporter::GetAnimationTime(IGameScene* scene)
{
	TimeValue start = scene->GetSceneStartTime();
	TimeValue end = scene->GetSceneEndTime();

	animHeader.StartTime = int(start);
	animHeader.EndTime = int(end);

	animHeader.FrameRate = 4800 / 30;//30 fps
}

void Exporter::Transpose(GMatrix& mat)
{
	float temp;

	for(int i = 1; i < 4; ++i)
	{
		for(int j = 0; j < i; ++j)
		{
			temp = mat[i][j];
			mat[i][j] = mat[j][i];
			mat[j][i] = temp;
		}
	}
}

GMatrix Exporter::GetMatrix(IGameNode* node, TimeValue time)
{
	GMatrix m;
	
	if(node->GetNodeParent() != 0)
		m = node->GetWorldTM(time) * node->GetNodeParent()->GetWorldTM(time).Inverse();
	else
		m = node->GetWorldTM(time);

	return m;
}

int Exporter::BuildNodeList(IGameNode* node)
{
	GasNode* gasNode = new GasNode();

	std::string name = node->GetName();
	GetCharArrayFromString(name, gasNode->name);

	gasNode->ID = gasnodes.size();
	gasnodes.push_back(gasNode);
	gamenodes.push_back(node);
	int id = gasnodes.size() - 1;

	//Process children recursively...
	int num = node->GetChildCount();
	int ID;
	for(int i = 0; i < num; ++i)
	{
		ID = BuildNodeList(node->GetNodeChild(i));
		gasNode->childIDs.push_back(ID);
	}

	return id;
}

int Exporter::ProcessNode(IGameNode* node, int index)
{
	IGameObject* obj = node->GetIGameObject();
	GasNode* gasNode = gasnodes[index];

	// For this exporter, only interested in meshes
	// ignore all other node types
	switch(obj->GetIGameType())
	{
	case IGameObject::IGAME_MESH:
		{
			ProcessMesh(node, gasNode);
		}
		break;
	}

	//Process children recursively...
	int num = node->GetChildCount();

	for(int i = 0; i < num; ++i)
	{
		index = ProcessNode(node->GetNodeChild(i), index + 1);
	}

	return index;
}

void Exporter::GetTreeAnimAt(int ID, int currTime)
{
	if(ID != 0)
	{
		GMatrix mat = GetMatrix(gamenodes[ID], currTime);
		gasnodes[ID]->localRotations.push_back(mat.Rotation());
		gasnodes[ID]->localScales.push_back(mat.Scaling());
		gasnodes[ID]->localTranslations.push_back(mat.Translation());
	}

	//recurse for all children
	for(int i = 0; i < gasnodes[ID]->childIDs.size(); ++i)
	{
		GetTreeAnimAt( gasnodes[ID]->childIDs[i], currTime );
	}
}

void Exporter::ProcessMesh(IGameNode* node, GasNode* gasnode)
{
	map<string, IGameMaterial*> mats;
	map<string, IGameMaterial*>::iterator it;

	IGameObject* obj = node->GetIGameObject();
	IGameMesh* mesh = reinterpret_cast<IGameMesh*>(obj);
	mesh->InitializeData();

	IGameModifier* modifier = 0;
	IGameSkin* skin = 0;

	bool foundSkinning = false;
	int numberOfModifiers = mesh->GetNumModifiers();
	for(int i = 0; i < numberOfModifiers; ++i)
	{
		modifier = mesh->GetIGameModifier(i);
		if(modifier != 0 && modifier->IsSkin())
		{
			foundSkinning = true;
			skin = (IGameSkin*) modifier;
			break;
		}
	}

	gasnode->isSkinned = foundSkinning;

	// Get all materials used in this mesh
	// For each material, store all the faces that use that material together
	for(int i = 0; i < mesh->GetNumberOfFaces(); ++i)
	{
		IGameMaterial* material = mesh->GetMaterialFromFace(i);
		
		// Get the texture associated with this material
		if(material != 0)
		{
			std::string name = material->GetMaterialName();
	
			mats.insert(pair<string, IGameMaterial*>(name, material));
	
			char texname[64];
			memset(texname, 0, sizeof(char) * 64);
	
			int numtextures = material->GetNumberOfTextureMaps();
			IGameTextureMap* texmaptest = 0;
			IGameTextureMap* texmap = 0;

			// Only interested in the diffuse texture
			for(int i = 0; i < numtextures; ++i)
			{
				texmaptest = material->GetIGameTextureMap(i);
				if(texmaptest != 0 && texmaptest->GetStdMapSlot() == ID_DI)
				{
					texmap = material->GetIGameTextureMap(i);
					break;
				}
				else if(i == numtextures - 1)
					texmap = 0;
			}
	
			if(texmap != 0)
				GetTextureName(texmap->GetBitmapFileName(), texname);
	
			//Store the name of the texture in the material
			char matname[64];
			memset(matname, 0, (sizeof(char) * 64));
			GetCharArrayFromString(name, matname);
			materialMap.insert( pair<string, GasMaterial>(name, GasMaterial(matname, texname)) );
		}
		else
		{	//use a default material instead
			mats.insert(pair<string, IGameMaterial*>("", nullptr));
			materialMap.insert( pair<string, GasMaterial>("", GasMaterial("", "")) );
		}
	}
	
	Point3 pos, normal, weights;
	Point2 tex;
	Point4 bones;
	
	// For each material, get the pos/texcoord/normal info
	// Also collect any animation data (bones and weights)
	for(it = mats.begin(); it != mats.end(); it++)
	{
		GasBatchStr batch = GasBatchStr((*it).first);
		batch.isSkinned = foundSkinning;
	
		for(int i = 0; i < mesh->GetNumberOfFaces(); ++i)
		{
			FaceEx* faceex = mesh->GetFace(i);
			string materialName = "";
			IGameMaterial* tempmat = mesh->GetMaterialFromFace(i);

			if(tempmat != 0)
				materialName = tempmat->GetMaterialName();
	
			//worldTM = model to world
			//world-1 = world to model(local)
			GMatrix minv = node->GetWorldTM().Inverse();
			//same for normal, but clear out the translation component
			GMatrix normalinv = node->GetWorldTM().Inverse();
			Point4 row = Point4(0,0,0,1);
			normalinv.SetRow(3, row);

			if(materialName == (*it).first)
			{
				for(int j = 0; j < 3; ++j)
				{
					//convert from world space to local space!
					pos =		mesh->GetVertex(faceex->vert[j]) * minv;
					tex =		mesh->GetTexVertex(faceex->texCoord[j]);
					normal =	(mesh->GetNormal(faceex->norm[j]) * normalinv);
	
					if(foundSkinning)
					{
						int numBones = skin->GetNumberOfBones(faceex->vert[j]);
						
						vector< pair<float, int> > weightList;

						//add bones to list and get weights for each bone
						for(int i = 0; i < numBones; ++i)
						{
							IGameNode* bone = skin->GetIGameBone(faceex->vert[j],i);

							int localindex = GetLocalNodeListIndex(bone, gasnode->bones);
							int globalindex = 0;
							if(localindex == -1)
							{
								localindex = gasnode->bones.size();

								//compute transform and add it to the list
								globalindex = GetGlobalNodeIndex(bone);
								GasBone newBone = GasBone( bone->GetName(), globalindex );
								newBone.TM = node->GetWorldTM(0) * bone->GetWorldTM(0).Inverse();
								gasnode->bones.push_back( newBone );
							}
							else
							{
								globalindex = GetGlobalNodeIndex(bone);
							}

							weightList.push_back( pair<float, int>(skin->GetWeight(faceex->vert[j], i), localindex) );
						}

						//determine which weights to use (and therefore which bones)
						GetBonesAndWeights(weightList, weights, bones);
					}

					int index = 0;
					GasVertex vert = GasVertex(pos, tex, normal);
					GasSkinVertex vertex = GasSkinVertex(pos, tex, normal, bones, weights);

					// Since skinned and unskinned meshes are handled seperately in the engine,
					// Store the animation data in the correct list
					if( !foundSkinning )
						index = GetListIndex(vert, batch);
					else 
						index = GetListIndex(vertex, batch);
	
					if(index == -1)
					{
						if(!foundSkinning)
							batch.indices.push_back(unsigned int(batch.vertices.size()));
						else
							batch.indices.push_back(unsigned int(batch.skinnedVertices.size()));
						
						if(!foundSkinning)
							batch.vertices.push_back(vert);
						else
							batch.skinnedVertices.push_back(vertex);
					}
					else
					{
						batch.indices.push_back(index);
					}
				}
			}
		}
		gasnode->batchIDs.push_back(batches.size());
		batches.push_back(batch);
	}
}

void Exporter::ShowAbout( HWND hwnd )
{
}

int Exporter::ExtCount()
{
	return 1;
}

const TCHAR* Exporter::Ext( int /*n*/ )
{
	return _T("gas");
}

const TCHAR* Exporter::LongDesc()
{
	return _T("3dsmax Exporter");
}

const TCHAR* Exporter::ShortDesc()
{
	return _T("Exporter");
}

const TCHAR* Exporter::AuthorName()
{
	return _T("Elizabeth Labelle");
}

const TCHAR* Exporter::CopyrightMessage()
{
	return _T("Copyright (C) 2012");
}

const TCHAR* Exporter::OtherMessage1()
{
	return _T("");
}

const TCHAR* Exporter::OtherMessage2()
{
	return _T("");
}

unsigned int Exporter::Version()
{
	return 1;
}

BOOL Exporter::SupportsOptions( int ext, DWORD options )
{
	return TRUE;
}

int Exporter::GetMaterialID(std::string materialName)
{
	map<std::string, GasMaterial>::iterator it;
	int index = 0;

	for(it = materialMap.begin(); it != materialMap.end(); it++)
	{
		if((*it).first == materialName)
			return index;
		index++;
	}

	return -1;
}

int Exporter::GetListIndex(GasVertex& vert, GasBatchStr& batch)
{
	for(int i = 0; i < batch.vertices.size(); ++i)
	{
		if(batch.vertices[i] == vert)
			return i;
	}

	return -1;
}

int Exporter::GetListIndex(GasSkinVertex& vert, GasBatchStr& batch)
{
	for(int i = 0; i < batch.skinnedVertices.size(); ++i)
	{
		if(batch.skinnedVertices[i] == vert)
			return i;
	}

	return -1;
}

void Exporter::GetTextureName(std::string filepath, char* arr)
{
	int index = int(filepath.find_last_of('\\'));

	if(index >= 0)
		filepath = filepath.substr(index + 1);

	GetCharArrayFromString(filepath, arr);
}

int Exporter::ComputeBatchesSize()
{
	int size = 0;

	for(int i = 0; i < batches.size(); ++i)
	{
		size += batches[i].GetSize();
	}

	return size;
}

void Exporter::PrintHeader()
{
	float versionNum = 2.0f;
	file.write(reinterpret_cast<char*>(&versionNum), sizeof(float));	

	int matOffset = int(sizeof(float) + sizeof(MatHeader) + sizeof(BatchHeader)
					+ sizeof(NodeHeader) + sizeof(AnimationHeader));
	int batchOffset = matOffset  + int(sizeof(GasMaterial) * materialMap.size());
	int nodeOffset = batchOffset + ComputeBatchesSize();

	MatHeader mat = MatHeader(int(materialMap.size()), matOffset);
	BatchHeader batch = BatchHeader(int(batches.size()), batchOffset);
	NodeHeader node = NodeHeader(int(gasnodes.size()), nodeOffset);

	file.write(reinterpret_cast<char*>(&mat), sizeof(MatHeader));
	file.write(reinterpret_cast<char*>(&batch), sizeof(BatchHeader));
	file.write(reinterpret_cast<char*>(&node), sizeof(NodeHeader));
	file.write(reinterpret_cast<char*>(&animHeader), sizeof(AnimationHeader));
}

void Exporter::PrintMaterials()
{
	std::map<std::string, GasMaterial>::iterator it;

	for(it = materialMap.begin(); it != materialMap.end(); ++it)
	{
		GasMaterial gasmat = (*it).second;

		file.write(reinterpret_cast<char*>(&gasmat), sizeof(gasmat));
	}
}

void Exporter::PrintBatches()
{
	for(int i = 0; i < batches.size(); ++i)
	{
		int numVertices = 0;

		if(batches[i].isSkinned)
			numVertices = int(batches[i].skinnedVertices.size());
		else
			numVertices = int(batches[i].vertices.size());

		int numIndices = int(batches[i].indices.size());
		unsigned int matID = unsigned int(GetMaterialID(batches[i].matname));

		file.write(reinterpret_cast<char*>(&numVertices), sizeof(int));
		file.write(reinterpret_cast<char*>(&numIndices), sizeof(int));
		file.write(reinterpret_cast<char*>(&matID), sizeof(unsigned int));
		file.write(reinterpret_cast<char*>(&(batches[i].isSkinned)), sizeof(bool));

		if(batches[i].isSkinned)
		{
			for(int j = 0; j < batches[i].skinnedVertices.size(); ++j)
			{
				GasSkinVertex gasskinvert = batches[i].skinnedVertices[j];
				file.write(reinterpret_cast<char*>(&gasskinvert), sizeof(GasSkinVertex));

				int size = sizeof(GasSkinVertex);
			}
		}
		else
		{
			for(int j = 0; j < batches[i].vertices.size(); ++j)
			{
				GasVertex gasvert = batches[i].vertices[j];
				file.write(reinterpret_cast<char*>(&gasvert), sizeof(GasVertex));
			}
		}

		for(int j = 0; j < batches[i].indices.size(); ++j)
		{
			unsigned int index = batches[i].indices[j];
			file.write(reinterpret_cast<char*>(&index), sizeof(unsigned int));
		}
	}
}

void Exporter::PrintNodes()
{
	for(int i = 0; i < gasnodes.size(); ++i)
	{
		int numBatches = int(gasnodes[i]->batchIDs.size());
		int numChildren = int(gasnodes[i]->childIDs.size());
		int numBones = int(gasnodes[i]->bones.size());
		unsigned int ID = gasnodes[i]->ID;
		bool isSkinned = gasnodes[i]->isSkinned;

		file.write(reinterpret_cast<char*>(&(gasnodes[i]->name[0])), sizeof(char) * 64);
		file.write(reinterpret_cast<char*>(&numBatches), sizeof(unsigned int));
		file.write(reinterpret_cast<char*>(&numChildren), sizeof(unsigned int));
		file.write(reinterpret_cast<char*>(&numBones), sizeof(int));
		file.write(reinterpret_cast<char*>(&ID), sizeof(unsigned int));
		file.write(reinterpret_cast<char*>(&isSkinned), sizeof(bool));

		for(int j = 0; j < gasnodes[i]->batchIDs.size(); ++j)
			file.write(reinterpret_cast<char*>(&(gasnodes[i]->batchIDs[j])), sizeof(unsigned int));

		for(int j = 0; j < gasnodes[i]->childIDs.size(); ++j)
			file.write(reinterpret_cast<char*>(&(gasnodes[i]->childIDs[j])), sizeof(unsigned int));

		//bones
		for(int j = 0; j < gasnodes[i]->bones.size(); ++j)
		{
			file.write(reinterpret_cast<char*>(&(gasnodes[i]->bones[j].ID)), sizeof(unsigned int));
			file.write(reinterpret_cast<char*>(&(gasnodes[i]->bones[j].TM)), sizeof(GMatrix));
		}

		if(i == 0)
		{
			for(int j = 0; j < gasnodes[i]->localTranslations.size(); ++j)
			{
				Quat r = gasnodes[i]->localRotations[j];
				Point3 s = gasnodes[i]->localScales[j];
				Point3 t = gasnodes[i]->localTranslations[j];

				file.write(reinterpret_cast<char*>(&r), sizeof(Quat));
				file.write(reinterpret_cast<char*>(&t), sizeof(Point3));
				file.write(reinterpret_cast<char*>(&s), sizeof(Point3));
			}
		}
		else
		{
			bool hasRotations = ShouldExportRotations(i);
			file.write(reinterpret_cast<char*>(&hasRotations), sizeof(bool));
			for(int j = 0; j < gasnodes[i]->localRotations.size(); ++j)
			{
				Quat r = gasnodes[i]->localRotations[j];
				file.write(reinterpret_cast<char*>(&r), sizeof(Quat));
			}

			bool hasTranslations = ShouldExportTranslations(i);
			file.write(reinterpret_cast<char*>(&hasTranslations), sizeof(bool));
			for(int j = 0; j < gasnodes[i]->localTranslations.size(); ++j)
			{
				Point3 t = gasnodes[i]->localTranslations[j];
				file.write(reinterpret_cast<char*>(&t), sizeof(Point3));
			}

			bool hasScales = ShouldExportScales(i);
			file.write(reinterpret_cast<char*>(&hasScales), sizeof(bool));
			for(int j = 0; j < gasnodes[i]->localScales.size(); ++j)
			{
				Point3 s = gasnodes[i]->localScales[j];
				file.write(reinterpret_cast<char*>(&s), sizeof(Point3));
			}
		}
	}
}

void Exporter::PrintEOF()
{
	file << EOF;
}

int Exporter::GetGlobalNodeIndex(IGameNode* node)
{
	string name = node->GetName();

	for(int i = 0; i < gasnodes.size(); ++i)
	{
		string name2 = gasnodes[i]->name;

		if(name == name2)
			return i;
	}
	return -1;
}

int Exporter::GetLocalNodeListIndex(IGameNode* node, std::vector<GasBone>& bones)
{
	string name = node->GetName();

	for(int i = 0; i < bones.size(); ++i)
	{
		string name2 = bones[i].name;

		if(name == name2)
			return i;
	}
	return -1;
}

bool mySort(pair<float, int> a, pair<float, int> b)
{
	return a.first > b.first;
}

void Exporter::GetBonesAndWeights(std::vector< std::pair<float, int> >& weightList, Point3& weights, Point4& bones)
{
	sort(weightList.begin(), weightList.end(), mySort);
	weights = Point3(0,0,0);
	bones = Point4(0,0,0,0);

	//first 4 in list are highest
	for(int i = 0; i < 4; ++i)
	{
		if(weightList.size() > i)
		{
			if(i != 3)
				weights[i] = weightList[i].first;
			bones[i] = weightList[i].second;
		}
		else
			break;
	}
}

bool Exporter::ShouldExportTranslations(int nodeIndex)
{
	GasNode* n = gasnodes[nodeIndex];

	for(int i = 0; i < n->localTranslations.size() - 1; ++i)
	{
		if(n->localTranslations[i] != n->localTranslations[i + 1])
			return true;
	}
	n->localTranslations.resize(1);
	return false;
}

bool Exporter::ShouldExportRotations(int nodeIndex)
{
	GasNode* n = gasnodes[nodeIndex];

	for(int i = 0; i < n->localRotations.size() - 1; ++i)
	{
		for(int j = 0; j < 4; ++j)
		{
			if((n->localRotations[i])[j] != (n->localRotations[i + 1])[j])
				return true;
		}
	}
	n->localRotations.resize(1);
	return false;
}

bool Exporter::ShouldExportScales(int nodeIndex)
{
	GasNode* n = gasnodes[nodeIndex];

	for(int i = 0; i < n->localScales.size() - 1; ++i)
	{
		if(n->localScales[i] != n->localScales[i + 1])
			return true;
	}
	n->localScales.resize(1);
	return false;
}
float4x4 worldMatrixArray[100]:WORLDMATARRAY;//boneTM array for each mesh
float4x4 vp:VIEWPROJECTION;
float3 freeLight:FREELIGHT;

Texture2D tex;

SamplerState defss
{
	AddressU = Wrap;
	AddressV = Wrap;
};

void MyVS(float3 pos:apos, float2 tc:atexcoord,
		  float3 normal:N, 
		  unsigned int4 bones:abones, float3 weight:aweights,
	out float4 opos:SV_POSITION, out float2 otc:TEX,
	out float3 onormal:NORMAL, out float3 olight:LIGHT)
{
	opos = float4(0,0,0,0);
	onormal = float4(0,0,0,0);
	
	int numBones = 4;
	int iBone = 0;
	float sumWeights = 1.0f;
	for(;iBone < numBones - 1; iBone++)
	{
		sumWeights -= weight[iBone];
		opos += mul( float4(pos, 1), worldMatrixArray[ bones[iBone] ]) * weight[iBone];
		onormal += mul(normal, (float3x3)worldMatrixArray[ bones[iBone] ]) * weight[iBone];
	}
	
	opos = mul(opos, vp);
	otc = tc;
	olight = normalize(freeLight - pos);
}

float4 MyPS(float4 pos:SV_POSITION, float2 tc:TEX,
			float3 normal:NORMAL, float3 light:LIGHT):SV_Target
{ 
   float3 normal2 = normalize(normal);
   float3 light2 = normalize(light);
	
   float Idiff = max( dot(normal2, light2), 0.1);

   return tex.Sample(defss, tc) * Idiff;
}

technique11 MyTechnique
{
    pass P0
    {          
		SetVertexShader(CompileShader(vs_4_0, MyVS()));
		SetPixelShader(CompileShader(ps_4_0, MyPS()));
		SetGeometryShader(NULL);
    }
}
float4x4 mvp:WORLDVIEWPROJECTION;
float3 freeLight:FREELIGHT;

Texture2D tex;

SamplerState defss
{
	AddressU = Wrap;
	AddressV = Wrap;
};

void MyVS(float3 pos:apos, float2 tc:atexcoord,
		  float3 normal:N,
	out float4 opos:SV_POSITION, out float2 otc:TEX,
	out float3 onormal:NORMAL, out float3 olight:LIGHT)
{
	opos = mul(float4(pos, 1), mvp);
	otc = tc;
	onormal = normal;
	olight = normalize(freeLight - pos);
}

float4 MyPS(float4 pos:SV_POSITION, float2 tc:TEX,
			float3 normal:NORMAL, float3 light:LIGHT):SV_Target
{ 
   float3 normal2 = normalize(normal);
   float3 light2 = normalize(light);
	
   float Idiff = max( dot(normal2, light2), 0.1);

   return tex.Sample(defss, tc) * Idiff;
}

technique11 MyTechnique
{
    pass P0
    {          
		SetVertexShader(CompileShader(vs_4_0, MyVS()));
		SetPixelShader(CompileShader(ps_4_0, MyPS()));
		SetGeometryShader(NULL);
    }
}