From 48f34b7b05f992e51a62266cb02b7529c20de7fe Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Mon, 17 Jul 2006 04:22:14 +0000
Subject: [PATCH] Created a new ComplexShape class which can currently be used
 to automatically split the sort of complicated mesh that contains
 per-point-per-face attributes into NiTriShape classes.  Should support
 skinning as well, but has not been tested. Added TexCoord == operator.
 Changed type of Triangle members to unsigned short. Color4 now defaults to
 all zeros. NiAVObject::AddProperty now takes a const argument. Fixed
 exception in NiTexturingProperty to throw a runtime_error instead of a
 string. NiTriBasedGeom::BindSkin should now automatically finds the skeleton
 root by itself.  The skeleton_root argument has been removed.

---
 ComplexShape.cpp            | 458 ++++++++++++++++++++++++++++++++++++
 ComplexShape.h              |  88 +++++++
 nif_math.cpp                |   9 +
 nif_math.h                  |  18 +-
 niflib.h                    | 125 ----------
 niflib.vcproj               |   8 +
 obj/NiAVObject.cpp          |   2 +-
 obj/NiAVObject.h            |   2 +-
 obj/NiTexturingProperty.cpp |   2 +-
 obj/NiTriBasedGeom.cpp      |  53 ++++-
 obj/NiTriBasedGeom.h        |   5 +-
 11 files changed, 632 insertions(+), 138 deletions(-)
 create mode 100644 ComplexShape.cpp
 create mode 100644 ComplexShape.h

diff --git a/ComplexShape.cpp b/ComplexShape.cpp
new file mode 100644
index 00000000..fe2220df
--- /dev/null
+++ b/ComplexShape.cpp
@@ -0,0 +1,458 @@
+/* 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/NiTriShape.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 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.r != n.color.r && color.g != n.color.g && color.b != n.color.b && color.a != n.color.a ) {
+			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::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 ) 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<NiTriShapeRef> shapes(num_shapes);
+
+	//Loop through each shape slot and create a NiTriShape
+	for ( unsigned int shape_num = 0; shape_num < shapes.size(); ++shape_num ) {
+		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 ) {
+
+		NiTriShapeDataRef 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.001f ) {
+					sk.index = vert_index;
+					sk.weight = wt->second;
+					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 );
+
+			NiSkinDataRef skinData = shapes[shape_num]->GetSkinInstance()->GetSkinData();
+
+			for ( unsigned int inf = 0; inf < shapeInfluences.size(); ++inf ) {
+				skinData->SetBoneWeights( inf, shapeWeights[ shapeInfluences[inf] ] );
+			}
+		}
+		
+		//Next Shape
+	}
+
+	return root;
+}
\ No newline at end of file
diff --git a/ComplexShape.h b/ComplexShape.h
new file mode 100644
index 00000000..83dec729
--- /dev/null
+++ b/ComplexShape.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2006, NIF File Format Library and Tools
+All rights reserved.  Please see niflib.h for licence. */
+
+#ifndef _COMPLEX_SHAPE_H_
+#define _COMPLEX_SHAPE_H_
+
+#include "Ref.h"
+#include "nif_math.h"
+#include "nif_basic_types.h"
+
+namespace Niflib {
+
+// Forward define of referenced classes
+class NiProperty;
+class NiNode;
+class NiAVObject;
+
+class ComplexShape {
+public:
+
+	struct SkinInfluence {
+		unsigned int influenceIndex;
+		float weight;
+	};
+
+	struct WeightedVertex {
+		Vector3 position;
+		vector<SkinInfluence> weights;
+	};
+
+	struct TexCoordIndex {
+		unsigned int texCoordSetIndex;
+		unsigned int texCoordIndex;
+	};
+
+	struct ComplexPoint {
+		unsigned int vertexIndex;
+		unsigned int normalIndex;
+		unsigned int colorIndex;
+		vector<TexCoordIndex> texCoordIndices;
+	};
+
+	struct ComplexFace {
+		vector<ComplexPoint> points;
+		unsigned int propGroupIndex; 
+	};
+
+	struct TexCoordSet {
+		TexType texType;
+		vector<TexCoord> texCoords;
+	};
+
+	Ref<NiAVObject> Split( Ref<NiNode> & parent ) const;
+
+	//Setters
+	void SetName( const string & n );
+	void SetVertices( const vector<WeightedVertex> & n );
+	void SetColors( const vector<Color4> & n );
+	void SetNormals( const vector<Vector3> & n );
+	void SetTexCoordSets( const vector<TexCoordSet> & n );
+	void SetFaces( const vector<ComplexFace> & n );
+	void SetPropGroups( const vector< vector< Ref<NiProperty> > > & n ); 
+	void SetSkinInfluences( const vector< Ref<NiNode> > & n );
+
+	//Getters
+	string GetName() const;
+	vector<WeightedVertex> GetVertices() const;
+	vector<Color4> GetColors() const;
+	vector<Vector3> GetNormals() const;
+	vector<TexCoordSet> GetTexCoordSets() const;
+	vector<ComplexFace> GetFaces() const;
+	vector< vector< Ref<NiProperty> > > GetPropGroups() const;
+	vector< Ref<NiNode> > GetSkinInfluences() const;
+
+private:
+	vector<WeightedVertex> vertices;
+	vector<Color4> colors;
+	vector<Vector3> normals;
+	vector<TexCoordSet> texCoordSets;
+	vector<ComplexFace> faces;
+	vector< vector< Ref<NiProperty> > > propGroups;
+	vector< Ref<NiNode> > skinInfluences;
+	string name;
+};
+
+}
+
+#endif
\ No newline at end of file
diff --git a/nif_math.cpp b/nif_math.cpp
index a6c3d6bd..19c59a7c 100644
--- a/nif_math.cpp
+++ b/nif_math.cpp
@@ -19,6 +19,15 @@ const Matrix33 Matrix33::IDENTITY( 1.0f, 0.0f, 0.0f,
 const Matrix22 Matrix22::IDENTITY( 1.0f, 0.0f,
 								   0.0f, 1.0f );
 
+/* TexCoord Methods
+ *
+ */
+
+bool TexCoord::operator==( const TexCoord & n ) const {
+	return ( u == n.u && v == n.v );
+}
+
+
 /*
  * Vector3 Methods
  */
diff --git a/nif_math.h b/nif_math.h
index 413a82e0..7e3d21e7 100644
--- a/nif_math.h
+++ b/nif_math.h
@@ -55,13 +55,15 @@ struct NIFLIB_API TexCoord {
 		this->u = u;
 		this->v = v;
 	}
+
+	bool operator==( const TexCoord & n ) const;
 };
 
 /*! Represents a triangle face formed between three vertices referenced by number */
 struct NIFLIB_API Triangle {
-	short v1; /*!< The index of the first vertex. */ 
-	short v2; /*!< The index of the second vertex. */ 
-	short v3; /*!< The index of the third vertex. */ 
+	unsigned short v1; /*!< The index of the first vertex. */ 
+	unsigned short v2; /*!< The index of the second vertex. */ 
+	unsigned short v3; /*!< The index of the third vertex. */ 
 
 	/*! Default constructor */
 	Triangle() {}
@@ -71,7 +73,7 @@ struct NIFLIB_API Triangle {
 	 * \param v2 The index of the second vertex.
 	 * \param v3 The index of the third vertex.
 	 */
-	Triangle(short v1, short v2, short v3) {
+	Triangle(unsigned short v1, unsigned short v2, unsigned short v3) {
 		this->v1 = v1;
 		this->v2 = v2;
 		this->v3 = v3;
@@ -82,7 +84,7 @@ struct NIFLIB_API Triangle {
 	 * \param v2 The index of the second vertex.
 	 * \param v3 The index of the third vertex.
 	 */
-	void Set(short v1, short v2, short v3) {
+	void Set(unsigned short v1, unsigned short v2, unsigned short v3) {
 		this->v1 = v1;
 		this->v2 = v2;
 		this->v3 = v3;
@@ -94,7 +96,7 @@ struct NIFLIB_API Triangle {
 	 * \param n The index into the data array.  Should be 0, 1, or 2.
 	 * \return The value at the given array index by reference so it can be read or set via the bracket operator.
 	 */
-	short & operator[](int n) {
+	unsigned short & operator[](int n) {
 		switch (n) {
 			case 0: return v1; break;
 			case 1: return v2; break;
@@ -102,7 +104,7 @@ struct NIFLIB_API Triangle {
 			default: throw std::out_of_range("Index out of range for Triangle");
 		};
 	}
-	short operator[](int n) const {
+	unsigned short operator[](int n) const {
 		switch (n) {
 			case 0: return v1; break;
 			case 1: return v2; break;
@@ -827,7 +829,7 @@ struct NIFLIB_API Color4 {
 	float a; /*!< The alpha translucency component of this color.  Should be between 0.0f and 1.0f. */ 
 
 	/*! Default constructor */
-	Color4() {}
+	Color4() : r(0.0f), g(0.0f), b(0.0f), a(0.0f) {}
 
 	/*! This constructor can be used to set all values in this structure during initialization
 	 * \param r The value to set the red component of this color to.  Should be between 0.0f and 1.0f.
diff --git a/niflib.h b/niflib.h
index 083e1409..f39e2f64 100644
--- a/niflib.h
+++ b/niflib.h
@@ -270,131 +270,6 @@ NIFLIB_API bool IsVersionSupported(unsigned int ver);
  */
 NIFLIB_API unsigned int GetVersion(string version);
 
-//TODO:  This is planned functionality but is unfinished
-//struct ComplexVertex {
-//	ComplexVertex() : has_color(false), has_normal(false), vertex_index(0), normal_index(0), color_index(0) {}
-//	~ComplexVertex();
-//	bool has_color, has_normal;
-//	int vertex_index, color_index, normal_index;
-//	bool has_base_uv, has_glow_uv;
-//	int base_uv_index, glow_uv_index;
-//}
-//
-//struct ComplexFace {
-//	vector<ComplexVertex> points;
-//	int base_map_index;
-//	int glow_map_index;
-//};
-//
-//class ComplexShape {
-//	void SetVertices( vector<Vector3> & vertices );
-//	void SetUVs( vector<TexCoord> & uvs );
-//	void SetColors( vector<Color4> & colors );
-//	void SetNormals( vector<Vector3> & normals );
-//	void SetBones( vector<blk_ref> & bones );
-//	void SetFaces( list< vector< ComplexVertex > > & faces );
-//
-//	vector<Vector3> GetVertices();
-//	vector<TexCoord> GetUVs();
-//	vector<Color4> GetColors();
-//	vector<Vector3> GetNormals();
-//	vector<blk_ref> GetBones();
-//	list< vector< ComplexVertex > > GetFaces();
-//
-//private:
-//	vector<Vector3> _vertices;
-//	vector<Color4> _colors;
-//	vector<Vector3> _normals;
-//	list<ComplexFace> _faces;
-//	map<string, blk_ref> _materials;
-//	map<string, vector<TexCoord> > _uvs;
-//	map<blk_ref, map<int, float> > _bones;
-//};
-//
-//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();
-//		//	}
-//		//}
-//
-//	}
-//}
-
 //--USER GUIDE DOCUMENTATION--//
 
 /*! \mainpage Niflib Documentation
diff --git a/niflib.vcproj b/niflib.vcproj
index 6ce39fc8..563cf0b7 100644
--- a/niflib.vcproj
+++ b/niflib.vcproj
@@ -355,6 +355,10 @@
 			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
 			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
 			>
+			<File
+				RelativePath=".\ComplexShape.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\kfm.cpp"
 				>
@@ -1394,6 +1398,10 @@
 			Filter="h;hpp;hxx;hm;inl;inc;xsd"
 			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
 			>
+			<File
+				RelativePath=".\ComplexShape.h"
+				>
+			</File>
 			<File
 				RelativePath=".\dll_export.h"
 				>
diff --git a/obj/NiAVObject.cpp b/obj/NiAVObject.cpp
index e621bf72..af1ffccb 100644
--- a/obj/NiAVObject.cpp
+++ b/obj/NiAVObject.cpp
@@ -80,7 +80,7 @@ Ref<NiNode> NiAVObject::GetParent() const {
 	return parent;
 }
 
-void NiAVObject::AddProperty( Ref<NiProperty> & obj ) {
+void NiAVObject::AddProperty( const Ref<NiProperty> & obj ) {
 	properties.push_back( obj );
 }
 
diff --git a/obj/NiAVObject.h b/obj/NiAVObject.h
index 582b362f..c888a860 100644
--- a/obj/NiAVObject.h
+++ b/obj/NiAVObject.h
@@ -68,7 +68,7 @@ public:
 
 	Ref<NiNode> GetParent() const;
 
-	void AddProperty( Ref<NiProperty> & obj );
+	void AddProperty( const Ref<NiProperty> & obj );
 	void RemoveProperty( Ref<NiProperty> obj );
 	void ClearProperties();
 	vector< Ref<NiProperty> > GetProperties() const;
diff --git a/obj/NiTexturingProperty.cpp b/obj/NiTexturingProperty.cpp
index 1c5c03f7..7d83ef6d 100644
--- a/obj/NiTexturingProperty.cpp
+++ b/obj/NiTexturingProperty.cpp
@@ -222,7 +222,7 @@ bool NiTexturingProperty::HasTexture( int n ) const {
 		case DECAL_1_MAP:
 			return hasDecal1Texture;
 		default:
-			throw("You have specified an invalid texture type.");
+			throw runtime_error("You have specified an invalid texture type.");
 	};
 }
 
diff --git a/obj/NiTriBasedGeom.cpp b/obj/NiTriBasedGeom.cpp
index ed34038c..bfd7f39e 100644
--- a/obj/NiTriBasedGeom.cpp
+++ b/obj/NiTriBasedGeom.cpp
@@ -73,12 +73,31 @@ Ref<NiSkinInstance> NiTriBasedGeom::GetSkinInstance() const {
 	return skinInstance;
 }
 
-void NiTriBasedGeom::BindSkin( Ref<NiNode> skeleton_root, vector< Ref<NiNode> > bone_nodes ) {
+void NiTriBasedGeom::BindSkin( vector< Ref<NiNode> > bone_nodes ) {
 	//Ensure skin is not aleady bound
 	if ( skinInstance != 0 ) {
 		throw runtime_error("You have attempted to re-bind a skin that is already bound.  Unbind it first.");
 	}
 
+	//--Find a suitable skeleton root--//
+
+	//create a list of nodes that have an influence or this TriBasedGeom
+	//as decendents
+	list<NiAVObjectRef> ancestors;
+
+	NiAVObjectRef shape_tree_top = ListAncestors( StaticCast<NiAVObject>(this), ancestors );
+	NiAVObjectRef bone_tree_top;
+
+	for ( unsigned int i = 0; i < bone_nodes.size(); ++i ) {
+		bone_tree_top = ListAncestors( StaticCast<NiAVObject>(bone_nodes[i]), ancestors );
+		//Make sure bone and shapre are part of the same tree
+		if ( bone_tree_top != shape_tree_top ) {
+			throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place.");
+		}
+	}
+
+	NiNodeRef skeleton_root = FindFirstCommonAncestor( shape_tree_top, ancestors );
+
 	//Create a skin instance using the bone and root data
 	skinInstance = new NiSkinInstance( skeleton_root, bone_nodes );
 
@@ -86,6 +105,38 @@ void NiTriBasedGeom::BindSkin( Ref<NiNode> skeleton_root, vector< Ref<NiNode> >
 	skinInstance->SetSkinData( new NiSkinData( this ) );
 };
 
+Ref<NiAVObject> NiTriBasedGeom::ListAncestors( const Ref<NiAVObject> & leaf, list< Ref<NiAVObject> > & ancestors ) const {
+	NiAVObjectRef avObj = leaf;
+	while ( avObj->GetParent() != NULL ) {
+		ancestors.push_back(avObj);
+		avObj = avObj->GetParent();
+	}
+
+	//Return top of tree
+	return avObj;
+}
+
+Ref<NiNode> NiTriBasedGeom::FindFirstCommonAncestor( const Ref<NiAVObject> & avObj, const list< Ref<NiAVObject> > & ancestors ) const {
+	//See if we've found the common ancestor yet
+	for ( list<NiAVObjectRef>::const_iterator ancestor = ancestors.begin(); ancestor != ancestors.end(); ++ancestor ) {
+		if ( *ancestor == avObj ) {
+			//We found the common ancestor, return it.
+			return DynamicCast<NiNode>(avObj);
+		}
+	}
+	
+	//Call this function on any children
+	NiNodeRef niNode = DynamicCast<NiNode>(avObj);
+	if ( niNode != NULL ) {
+		vector<NiAVObjectRef> children = niNode->GetChildren();
+		for ( uint i = 0; i < children.size(); ++i ) {
+			return FindFirstCommonAncestor( children[i], ancestors );
+		}
+	} else {
+		return NULL;
+	}
+}
+
 void NiTriBasedGeom::UnbindSkin() {
 	//Clear skin instance
 	skinInstance = NULL;
diff --git a/obj/NiTriBasedGeom.h b/obj/NiTriBasedGeom.h
index b12c5be5..82d28da5 100644
--- a/obj/NiTriBasedGeom.h
+++ b/obj/NiTriBasedGeom.h
@@ -46,7 +46,7 @@ public:
 	 * NiSkinInstance and NiSkinData class. The bones must have a common
 	 * ancestor in the scenegraph.  This becomes the skeleton root.
 	 */
-	void BindSkin( Ref<NiNode> skeleton_root, vector< Ref<NiNode> > bone_nodes );
+	void BindSkin( vector< Ref<NiNode> > bone_nodes );
 	void UnbindSkin();
 	Ref<NiSkinInstance> GetSkinInstance() const;
 
@@ -62,6 +62,9 @@ public:
 	vector<Vector3> GetSkinInfluencedVertices() const;
 
 protected:
+	Ref<NiAVObject> ListAncestors( const Ref<NiAVObject> & leaf, list< Ref<NiAVObject> > & ancestors ) const;
+	Ref<NiNode> FindFirstCommonAncestor( const Ref<NiAVObject> & avObj, const list< Ref<NiAVObject> > & ancestors ) const;
+
 	NI_TRI_BASED_GEOM_MEMBERS
 	STANDARD_INTERNAL_METHODS
 };
-- 
GitLab