/* Copyright (c) 2006, NIF File Format Library and Tools
All rights reserved.  Please see niflib.h for licence. */

#include "ComplexShape.h"
#include "obj/NiNode.h"
#include "obj/NiProperty.h"
#include "obj/NiAVObject.h"
#include "obj/NiTriBasedGeom.h"
#include "obj/NiTriShape.h"
#include "obj/NiTriStrips.h"
#include "obj/NiTriStripsData.h"
#include "obj/NiTriShapeData.h"
#include "obj/NiTexturingProperty.h"
#include "obj/NiSkinInstance.h"
#include "obj/NiSkinData.h"
#include "gen/SkinWeight.h"
#include <stdlib.h>

using namespace Niflib;

struct VertNorm {
	Vector3 position;
	Vector3 normal;
	map<NiNodeRef, float> weights;

	VertNorm() {}
	~VertNorm() {}
	VertNorm( const VertNorm & n ) {
		*this = n;
	}
	VertNorm & operator=( const VertNorm & n ) {
		position = n.position;
		normal = n.normal;
		weights = n.weights;
		return *this;
	}
	bool operator==( const VertNorm & n ) {
		if ( abs(position.x - n.position.x) > 0.001 || abs(position.y - n.position.y) > 0.001 || abs(position.z - n.position.z) > 0.001 ) {
			return false;
		}
		if ( abs(normal.x - n.normal.x) > 0.001 || abs(normal.y - n.normal.y) > 0.001 || abs(normal.z - n.normal.z) > 0.001 ) {
			return false;
		}
		//if ( weights != n.weights ) {
		//	return false;
		//}

		return true;
	}
};


struct CompoundVertex {
	Vector3 position;
	Vector3 normal;
	Color4 color;
	map<TexType, TexCoord> texCoords;
	map<NiNodeRef, float> weights;

	CompoundVertex() {}
	~CompoundVertex() {}
	CompoundVertex( const CompoundVertex & n ) {
		*this = n;
	}
	CompoundVertex & operator=( const CompoundVertex & n ) {
		position = n.position;
		normal = n.normal;
		color = n.color;
		texCoords = n.texCoords;
		weights = n.weights;
		return *this;
	}
	bool operator==( const CompoundVertex & n ) {
		if ( position != n.position ) {
			return false;
		}
		if ( normal != n.normal ) {
			return false;
		}
		if ( color != n.color ) {
			return false;
		}
		if ( texCoords != n.texCoords ) {
			return false;
		}
		if ( weights != n.weights ) {
			return false;
		}

		return true;
	}
};

void ComplexShape::SetName( const string & n ) {
	name = n;
}

void ComplexShape::SetVertices( const vector<WeightedVertex> & n ) {
	vertices = n;
}

void ComplexShape::SetColors( const vector<Color4> & n ) {
	colors = n;
}

void ComplexShape::SetNormals( const vector<Vector3> & n ) {
	normals = n;
}

void ComplexShape::SetTexCoordSets( const vector<TexCoordSet> & n ) {
	texCoordSets = n;
}

void ComplexShape::SetFaces( const vector< ComplexFace > & n ) {
	faces = n;
}

void ComplexShape::SetPropGroups( const vector< vector< Ref<NiProperty> > > & n ) {
	propGroups = n;
} 

void ComplexShape::SetSkinInfluences( const vector< Ref<NiNode> > & n ) {
	skinInfluences = n;
}


string ComplexShape::GetName() const {
	return name;
}

vector<ComplexShape::WeightedVertex> ComplexShape::GetVertices() const {
	return vertices;
}

vector<Color4> ComplexShape::GetColors() const {
	return colors;
}

vector<Vector3> ComplexShape::GetNormals() const {
	return normals;
}

vector<ComplexShape::TexCoordSet> ComplexShape::GetTexCoordSets() const {
	return texCoordSets;
}

vector< ComplexShape::ComplexFace > ComplexShape::GetFaces() const {
	return faces;
}

vector< vector< Ref<NiProperty > > > ComplexShape::GetPropGroups() const {
	return propGroups;
}

vector< Ref<NiNode> > ComplexShape::GetSkinInfluences() const {
	return skinInfluences;
}

void ComplexShape::Clear() {
	vertices.clear();
	colors.clear();
	normals.clear();
	texCoordSets.clear();
	faces.clear();
	propGroups.clear();
	skinInfluences.clear();
	name.clear();
}

struct MergeLookUp {
	unsigned vertIndex;
	unsigned normIndex;
	unsigned colorIndex;
	map<unsigned, unsigned> uvIndices; //TexCoordSet Index, TexCoord Index
};

void ComplexShape::Merge( const Ref<NiAVObject> & root ) {

	if ( root == NULL ) {
		throw runtime_error("Called ComplexShape::Merge with a null root reference.");
	}

	vector<NiTriBasedGeomRef> shapes;

	//cout << "Determine root type" << endl;
	if ( root->IsDerivedType( NiTriBasedGeom::TypeConst() ) ) {
		//The function was called on a single shape.
		//Add it to the list
		shapes.push_back( DynamicCast<NiTriBasedGeom>(root) );
	} else if ( root->IsDerivedType( NiNode::TypeConst() ) ) {
		//The function was called on a NiNOde.  Search for
		//shape children
		NiNodeRef nodeRoot = DynamicCast<NiNode>(root);
		vector<NiAVObjectRef> children = nodeRoot->GetChildren();
		for ( unsigned child = 0; child < children.size(); ++child ) {
			if ( children[child]->IsDerivedType( NiTriBasedGeom::TypeConst() ) ) {
				shapes.push_back( DynamicCast<NiTriBasedGeom>(children[child]) );
			}
		}

		if ( shapes.size() == 0 ) {
			throw runtime_error("The NiNode passed to ComplexShape::Merge has no shape children.");
		}
	} else {
		throw runtime_error(" The ComplexShape::Merge function requies either a NiNode or a NiTriBasedGeom AVObject.");
	}

	//The vector of VertNorm struts allows us to to refuse
	//to merge vertices that have different normals.
	vector<VertNorm> vns;

	//cout << "Clear all existing data" << endl;
	//Clear all existing data
	Clear();

	//cout << "Merge in data from each shape" << endl;
	//Merge in data from each shape
	bool has_any_verts = false;
	bool has_any_norms = false;
	propGroups.resize( shapes.size() );
	unsigned prop_group_index = 0;
	for ( vector<NiTriBasedGeomRef>::iterator geom = shapes.begin(); geom != shapes.end(); ++geom ) {
	
		//cout << "Merging in " << *geom << endl;
		//Get properties of this shape
		propGroups[prop_group_index] = (*geom)->GetProperties();
		
		
		NiTriBasedGeomDataRef geomData = (*geom)->GetData();

		if ( geomData == NULL ) {
			throw runtime_error("One of the NiTriBasedGeom found by ComplexShape::Merge with a NiTriBasedGeom has no NiTriBasedGeomData attached.");
		}

		//cout << "Get Data" << endl;
		//Get Data
		vector<Vector3> shapeVerts;		
		//If this is a skin influenced mesh, get vertices from niGeom
		if ( (*geom)->GetSkinInstance() != NULL ) {
			shapeVerts = (*geom)->GetSkinInfluencedVertices();
		} else {
			shapeVerts = geomData->GetVertices();
		}

		vector<Vector3> shapeNorms = geomData->GetNormals();
		vector<Color4> shapeColors = geomData->GetColors();
		vector< vector<TexCoord> > shapeUVs( geomData->GetUVSetCount() );
		for ( unsigned i = 0; i < shapeUVs.size(); ++i ) {
			shapeUVs[i] = geomData->GetUVSet(i);
		}
		vector<Triangle> shapeTris= geomData->GetTriangles();

		//Lookup table
		vector<MergeLookUp> lookUp( geomData->GetVertexCount() );

		//cout << "Vertices and normals" << endl;
		//Vertices and normals
		if ( shapeVerts.size() != 0 ) {
			has_any_verts = true;
		}

		bool shape_has_norms = ( shapeNorms.size() == shapeVerts.size() );

		if ( shape_has_norms ) {
			has_any_norms = true;
		}
		for ( unsigned v = 0; v < shapeVerts.size(); ++v ) {
			VertNorm newVert;

			newVert.position = shapeVerts[v];
			if ( shape_has_norms ) {
				newVert.normal = shapeNorms[v];
			}

			//Search for matching vert/norm
			bool match_found = false;
			for ( unsigned vn_index = 0; vn_index < vns.size(); ++vn_index ) {
				if ( vns[vn_index] == newVert ) {
					//Match found, use existing index
					lookUp[v].vertIndex = vn_index;
					if ( shapeNorms.size() != 0 ) {
						lookUp[v].normIndex = vn_index;
					}
					match_found = true;
					//Stop searching
					break;
				}
			}

			if ( match_found == false ) {
				//No match found, add this vert/norm to the list
				vns.push_back(newVert);
				//Record new index
				lookUp[v].vertIndex = unsigned(vns.size()) - 1;
				if ( shapeNorms.size() != 0 ) {
					lookUp[v].normIndex = unsigned(vns.size()) - 1;
				}
			}
		}

		//cout << "Colors" << endl;
		//Colors
		for ( unsigned c = 0; c < shapeColors.size(); ++c ) {
			Color4 newColor;

			newColor = shapeColors[c];

			//Search for matching color
			bool match_found = false;
			for ( unsigned c_index = 0; c_index < colors.size(); ++c_index ) {
				if ( colors[c_index].r == newColor.r && colors[c_index].g == newColor.g && colors[c_index].b == newColor.b && colors[c_index].a == newColor.a ) {
					//Match found, use existing index
					//cout << "Color match found:  " << colors[c_index] << " and " << newColor << " at index " << c_index << endl;
					lookUp[c].colorIndex = c_index;
					match_found = true;
					//Stop searching
					break;
				}
			}

			if ( match_found == false ) {
				//No match found, add this color to the list
				colors.push_back(newColor);
				//Record new index
				lookUp[c].colorIndex = unsigned(colors.size()) - 1;
				//cout << "No Match found.  Placed new color " << newColor << " at lookUp[" << c << "].colorIndex:  " << lookUp[c].colorIndex << endl;
			}
		}

		//cout << "Texture Coordinates" << endl;
		//Texture Coordinates

		//Create UV set list
		vector<TexType> uvSetList;
		NiPropertyRef niProp = (*geom)->GetPropertyByType( NiTexturingProperty::TypeConst() );
		NiTexturingPropertyRef niTexProp;
		if ( niProp != NULL ) {
			niTexProp = DynamicCast<NiTexturingProperty>(niProp);
		}
		if ( niTexProp != NULL ) {
			for ( int tex = 0; tex < 8; ++tex ) {
				if ( niTexProp->HasTexture(tex) == true ) {
					////cout << "Adding texture type to list:  " << TexType(tex) << endl;
					uvSetList.push_back( TexType(tex) );
				}
			}
		}

		for ( unsigned set = 0; set < shapeUVs.size(); ++set ) {
			TexType newType = BASE_MAP;
			if ( uvSetList.size() > set ) {
				 newType = uvSetList[set];
			}

			//Search for matching UV set
			bool match_found = false;
			unsigned uvSetIndex;
			for ( unsigned set_index = 0; set_index < texCoordSets.size(); ++set_index ) {
				if ( texCoordSets[set_index].texType  == newType ) {
					////cout << "Match found, use existing texture set index" << endl;
					//Match found, use existing index
					uvSetIndex = set_index;
					match_found = true;
					//Stop searching
					break;
				}
			}

			if ( match_found == false ) {
				////cout << "No match found, add this texture set to the list" << endl;
				//No match found, add this UV set to the list
				TexCoordSet newTCS;
				newTCS.texType = newType;
				texCoordSets.push_back( newTCS );
				//Record new index
				uvSetIndex = unsigned(texCoordSets.size()) - 1;
			}

			////cout << "Loop through texture cooridnates in this set" << endl;
			for ( unsigned v = 0; v < shapeUVs[set].size(); ++v ) {
				TexCoord newCoord;

				newCoord = shapeUVs[set][v];

				//cout << "Search for matching texture coordinate" << endl;
				//cout << "uvSetIndex:  " << uvSetIndex << endl;
				//cout << "set:  " << set << endl;
				//cout << "texCoordSets.size():  " << unsigned(texCoordSets.size()) << endl;
				//cout << "v:  " << v << endl;
				//cout << "lookUp.size():  " << unsigned(lookUp.size()) << endl;
				//cout << "texCoordSets[uvSetIndex].texCoords.size():  " << unsigned(texCoordSets[uvSetIndex].texCoords.size()) << endl;
				//Search for matching texture cooridnate
				bool match_found = false;
				for ( unsigned tc_index = 0; tc_index < texCoordSets[uvSetIndex].texCoords.size(); ++tc_index ) {
					if ( texCoordSets[uvSetIndex].texCoords[tc_index]  == newCoord ) {
						////cout << " Match found, using existing index" << endl;;
						//Match found, use existing index
						lookUp[v].uvIndices[uvSetIndex] = tc_index;
						match_found = true;
						////cout << "Stop searching" << endl;
						//Stop searching
						break;
					}
				}

				////cout << "Done with loop, check if match was found" << endl;
				if ( match_found == false ) {
					////cout << "No match found" << endl;
					//No match found, add this texture coordinate to the list
					texCoordSets[uvSetIndex].texCoords.push_back( newCoord );
					////cout << "Record new index" << endl;
					//Record new index
					lookUp[v].uvIndices[uvSetIndex] = unsigned(texCoordSets[uvSetIndex].texCoords.size()) - 1;
				}
			}
		}

		//cout << "Look up table colors:" << endl;
		for ( unsigned z = 0; z < lookUp.size(); ++z ) {
			//cout << z << ":  " << colors[lookUp[z].colorIndex] << endl;
		}

		//cout << "Use look up table to build list of faces" << endl;
		//Use look up table to build list of faces
		for ( unsigned t = 0; t < shapeTris.size(); ++t ) {
			ComplexFace newFace;
			newFace.propGroupIndex = prop_group_index;
			newFace.points.resize(3);
			const Triangle & tri = shapeTris[t];
			for ( unsigned p = 0; p < 3; ++p ) {
				if ( shapeVerts.size() != 0 ) {
					newFace.points[p].vertexIndex = lookUp[ tri[p] ].vertIndex;
				}
				if ( shapeNorms.size() != 0 ) {
					newFace.points[p].normalIndex = lookUp[ tri[p] ].normIndex;
				}
				if ( shapeColors.size() != 0 ) {
					newFace.points[p].colorIndex = lookUp[ tri[p] ].colorIndex;
				}
				for ( map<unsigned,unsigned>::iterator set = lookUp[ tri[p] ].uvIndices.begin(); set != lookUp[ tri[p] ].uvIndices.end(); ++set ) {
					TexCoordIndex tci;
					tci.texCoordSetIndex = set->first;
					tci.texCoordIndex = set->second;
					newFace.points[p].texCoordIndices.push_back(tci);
				}
			}
			faces.push_back(newFace);
		}

		//cout << "Use look up table to set vertex wights, if any" << endl;
		//Use look up table to set vertex weights, if any
		NiSkinInstanceRef skinInst = (*geom)->GetSkinInstance();

		if ( skinInst != NULL ) {

			NiSkinDataRef skinData = skinInst->GetSkinData();

			if ( skinData !=NULL ) {
				//Get influence list
				vector<NiNodeRef> shapeBones = skinInst->GetBones();

				//Get weights
				vector<SkinWeight> shapeWeights;
				for ( unsigned b = 0; b < shapeBones.size(); ++b ) {
					shapeWeights = skinData->GetBoneWeights(b);
					for ( unsigned w = 0; w < shapeWeights.size(); ++w ) {
						unsigned vn_index = lookUp[ shapeWeights[w].index ].vertIndex;
						NiNodeRef boneRef = shapeBones[b];
						float weight = shapeWeights[w].weight;

						vns[vn_index].weights[boneRef] = weight;
					}
				}
			}
		}

		//Next Shape
		++prop_group_index;
	}

	//cout << "Finished with all shapes.  Build up a list of influences" << endl;
	//Finished with all shapes.  Build up a list of influences
	map<NiNodeRef,unsigned> boneLookUp;
	for ( unsigned v = 0; v < vns.size(); ++v ) {
		for ( map<NiNodeRef,float>::iterator w = vns[v].weights.begin(); w != vns[v].weights.end(); ++w ) {
			boneLookUp[w->first] = 0; //will change later
		}
	}

	skinInfluences.resize( boneLookUp.size() );
	unsigned si_index = 0;
	for ( map<NiNodeRef,unsigned>::iterator si = boneLookUp.begin(); si != boneLookUp.end(); ++si ) {
		si->second = si_index;
		skinInfluences[si_index] = si->first;
		++si_index;
	}
	
	//cout << "Copy vns data to vertices and normals" << endl;
	//Copy vns data to vertices and normals
	if ( has_any_verts ) {
		vertices.resize( vns.size() );
	}
	if ( has_any_norms ) {
		normals.resize( vns.size() );
	}

	for ( unsigned v = 0; v < vns.size(); ++v ) {
		if ( has_any_verts ) {
			vertices[v].position = vns[v].position;
			vertices[v].weights.resize( vns[v].weights.size() );
			unsigned weight_index = 0;
			for ( map<NiNodeRef,float>::iterator w = vns[v].weights.begin(); w != vns[v].weights.end(); ++w ) {
				vertices[v].weights[weight_index].influenceIndex = boneLookUp[w->first];
				vertices[v].weights[weight_index].weight = w->second;
				++weight_index;
			}
		}
		if ( has_any_norms ) {
			normals[v] = vns[v].normal;
		}
	}
	//cout << "Done Merging" << endl;
}

//void ComplexShape::CombineTriShapes( list<blk_ref> & tri_shapes ) {
//	//Clear all internal datea
//	_vertices.clear();
//	_colors.clear();
//	_normals.clear();
//	_materials.clear();
//	_uvs.clear();
//	_faces.clear();
//	_bones.clear();
//
//	//Create a temporary spot to hold the triangle lists from each TriShape
//	vector< vector<Triangle> > ts_faces;
//
//	//Create lists to hold the lookup tables
//	vector<int> tri_lookup, nor_lookup, col_lookup;
//	map<string, vector<int> > mat_lookup, uv_lookup;
//	
//	//--Cycle through all the TriShapes, adding their data to the lists--//
//	list<blk_ref>::iterator it;
//
//	for (it = tri_shapes.begin(); it != tri_shapes.end(); ++it) {
//		ITriShapeData * data = QueryTriShapeData(*it);
//
//		//Vertices
//		vector<Vector3> ts_verts = data->GetVertices();
//		_vertices.insert(_vertices.end(), ts_verts.begin(), ts_verts.end();
//
//		//Normals
//		vector<Vector3> ts_norms = data->GetNormals();
//		_normals.insert(_normals.end(), ts_norms.begin(), ts_norms.end();
//		
//		//Colors
//		vector<Colors> ts_cols = data->GetColors();
//		_colors.insert(_colors.end(), ts_colors.begin(), ts_colors.end();
//
//		//Triangles
//		ts_faces[i] = data->GetTriangles();
//
//		//UV Coords
//		vector< vector<TexCoords> > uvs(data->GetUVSetCount());
//		for (int i = 0; i < data->GetUVSetCount(); ++i) {
//			uvs[i] = data->GetUVSet(i);
//		}
//
//		//Associate UV Coord Data with proper map name
//		blk_ref tx_prop = par["Properties"]->FindLink( "NiTexturingProperty");
//		if ( tx_prop.is_null() == false ) {
//			int uv_set = 0;
//			for (int i = 0; i < 7; ++i) {
//				string attr_name, map;
//				switch(i) {
//					case 0:	attr_name = "Base Texture";     map = "map1";   break;
//					case 1:	attr_name = "Dark Texture";     map = "dark";   break;
//					case 2:	attr_name = "Detail Texture";   map = "detail"; break;
//					case 3:	attr_name = "Gloss Texture";    map = "gloss";  break;
//					case 4:	attr_name = "Glow Texture";     map = "glow";   break;
//					case 5:	attr_name = "Bump Map Texture"; map = "bump";   break;
//					case 6:	attr_name = "Decal 0 Texture";  map = "decal0";
//				}
//
//				if ( tx_prop[attr_name]->asTexDesc().isUsed == true ) {
//					//How to merge all UVs?
//				}
//
//		}
//
//
//		//blk_ref material = (*it)->GetAttr("Propreties")->FindLink("NiMaterialProperty");
//		//blk_ref skin_inst = (*it)->GetAttr("Skin Instance")->asLink();
//		//blk_ref skin_data;
//		//vector<blk_ref> bones;
//		//map<int, float> weights;
//		//if ( skin_inst.is_null() == false ) {
//		//	skin_block = skin_inst->GetAttr("Data");
//		//	if (skin_block.is_null() == false ) {
//		//		ISkinData * skin_data = QuerySkinData(skin_block);
//		//		weights = skin_data->GetWeights();
//		//		bones = skin_data->GetBones();
//		//	}
//		//}
//
//	}
//}

Ref<NiAVObject> ComplexShape::Split( Ref<NiNode> & parent, bool hw_skin_data, bool stripify ) const {

	//Make sure parent is not NULL
	if ( parent == NULL ) {
		throw runtime_error ("A parent is necessary to split a complex shape.");
	}

	//There will be one NiTriShape per property group
	//with a minimum of 1
	unsigned int num_shapes = unsigned int(propGroups.size());
	if ( num_shapes == 0 ) {
		num_shapes = 1;
	}

	vector<NiTriBasedGeomRef> shapes(num_shapes);

	//Loop through each shape slot and create a NiTriShape
	for ( unsigned int shape_num = 0; shape_num < shapes.size(); ++shape_num ) {
		if ( stripify ) {
			shapes[shape_num] = new NiTriStrips;
		} else {
			shapes[shape_num] = new NiTriShape;
		}
	}

	NiAVObjectRef root;

	//If there is just one shape, return it.  Otherwise
	//create a node, parent all shapes to it, and return
	// that
	if ( shapes.size() == 1 ) {
		//One shape
		shapes[0]->SetName(name);
		root = StaticCast<NiAVObject>(shapes[0]);
	} else {
		//Multiple shapes
		NiNodeRef niNode = new NiNode;
		niNode->SetName(name);
		for ( unsigned int i = 0; i < shapes.size(); ++i ) {
			niNode->AddChild( StaticCast<NiAVObject>(shapes[i]) );

			//Set Shape Name
			stringstream shapeName;
			shapeName << name << " " << i;
			shapes[i]->SetName( shapeName.str() );
		}
		root = StaticCast<NiAVObject>(niNode);
	}

	parent->AddChild( root );

	//Create NiTriShapeData and fill it out with all data that is relevant
	//to this shape based on the material.
	for ( unsigned int shape_num = 0; shape_num < shapes.size(); ++shape_num ) {

		NiTriBasedGeomDataRef niData;
		
		if ( stripify ) {
			niData = new NiTriStripsData;
		} else {
			niData = new NiTriShapeData;
		}
		shapes[shape_num]->SetData( StaticCast<NiTriBasedGeomData>(niData) );

		//Create a list of CompoundVertex to make it easier to
		//test for the need to clone a vertex
		vector<CompoundVertex> compVerts;

		//List of triangles for the final shape to use
		vector<Triangle> shapeTriangles;

		//Loop through all faces, and all points on each face
		//to set the vertices in the CompoundVertex list
		for ( vector<ComplexFace>::const_iterator face = faces.begin(); face != faces.end(); ++face ) {
			//Ignore faces with less than 3 vertices
			if ( face->points.size() < 3 ) {
				continue;
			}

			//Skip this face if the material does not relate to this shape
			if ( face->propGroupIndex != shape_num ) {
				continue;
			}

			vector<unsigned short> shapeFacePoints;
			for ( vector<ComplexPoint>::const_iterator point = face->points.begin(); point != face->points.end(); ++point ) {

				//--Set up Compound vertex--//
				CompoundVertex cv;

				if ( vertices.size() > 0 ) {
					const WeightedVertex & wv = vertices[point->vertexIndex];
					cv.position = wv.position;

					if ( skinInfluences.size() > 0 ) {
						for ( unsigned int i = 0; i < wv.weights.size(); ++i ) {
							const SkinInfluence & inf = wv.weights[i];

							cv.weights[ skinInfluences[inf.influenceIndex] ] = inf.weight;
						}
					}
				}

				if ( normals.size() > 0 ) {
					cv.normal = normals[point->normalIndex];
				}
				if ( colors.size() > 0 ) {
					cv.color = colors[point->colorIndex];
				}

				if ( texCoordSets.size() > 0 ) {
					for ( unsigned int i = 0; i < point->texCoordIndices.size(); ++i ) {
						const TexCoordSet & set = texCoordSets[ point->texCoordIndices[i].texCoordSetIndex ];

						cv.texCoords[ set.texType ] = set.texCoords[ point->texCoordIndices[i].texCoordIndex ];
					}
				}
				
				bool found_match = false;
				//Search for an identical vertex in the list
				for ( unsigned short cv_index = 0; cv_index < compVerts.size(); ++cv_index ) {
					if ( compVerts[cv_index] == cv ) {
						//We found a match, push its index into the face list
						found_match = true;
						shapeFacePoints.push_back(cv_index);
						break;
					}
				}

				//If no match was found, append this vertex to the list
				if ( found_match == false ) {
					compVerts.push_back(cv);
					//put the new vertex into the face point list
					shapeFacePoints.push_back( unsigned int(compVerts.size()) - 1 );
				}
				
				//Next Point
			}

			//Starting from vertex 0, create a fan of triangles to fill
			//in non-triangle polygons
			Triangle new_face;
			for ( unsigned int i = 0; i < shapeFacePoints.size() - 2; ++i ) {
				new_face[0] = shapeFacePoints[0];
				new_face[1] = shapeFacePoints[i+1];
				new_face[2] = shapeFacePoints[i+2];

				//Push the face into the face list
				shapeTriangles.push_back(new_face);
			}

			//Next Face
		}

		//Attatch properties if any
		if ( propGroups.size() > 0 ) {
			for ( vector<NiPropertyRef>::const_iterator prop = propGroups[shape_num].begin(); prop != propGroups[shape_num].end(); ++prop ) {
				shapes[shape_num]->AddProperty( *prop );						
			}
		}

		//--Set Shape Data--//

		//lists to hold data
		vector<Vector3> shapeVerts( compVerts.size() );
		vector<Vector3> shapeNorms( compVerts.size() );
		vector<Color4> shapeColors( compVerts.size() );
		vector< vector<TexCoord> > shapeTCs;
		list<int> shapeTexCoordSets;
		map<NiNodeRef, vector<SkinWeight> > shapeWeights;

		//Search for a NiTexturingProperty to build list of
		//texture cooridinate sets to create
		NiPropertyRef niProp = shapes[shape_num]->GetPropertyByType( NiTexturingProperty::TypeConst() );
		NiTexturingPropertyRef niTexProp;
		if ( niProp != NULL ) {
			niTexProp = DynamicCast<NiTexturingProperty>(niProp);
		}
		if ( niTexProp != NULL ) {
			for ( int tex_num = 0; tex_num < 8; ++tex_num ) {
				if (niTexProp->HasTexture(tex_num)) {
					shapeTexCoordSets.push_back(tex_num);
				}
			}
		} else {
			//Always include the base map if it's there, whether there's a
			//texture or not
			shapeTexCoordSets.push_back( BASE_MAP );
		}
		shapeTCs.resize( shapeTexCoordSets.size() );
		for ( vector< vector<TexCoord> >::iterator set = shapeTCs.begin(); set != shapeTCs.end(); ++set ) {
			set->resize( compVerts.size() );
		}

		//Loop through all compound vertices, adding the data
		//to the correct arrays.
		uint vert_index = 0;
		for ( vector<CompoundVertex>::iterator cv = compVerts.begin(); cv != compVerts.end(); ++cv ) {
			shapeVerts[vert_index] = cv->position;
			shapeColors[vert_index] = cv->color;
			shapeNorms[vert_index] = cv->normal;
			shapeNorms[vert_index] = cv->normal;
			uint tex_index = 0;
			for ( list<int>::iterator tex = shapeTexCoordSets.begin(); tex != shapeTexCoordSets.end(); ++tex ) {
				if ( cv->texCoords.find( TexType(*tex) ) != cv->texCoords.end() ) {
					shapeTCs[tex_index][vert_index] = cv->texCoords[ TexType(*tex) ];
				}
				tex_index++;
			}
			SkinWeight sk;
			for ( map<NiNodeRef, float>::iterator wt = cv->weights.begin(); wt != cv->weights.end(); ++wt ) {
				//Only record influences that make a noticable contribution
				if ( wt->second > 0.0f ) {
					sk.index = vert_index;
					sk.weight = wt->second;
					if ( shapeWeights.find( wt->first ) == shapeWeights.end() ) {
						shapeWeights[wt->first] = vector<SkinWeight>();
					}
					shapeWeights[wt->first].push_back( sk );
				}
			}

			++vert_index;
		}

		//Finally, set the data into the NiTriShapeData
		if ( vertices.size() > 0 ) {
			niData->SetVertices( shapeVerts );
			niData->SetTriangles( shapeTriangles );
		}
		if ( normals.size() > 0 ) {
			niData->SetNormals( shapeNorms );
		}
		if ( colors.size() > 0 ) {
			niData->SetVertexColors( shapeColors );
		}
		if ( texCoordSets.size() > 0 ) {
			niData->SetUVSetCount( int(shapeTCs.size()) );
			for ( unsigned int tex_index = 0; tex_index < shapeTCs.size(); ++tex_index ) {
				niData->SetUVSet( tex_index, shapeTCs[tex_index] );
			}
		}

		//If there are any skin influences, bind the skin
		if ( shapeWeights.size() > 0 ) {
			vector<NiNodeRef> shapeInfluences;
			for ( map<NiNodeRef, vector<SkinWeight> >::iterator inf = shapeWeights.begin(); inf != shapeWeights.end(); ++inf ) {
				shapeInfluences.push_back( inf->first );
			}

			shapes[shape_num]->BindSkin( shapeInfluences );

			for ( unsigned int inf = 0; inf < shapeInfluences.size(); ++inf ) {
				shapes[shape_num]->SetBoneWeights( inf, shapeWeights[ shapeInfluences[inf] ] );
			}

			if ( hw_skin_data ) {
				shapes[shape_num]->GenHardwareSkinInfo();
			}

			//NiSkinInstanceRef skinInst = shapes[shape_num]->GetSkinInstance();

			//if ( skinInst != NULL ) {
			//	NiSkinDataRef skinData = skinInst->GetSkinData();

			//	if ( skinData != NULL ) {
			//		for ( unsigned int inf = 0; inf < shapeInfluences.size(); ++inf ) {
			//			skinData->SetBoneWeights( inf, shapeWeights[ shapeInfluences[inf] ] );
			//		}
			//	}
			//}
		}
		
		//Next Shape
	}

	return root;
}