From 1ba7c1de06390950c91d3d12b76ed5a625bff947 Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Thu, 22 Jun 2006 01:53:39 +0000
Subject: [PATCH] Changed Matrix44::Decompose to set a single averaged scale
 value. Finished Skin API except for NiSkinPartition.  Part of this was making
 NiSkinData read-only with a new interface. Fixed a bug in the SubtractRef
 function.

---
 Ref.h                  | 20 ++++-----
 nif_math.cpp           |  9 ++--
 nif_math.h             |  2 +-
 obj/NiNode.cpp         | 67 ++++++++---------------------
 obj/NiObject.cpp       |  2 +-
 obj/NiSkinData.cpp     | 79 +++++++++++++++++++++++++++++-----
 obj/NiSkinData.h       | 16 +++++--
 obj/NiSkinInstance.cpp | 97 +++++++++++++++++++-----------------------
 obj/NiSkinInstance.h   | 29 ++++---------
 obj/NiTriBasedGeom.cpp | 23 ++++++++--
 obj/NiTriBasedGeom.h   | 12 ++++--
 11 files changed, 197 insertions(+), 159 deletions(-)

diff --git a/Ref.h b/Ref.h
index 3c64d134..fc153ff0 100644
--- a/Ref.h
+++ b/Ref.h
@@ -103,6 +103,11 @@ Ref<T> & Ref<T>::operator=( T * object ) {
 		return *this; //Do nothing
 	}
 
+	//Increment reference count on new object if it is not NULL
+	if ( object != NULL ) {
+		object->AddRef();
+	}
+
 	//Decriment reference count on previously referenced object, if any
 	if ( _object != NULL ) {
 		_object->SubtractRef();
@@ -111,11 +116,6 @@ Ref<T> & Ref<T>::operator=( T * object ) {
 	//Change reference to new object
 	_object = object;
 
-	//Increment reference count on new object if it is not NULL
-	if ( _object != NULL ) {
-		_object->AddRef();
-	}
-
 	return *this;
 }
 
@@ -127,6 +127,11 @@ Ref<T> & Ref<T>::operator=( const Ref & ref ) {
 		return *this; //Do nothing
 	}
 
+	//Increment reference count on new object if it is not NULL
+	if ( ref._object != NULL ) {
+		ref._object->AddRef();
+	}
+
 	//Decriment reference count on previously referenced object, if any
 	if ( _object != NULL ) {
 		_object->SubtractRef();
@@ -135,11 +140,6 @@ Ref<T> & Ref<T>::operator=( const Ref & ref ) {
 	//Change reference to new object
 	_object = ref._object;
 
-	//Increment reference count on new object if it is not NULL
-	if ( _object != NULL ) {
-		_object->AddRef();
-	}
-
 	return *this;
 }
 #endif
diff --git a/nif_math.cpp b/nif_math.cpp
index 28995d50..60a42812 100644
--- a/nif_math.cpp
+++ b/nif_math.cpp
@@ -393,7 +393,7 @@ float Matrix44::Determinant() const {
 	      - t[0][3] * Submatrix(0, 3).Determinant();
 }
 
-void Matrix44::Decompose( Vector3 & translate, Matrix33 & rotation, Float3 & scale ) const {
+void Matrix44::Decompose( Vector3 & translate, Matrix33 & rotation, float & scale ) const {
    translate = Vector3( (*this)[3][0], (*this)[3][1], (*this)[3][2] );
    Matrix33 rotT;
    for ( int i = 0; i < 3; i++ ){
@@ -403,12 +403,15 @@ void Matrix44::Decompose( Vector3 & translate, Matrix33 & rotation, Float3 & sca
       }
    }
    Matrix33 mtx = rotation * rotT;
-   scale = Float3( sqrt(mtx[0][0]), sqrt(mtx[1][1]), sqrt(mtx[2][2]) );
+   Float3 scale3( sqrt(mtx[0][0]), sqrt(mtx[1][1]), sqrt(mtx[2][2]) );
    for ( int i = 0; i < 3; i++ ){
       for ( int j = 0; j < 3; j++ ){
-         rotation[i][j] /= scale[i];
+         rotation[i][j] /= scale3[i];
       }
    }
+
+   //averate the scale since NIF doesn't support discreet scaling
+   scale = scale3[0] + scale3[1] + scale3[2] / 3.0f;
 }
 /*
  * Quaternion Methods
diff --git a/nif_math.h b/nif_math.h
index 3811d18b..49224082 100644
--- a/nif_math.h
+++ b/nif_math.h
@@ -744,7 +744,7 @@ struct Matrix44 {
 	}
 
    // undocumented
-   NIFLIB_API void Decompose( Vector3 & translate, Matrix33 & rotation, Float3 & scale ) const;
+   NIFLIB_API void Decompose( Vector3 & translate, Matrix33 & rotation, float & scale ) const;
 
 	//Python Operator Overloads
 	NIFLIB_API Float4 & __getitem__(int n) {
diff --git a/obj/NiNode.cpp b/obj/NiNode.cpp
index 1b0bfadb..daa96dcf 100644
--- a/obj/NiNode.cpp
+++ b/obj/NiNode.cpp
@@ -170,7 +170,7 @@ void NiNode::GoToSkeletonBindPosition() {
 	//Loop through all attached skins, straightening the skeleton on each
 	for ( list<NiSkinInstance*>::iterator it = skins.begin(); it != skins.end(); ++it ) {
 		//Get Bone list and Skin Data
-		vector<NiNodeRef> bones = (*it)->GetBones();
+		vector<NiNodeRef> bone_nodes = (*it)->GetBones();
 		NiSkinDataRef skin_data = (*it)->GetSkinData();
 
 		if ( skin_data == NULL ) {
@@ -178,59 +178,32 @@ void NiNode::GoToSkeletonBindPosition() {
 			continue;
 		}
 
-		//Get bone data from NiSkinData class
-		vector<SkinData> bone_data = skin_data->GetBoneData();
-
 		//Make sure the counts match
-		if ( bones.size() != bone_data.size() ) {
+		if ( bone_nodes.size() != skin_data->GetBoneCount() ) {
 			throw runtime_error( "Bone counts in NiSkinInstance and attached NiSkinData must match" );
 		}
 
 		//Loop through all bones influencing this skin
-		for ( uint i = 0; i < bones.size(); ++i ) {
+		for ( uint i = 0; i < bone_nodes.size(); ++i ) {
 			//Get current offset Matrix for this bone
-			//Matrix44 parent_offset( bone_data[i].translation,
-			//	                    bone_data[i].rotation,
-			//						bone_data[i].scale );
-			Matrix44 parent_offset(
-				bone_data[i].rotation[0][0], bone_data[i].rotation[0][1], bone_data[i].rotation[0][2], 0.0f,
-				bone_data[i].rotation[1][0], bone_data[i].rotation[1][1], bone_data[i].rotation[1][2], 0.0f,
-				bone_data[i].rotation[2][0], bone_data[i].rotation[2][1], bone_data[i].rotation[2][2], 0.0f,
-				bone_data[i].translation.x, bone_data[i].translation.y, bone_data[i].translation.z, 1.0f
-			); 
+			Matrix44 parent_offset = skin_data->GetBoneTransform(i);
 
 			//Loop through all bones again, checking for any that have this bone as a parent
-			for ( uint j = 0; j < bones.size(); ++j ) {
-				if ( bones[j]->GetParent() == bones[i] ) {
+			for ( uint j = 0; j < bone_nodes.size(); ++j ) {
+				if ( bone_nodes[j]->GetParent() == bone_nodes[i] ) {
 					//cout << "Bone " << bones[j] << " has bone " << bones[i] << " as parent." << endl;
 					//Node 2 has node 1 as a parent
 
 					//Get child offset Matrix33
-					/*Matrix44 child_offset( bone_data[j].translation,
-										   bone_data[j].rotation,
-										   bone_data[j].scale );*/
-					Matrix44 child_offset(
-						bone_data[j].rotation[0][0], bone_data[j].rotation[0][1], bone_data[j].rotation[0][2], 0.0f,
-						bone_data[j].rotation[1][0], bone_data[j].rotation[1][1], bone_data[j].rotation[1][2], 0.0f,
-						bone_data[j].rotation[2][0], bone_data[j].rotation[2][1], bone_data[j].rotation[2][2], 0.0f,
-						bone_data[j].translation.x, bone_data[j].translation.y, bone_data[j].translation.z, 1.0f
-					);
+					Matrix44 child_offset = skin_data->GetBoneTransform(j);
 
 					//Do calculation to get correct bone postion in relation to parent
-					//Matrix44 inverse_co = child_offset.Inverse();
-					//world_positions[bones[j]] = inverse_co * parent_offset;
-					Matrix44 inverse_co = child_offset.Inverse();
-					Matrix44 child_pos = inverse_co * parent_offset;
+					Matrix44 child_pos = child_offset.Inverse() * parent_offset;
 
 					//bones[j]->SetWorldBindPos( child_pos );
-					bones[j]->SetLocalRotation( child_pos.GetRotation() );
-					bones[j]->SetLocalScale( 1.0f );
-					bones[j]->SetLocalTranslation( child_pos.GetTranslation() );
-
-					//cout << "Matrix:  " << cout << "Translation:  " << world_positions[bones[j]] << endl;
-					//cout << "Translation:  " << world_positions[bones[j]].GetTranslation() << endl;
-					//cout << "Rotation:  " << world_positions[bones[j]].GetRotation() << endl;
-					//cout << "Scale:  " << world_positions[bones[j]].GetScale() << endl;
+					bone_nodes[j]->SetLocalRotation( child_pos.GetRotation() );
+					bone_nodes[j]->SetLocalScale( 1.0f );
+					bone_nodes[j]->SetLocalTranslation( child_pos.GetTranslation() );
 				}
 			}
 		}
@@ -261,30 +234,24 @@ void NiNode::RepositionGeom( NiAVObjectRef root ) {
 			return;
 		}
 
-		//Get bone info
-		vector<NiNodeRef> bones = skin_inst->GetBones();
-		vector<SkinData> bone_data = skin_data->GetBoneData();
+		//Get bone nodes
+		vector<NiNodeRef> bone_nodes = skin_inst->GetBones();
 
 		//Make sure the counts match
-		if ( bones.size() != bone_data.size() ) {
+		if ( bone_nodes.size() != skin_data->GetBoneCount() ) {
 			throw runtime_error( "Bone counts in NiSkinInstance and attached NiSkinData must match" );
 		}
 
 		//There must be at least one bone to do anything
-		if ( bones.size() == 0 ) {
+		if ( bone_nodes.size() == 0 ) {
 			return;
 		}
 
 		//Use first bone (arbitrary choice)
-		Matrix44 offset_mat(
-			bone_data[0].rotation[0][0], bone_data[0].rotation[0][1], bone_data[0].rotation[0][2], 0.0f,
-			bone_data[0].rotation[1][0], bone_data[0].rotation[1][1], bone_data[0].rotation[1][2], 0.0f,
-			bone_data[0].rotation[2][0], bone_data[0].rotation[2][1], bone_data[0].rotation[2][2], 0.0f,
-			bone_data[0].translation.x, bone_data[0].translation.y, bone_data[0].translation.z, 1.0f
-		);
+		Matrix44 offset_mat = skin_data->GetBoneTransform(0);
 			
 		//Get built up rotations to the root of the skeleton from this bone
-		Matrix44 bone_mat = bones[0]->GetWorldTransform();
+		Matrix44 bone_mat = bone_nodes[0]->GetWorldTransform();
 
 		Matrix44 world_mat = offset_mat * bone_mat;
 
diff --git a/obj/NiObject.cpp b/obj/NiObject.cpp
index 5a392431..80985564 100644
--- a/obj/NiObject.cpp
+++ b/obj/NiObject.cpp
@@ -34,7 +34,7 @@ void NiObject::AddRef() const {
 }
 
 void NiObject::SubtractRef() const {
-	if ( --_ref_count == 0 ) {
+	if ( _ref_count-- == 0 ) {
 		delete this;
 	}
 }
diff --git a/obj/NiSkinData.cpp b/obj/NiSkinData.cpp
index fa252e03..22933978 100644
--- a/obj/NiSkinData.cpp
+++ b/obj/NiSkinData.cpp
@@ -5,6 +5,8 @@ All rights reserved.  Please see niflib.h for licence. */
 #include "../gen/SkinData.h"
 #include "../gen/SkinWeight.h"
 #include "NiSkinPartition.h"
+#include "NiTriBasedGeom.h"
+#include "NiSkinInstance.h"
 using namespace Niflib;
 
 //Definition of TYPE constant
@@ -38,21 +40,78 @@ const Type & NiSkinData::GetType() const {
 	return TYPE;
 };
 
-void NiSkinData::SetBoneData( const vector<SkinData> & n ) {
-	boneList = n;
+uint NiSkinData::GetBoneCount() const {
+	return uint( boneList.size() );
 }
 
-vector<SkinData> NiSkinData::GetBoneData() const {
-	return boneList;
+Matrix44 NiSkinData::GetBoneTransform( uint bone_index ) const {
+	if ( bone_index > boneList.size() ) {
+		throw runtime_error( "The specified bone index was larger than the number of bones in this NiSkinData." );
+	}
+
+	return Matrix44( boneList[bone_index].translation, boneList[bone_index].rotation, boneList[bone_index].scale );
 }
 
-void NiSkinData::SetOverallTransform( const Matrix44 & n ) {
-	translation = n.GetTranslation();
-	rotation = n.GetRotation();
-	Vector3 s = n.GetScale();
-	scale = s.x + s.y + s.z / 3.0f;
+vector<SkinWeight> NiSkinData::GetBoneWeights( uint bone_index ) const {
+	if ( bone_index > boneList.size() ) {
+		throw runtime_error( "The specified bone index was larger than the number of bones in this NiSkinData." );
+	}
+
+	return boneList[bone_index].vertexWeights;
 }
-	
+
+void NiSkinData::SetBoneWeights( uint bone_index, const vector<SkinWeight> & n ) {
+	if ( bone_index > boneList.size() ) {
+		throw runtime_error( "The specified bone index was larger than the number of bones in this NiSkinData." );
+	}
+
+	boneList[bone_index].vertexWeights = n;
+}
+
 Matrix44 NiSkinData::GetOverallTransform() const {
 	return Matrix44( translation, rotation, scale );
+}
+
+NiSkinData::NiSkinData( const Ref<NiTriBasedGeom> & owner ) {
+	//Get skin instance
+	NiSkinInstanceRef skinInst = owner->GetSkinInstance();
+
+	if ( skinInst == NULL ) {
+		throw runtime_error("Skin instance should have already been created.");
+	}
+
+	boneList.resize( skinInst->GetBoneCount() );
+	vector<NiNodeRef> bone_nodes = skinInst->GetBones();
+	
+	//--Calculate Overall Offset--//
+
+	//Get TriBasedGeom world transform
+	Matrix44 owner_mat = owner->GetWorldTransform();
+
+	//Get Skeleton root world transform
+	Matrix44 skel_root_mat = skinInst->GetSkeletonRoot()->GetWorldTransform();
+
+	//Inverse owner NiTriBasedGeom matrix & multiply with skeleton root matrix
+	Matrix44 res_mat = owner_mat.Inverse() * skel_root_mat;
+
+	//Store result
+	res_mat.Decompose( translation, rotation, scale );
+
+	//--Calculate Bone Offsets--//
+
+	Matrix44 bone_mat;
+	for (uint i = 0; i < boneList.size(); ++i ) {
+		//--Get Bone Bind Pose--//
+
+		//Get bone world position
+		bone_mat = bone_nodes[i]->GetWorldTransform();
+
+		//Multiply NiTriBasedGeom matrix with inversed bone matrix
+		res_mat = owner_mat * bone_mat.Inverse();
+
+		//Store result
+		res_mat.Decompose( boneList[i].translation, boneList[i].rotation, boneList[i].scale );
+
+		//TODO:  Calculate center and radius of each bone
+	}
 }
\ No newline at end of file
diff --git a/obj/NiSkinData.h b/obj/NiSkinData.h
index b08f3d17..baa99a39 100644
--- a/obj/NiSkinData.h
+++ b/obj/NiSkinData.h
@@ -12,6 +12,7 @@ namespace Niflib {
 
 // Forward define of referenced blocks
 class NiSkinPartition;
+class NiTriBasedGeom;
 
 #include "../gen/obj_defines.h"
 
@@ -25,7 +26,15 @@ typedef Ref<NiSkinData> NiSkinDataRef;
 class NIFLIB_API NiSkinData : public NI_SKIN_DATA_PARENT {
 public:
 	NiSkinData();
+
+	/*!
+	 * This constructor is called by NiTriBasedGeom when it creates a new skin
+	 * instance using the BindSkin function.
+	 */
+	NiSkinData( const Ref<NiTriBasedGeom> & owner );
+
 	~NiSkinData();
+
 	//Run-Time Type Information
 	static const Type & TypeConst() { return TYPE; }
 private:	
@@ -38,11 +47,12 @@ public:
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
 
-	void SetOverallTransform( const Matrix44 & n );
 	Matrix44 GetOverallTransform() const;
+	uint GetBoneCount() const;
+	Matrix44 GetBoneTransform( uint bone_index ) const;
+	vector<SkinWeight> GetBoneWeights( uint bone_index ) const;
+	void SetBoneWeights( uint bone_index, const vector<SkinWeight> & n );
 
-	void SetBoneData( const vector<SkinData> & n );
-	vector<SkinData> GetBoneData() const;
 protected:
 	NI_SKIN_DATA_MEMBERS
 	STANDARD_INTERNAL_METHODS
diff --git a/obj/NiSkinInstance.cpp b/obj/NiSkinInstance.cpp
index ed286738..4633917f 100644
--- a/obj/NiSkinInstance.cpp
+++ b/obj/NiSkinInstance.cpp
@@ -13,51 +13,7 @@ const Type NiSkinInstance::TYPE("NiSkinInstance", &NI_SKIN_INSTANCE_PARENT::Type
 
 NiSkinInstance::NiSkinInstance() NI_SKIN_INSTANCE_CONSTRUCT {}
 
-NiSkinInstance::~NiSkinInstance() {}
-
-void NiSkinInstance::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
-	NI_SKIN_INSTANCE_READ
-}
-
-void NiSkinInstance::Write( ostream& out, map<NiObjectRef,uint> link_map, unsigned int version, unsigned int user_version ) const {
-	NI_SKIN_INSTANCE_WRITE
-}
-
-string NiSkinInstance::asString( bool verbose ) const {
-	NI_SKIN_INSTANCE_STRING
-}
-
-void NiSkinInstance::FixLinks( const vector<NiObjectRef> & objects, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
-	NI_SKIN_INSTANCE_FIXLINKS
-
-	//Inform newly fixed skeleton root of attachment
-	if ( skeletonRoot != NULL ) {
-		skeletonRoot->AddSkin( this );
-	}
-}
-
-list<NiObjectRef> NiSkinInstance::GetRefs() const {
-	NI_SKIN_INSTANCE_GETREFS
-}
-
-const Type & NiSkinInstance::GetType() const {
-	return TYPE;
-};
-
-vector< Ref<NiNode> > NiSkinInstance::GetBones() const {
-	vector<NiNodeRef> ref_bones( bones.size() );
-	for (uint i = 0; i < bones.size(); ++i ) {
-		ref_bones[i] = bones[i];
-	}
-	return ref_bones;
-}
-
-void NiSkinInstance::Bind( Ref<NiNode> skeleton_root, vector< Ref<NiNode> > bone_nodes ) {
-	//Ensure skin is not aleady bound
-	if ( bones.size() != 0 ) {
-		throw runtime_error("You have attempted to re-bind a skin that is already bound.  Unbind it first.");
-	}
-
+NiSkinInstance::NiSkinInstance( Ref<NiNode> skeleton_root, vector< Ref<NiNode> > bone_nodes ) NI_SKIN_INSTANCE_CONSTRUCT {
 	//Ensure that all bones are below the skeleton root node on the scene graph
 	for ( uint i = 0; i < bone_nodes.size(); ++i ) {
 		bool is_decended = false;
@@ -88,23 +44,48 @@ void NiSkinInstance::Bind( Ref<NiNode> skeleton_root, vector< Ref<NiNode> > bone
 	//Store skeleton root and inform it of this attachment
 	skeletonRoot = skeleton_root;
 	skeletonRoot->AddSkin( this );
-};
+}
 
-void NiSkinInstance::Unbind() {
+NiSkinInstance::~NiSkinInstance() {
 	//Inform Skeleton Root of detatchment and clear it.
 	skeletonRoot->RemoveSkin( this );
-	skeletonRoot = NULL;
+}
 
-	//Clear bone list
-	bones.clear();
+void NiSkinInstance::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
+	NI_SKIN_INSTANCE_READ
+}
 
-	//Destroy skin data
-	data = NULL;
-	skinPartition = NULL;
+void NiSkinInstance::Write( ostream& out, map<NiObjectRef,uint> link_map, unsigned int version, unsigned int user_version ) const {
+	NI_SKIN_INSTANCE_WRITE
+}
+
+string NiSkinInstance::asString( bool verbose ) const {
+	NI_SKIN_INSTANCE_STRING
+}
+
+void NiSkinInstance::FixLinks( const vector<NiObjectRef> & objects, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
+	NI_SKIN_INSTANCE_FIXLINKS
+
+	//Inform newly fixed skeleton root of attachment
+	if ( skeletonRoot != NULL ) {
+		skeletonRoot->AddSkin( this );
+	}
 }
 
-void NiSkinInstance::CalcHardwareSkinningData () {
+list<NiObjectRef> NiSkinInstance::GetRefs() const {
+	NI_SKIN_INSTANCE_GETREFS
+}
+
+const Type & NiSkinInstance::GetType() const {
+	return TYPE;
+};
 
+vector< Ref<NiNode> > NiSkinInstance::GetBones() const {
+	vector<NiNodeRef> ref_bones( bones.size() );
+	for (uint i = 0; i < bones.size(); ++i ) {
+		ref_bones[i] = bones[i];
+	}
+	return ref_bones;
 }
 
 Ref<NiSkinData> NiSkinInstance::GetSkinData() const {
@@ -132,4 +113,12 @@ void NiSkinInstance::SkeletonLost() {
 	//Destroy skin data
 	data = NULL;
 	skinPartition = NULL;
+}
+
+uint NiSkinInstance::GetBoneCount() const {
+	return uint(bones.size());
+}
+
+Ref<NiNode> NiSkinInstance::GetSkeletonRoot() const {
+	return skeletonRoot;
 }
\ No newline at end of file
diff --git a/obj/NiSkinInstance.h b/obj/NiSkinInstance.h
index 7f987936..5fa4fd8c 100644
--- a/obj/NiSkinInstance.h
+++ b/obj/NiSkinInstance.h
@@ -27,6 +27,13 @@ typedef Ref<NiSkinInstance> NiSkinInstanceRef;
 class NIFLIB_API NiSkinInstance : public NI_SKIN_INSTANCE_PARENT {
 public:
 	NiSkinInstance();
+
+	/*!
+	 * This constructor is called by NiTriBasedGeom when it creates a new skin
+	 * instance using the BindSkin function.
+	 */
+	NiSkinInstance( Ref<NiNode> skeleton_root, vector< Ref<NiNode> > bone_nodes );
+
 	~NiSkinInstance();
 	//Run-Time Type Information
 	static const Type & TypeConst() { return TYPE; }
@@ -40,27 +47,9 @@ public:
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
 
+	uint GetBoneCount() const;
 	vector< Ref<NiNode> > GetBones() const;
-
-	/*!
-	 * Binds any geometry that uses this skin instance to a list of bones.
-	 * The bones must have a common ancestor in the scenegraph.  This becomes
-	 * the skeleton root.
-	 */
-	void Bind( Ref<NiNode> skeleton_root, vector< Ref<NiNode> > bone_nodes );
-
-	/*! 
-	 * Detatches this skin instance from any bones it was previously bound to.
-	 */
-	void Unbind();
-
-	/*! 
-	 * Calculates a NiSkinPartition and attaches it to both pointers, the one
-	 * used in later versions in this class, and the one in the attached
-	 * NiSkinData class.  This way it will be written regardless of the
-	 * version.  SkinData must be set before this can be calculated.
-	 */
-	void CalcHardwareSkinningData ();
+	Ref<NiNode> GetSkeletonRoot() const;
 
 	Ref<NiSkinData> GetSkinData() const;
 	void SetSkinData( const Ref<NiSkinData> & n );
diff --git a/obj/NiTriBasedGeom.cpp b/obj/NiTriBasedGeom.cpp
index b31a87a9..50db534b 100644
--- a/obj/NiTriBasedGeom.cpp
+++ b/obj/NiTriBasedGeom.cpp
@@ -5,6 +5,7 @@ All rights reserved.  Please see niflib.h for licence. */
 #include "NiTriBasedGeomData.h"
 #include "NiSkinInstance.h"
 #include "NiObject.h"
+#include "NiSkinData.h"
 using namespace Niflib;
 
 //Definition of TYPE constant
@@ -67,11 +68,25 @@ void NiTriBasedGeom::SetShader( const string & n ) {
 		shaderName = n;
 	}
 }
-	
-void NiTriBasedGeom::SetSkinInstance( Ref<NiSkinInstance> & n ) {
-	skinInstance = n;
-}
 
 Ref<NiSkinInstance> NiTriBasedGeom::GetSkinInstance() const {
 	return skinInstance;
+}
+
+void NiTriBasedGeom::BindSkin( Ref<NiNode> skeleton_root, 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.");
+	}
+
+	//Create a skin instance using the bone and root data
+	skinInstance = new NiSkinInstance( skeleton_root, bone_nodes );
+
+	//Create a NiSkinData object based on this mesh
+	skinInstance->SetSkinData( new NiSkinData( this ) );
+};
+
+void NiTriBasedGeom::UnbindSkin() {
+	//Clear skin instance
+	skinInstance = NULL;
 }
\ No newline at end of file
diff --git a/obj/NiTriBasedGeom.h b/obj/NiTriBasedGeom.h
index 5a94c1fd..767f0828 100644
--- a/obj/NiTriBasedGeom.h
+++ b/obj/NiTriBasedGeom.h
@@ -40,7 +40,14 @@ public:
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
 
-	//TODO:  Handle attatchment of SkinInstance with new skinning API
+	/*!
+	 * Binds this geometry to a list of bones.  Creates and attatches a
+	 * 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 UnbindSkin();
+	Ref<NiSkinInstance> GetSkinInstance() const;
 
 	Ref<NiTriBasedGeomData> GetData() const;
 	void SetData( const Ref<NiTriBasedGeomData> & n );
@@ -51,8 +58,7 @@ public:
 	string GetShader() const;
 	void SetShader( const string & n );
 
-	void SetSkinInstance( Ref<NiSkinInstance> & n );
-	Ref<NiSkinInstance> GetSkinInstance() const;
+	
 	
 protected:
 	NI_TRI_BASED_GEOM_MEMBERS
-- 
GitLab