From 5c61240280d8a7ecd6e8f812e7aa5e38a3c96d8a Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Mon, 12 Jun 2006 04:29:18 +0000
Subject: [PATCH] Tried to implement GoToSkeletonBindPosition but it doesn't
 work.

---
 nif_math.cpp           | 40 ++++++++++++++++++++++
 nif_math.h             |  6 +++-
 niflib.cpp             |  8 +++++
 obj/NiNode.cpp         | 78 +++++++++++++++++++++++++++++++++++++++---
 obj/NiSkinData.cpp     | 18 ++++++++++
 obj/NiSkinData.h       |  6 ++++
 obj/NiSkinInstance.cpp | 16 +++++++++
 obj/NiSkinInstance.h   |  6 ++++
 8 files changed, 173 insertions(+), 5 deletions(-)

diff --git a/nif_math.cpp b/nif_math.cpp
index cf89aed5..b14c294f 100644
--- a/nif_math.cpp
+++ b/nif_math.cpp
@@ -217,6 +217,46 @@ Matrix44::Matrix44( const Vector3 & t, const Matrix33 & r, float scale ) {
 	*this = s * rt;
 }
 
+Matrix33 Matrix44::GetRotation() const {
+	const Matrix44 & t = *this;
+
+	Matrix33 m( t[0][0], t[0][1], t[0][2],
+	            t[1][0], t[1][1], t[1][2],
+				t[2][0], t[2][1], t[2][2]
+			   );
+
+   	//--Extract Scale from first 3 rows--//
+	float scale[3];
+	for (int r = 0; r < 3; ++r) {
+		//Get scale for this row
+		scale[r] = sqrt(m[r][0] * m[r][0] + m[r][1] * m[r][1] + m[r][2] * m[r][2]);
+
+		//Normalize the row by dividing each factor by scale
+		m[r][0] /= scale[r];
+		m[r][1] /= scale[r];
+		m[r][2] /= scale[r];
+	}
+
+	//Return result
+	return m;
+}
+
+Vector3 Matrix44::GetScale() const {
+	const Matrix44 & m = *this;
+	float scale[3];
+	for (int r = 0; r < 3; ++r) {
+		//Get scale for this row
+		scale[r] = sqrt(m[r][0] * m[r][0] + m[r][1] * m[r][1] + m[r][2] * m[r][2]);
+	}
+	return Vector3( scale[0], scale[1], scale[2] );
+}
+
+
+Vector3 Matrix44::GetTranslation() const {
+	const Matrix44 & m = *this;
+	return Vector3( m[3][0], m[3][1], m[3][2] );
+}
+
 Matrix44 Matrix44::operator*( const Matrix44 & rh ) const {
 	return Matrix44(*this) *= rh;
 }
diff --git a/nif_math.h b/nif_math.h
index b5a2c573..75c530cc 100644
--- a/nif_math.h
+++ b/nif_math.h
@@ -729,7 +729,11 @@ struct Matrix44 {
 	 */
 	NIFLIB_API float Adjunct( int skip_r, int skip_c ) const;
 
-	//undocumented
+	NIFLIB_API Matrix33 GetRotation() const;
+	NIFLIB_API Vector3 GetScale() const;
+	NIFLIB_API Vector3 GetTranslation() const;
+
+	//undocumented, may be removed
 	NIFLIB_API void AsFloatArr( float out[4][4] ) {
 		out[0][0] = rows[0][0]; out[0][1] = rows[0][1]; out[0][2] = rows[0][2]; out[0][3] = rows[0][3];
 		out[1][0] = rows[1][0]; out[1][1] = rows[1][1]; out[1][2] = rows[1][2]; out[1][3] = rows[1][3];
diff --git a/niflib.cpp b/niflib.cpp
index 96234bdf..6865c23d 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -336,6 +336,14 @@ vector<NiObjectRef> ReadNifList( istream & in ) {
 		blocks[i]->FixLinks( blocks, link_stack, version, user_version );
 	}
 
+	//TODO:  Make this an optional step?
+	for (uint i = 0; i < blocks.size(); ++i) {
+		NiNodeRef node = DynamicCast<NiNode>(blocks[i]);
+		if ( node != NULL && node->IsSkeletonRoot() ) {
+			node->GoToSkeletonBindPosition();
+		}
+	}
+
 	//TODO:  No longer necessary?
 	////Build up the bind pose matricies into their world-space equivalents
 	//NiAVObjectRef av_root = DynamicCast<NiAVObject>( FindRoot(blocks) );
diff --git a/obj/NiNode.cpp b/obj/NiNode.cpp
index 5a038299..c9974b1c 100644
--- a/obj/NiNode.cpp
+++ b/obj/NiNode.cpp
@@ -5,6 +5,7 @@ All rights reserved.  Please see niflib.h for licence. */
 #include "NiAVObject.h"
 #include "NiDynamicEffect.h"
 #include "NiSkinInstance.h"
+#include "NiSkinData.h"
 
 //Definition of TYPE constant
 const Type NiNode::TYPE("NiNode", &NI_NODE_PARENT::TYPE );
@@ -14,6 +15,11 @@ NiNode::NiNode() NI_NODE_CONSTRUCT {}
 NiNode::~NiNode() {
 	//Clear Children
 	ClearChildren();
+
+	//Unbind any attached skins
+	for ( list<NiSkinInstance*>::iterator it = skins.begin(); it != skins.end(); ++it ) {
+		(*it)->SkeletonLost();
+	}
 }
 
 void NiNode::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) {
@@ -93,10 +99,6 @@ bool NiNode::IsSkinInfluence() const {
 	return ((flags & 8) == 0);
 }
 
-void NiNode::GoToSkeletonBindPosition() {
-	//TODO:: Implement GoToSkeletonBindPosition()
-}
-
 void NiNode::AddSkin( NiSkinInstance * skin_inst ) {
 	//Ensure that all bones are below this node on the scene graph
 	vector<NiNodeRef> bones = skin_inst->GetBones();
@@ -151,4 +153,72 @@ void NiNode::SetSkinFlag( bool n ) {
 		//Requested value is different, flip bit
 		flags ^= 8;
 	}
+}
+
+void NiNode::GoToSkeletonBindPosition() {
+	map<NiNodeRef, Matrix44> world_positions;
+	
+	//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();
+		NiSkinDataRef skin_data = (*it)->GetSkinData();
+
+		if ( skin_data == NULL ) {
+			//There's no skin data for this skin instance; skip it.
+			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() ) {
+			throw runtime_error( "Bone counts in NiSkinInstance and attached NiSkinData must match" );
+		}
+
+		//Loop through all bones influing this skin
+		for ( uint i = 0; i < bones.size(); ++i ) {
+			//Get current offset Matrix for this bone
+			Matrix44 parent_offset( bone_data[i].translation,
+				                    bone_data[i].rotation,
+									bone_data[i].scale );
+
+			//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] ) {
+					//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 );
+
+					//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;
+				}
+			}
+		}
+	}
+
+	//All the world positoins have been calculated, so use them to set the local
+	//positoins
+
+	//Put skeleton root into world positions if it's not already there
+	if ( world_positions.find( this ) == world_positions.end() ) {
+		world_positions[this] = GetWorldTransform();
+	}
+
+	//Now loop through all nodes in the world_positions map
+	for ( map< NiNodeRef, Matrix44>::iterator it = world_positions.begin(); it != world_positions.end(); ++it ) {
+		Matrix44 res_mat = world_positions[it->first] * world_positions[it->first->GetParent()].Inverse();
+
+		//Store result in node's local transforms
+		it->first->SetLocalRotation( res_mat.GetRotation() );
+		it->first->SetLocalTranslation( res_mat.GetTranslation());
+		Vector3 scale = res_mat.GetScale();
+		it->first->SetLocalScale( 1.0f );//scale.x + scale.y + scale.z / 3.0f );
+	}
+
 }
\ No newline at end of file
diff --git a/obj/NiSkinData.cpp b/obj/NiSkinData.cpp
index 4a775f42..fc925df7 100644
--- a/obj/NiSkinData.cpp
+++ b/obj/NiSkinData.cpp
@@ -37,3 +37,21 @@ const Type & NiSkinData::GetType() const {
 	return TYPE;
 };
 
+void NiSkinData::SetBoneData( const vector<SkinData> & n ) {
+	boneList = n;
+}
+
+vector<SkinData> NiSkinData::GetBoneData() const {
+	return boneList;
+}
+
+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;
+}
+	
+Matrix44 NiSkinData::GetOverallTransform() const {
+	return Matrix44( translation, rotation, scale );
+}
\ No newline at end of file
diff --git a/obj/NiSkinData.h b/obj/NiSkinData.h
index c22a5812..ff7fb1ce 100644
--- a/obj/NiSkinData.h
+++ b/obj/NiSkinData.h
@@ -33,6 +33,12 @@ public:
 	virtual void FixLinks( const vector<NiObjectRef> & objects, list<uint> & link_stack, unsigned int version, unsigned int user_version );
 	virtual list<NiObjectRef> GetRefs() const;
 	virtual const Type & GetType() const;
+
+	void SetOverallTransform( const Matrix44 & n );
+	Matrix44 GetOverallTransform() const;
+
+	void SetBoneData( const vector<SkinData> & n );
+	vector<SkinData> GetBoneData() const;
 protected:
 	NI_SKIN_DATA_MEMBERS
 };
diff --git a/obj/NiSkinInstance.cpp b/obj/NiSkinInstance.cpp
index e258d051..67f46409 100644
--- a/obj/NiSkinInstance.cpp
+++ b/obj/NiSkinInstance.cpp
@@ -28,6 +28,11 @@ string NiSkinInstance::asString( bool verbose ) const {
 
 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 {
@@ -94,4 +99,15 @@ Ref<NiSkinPartition> NiSkinInstance::GetSkinPartition() const {
 
 void NiSkinInstance::SetSkinPartition( const Ref<NiSkinPartition> & n ) {
 	skinPartition = n;
+}
+
+void NiSkinInstance::SkeletonLost() {
+	skeletonRoot = NULL;
+
+	//Clear bone list
+	bones.clear();
+
+	//Destroy skin data
+	data = NULL;
+	skinPartition = NULL;
 }
\ No newline at end of file
diff --git a/obj/NiSkinInstance.h b/obj/NiSkinInstance.h
index 5ebe8dc9..e2162550 100644
--- a/obj/NiSkinInstance.h
+++ b/obj/NiSkinInstance.h
@@ -62,6 +62,12 @@ public:
 	Ref<NiSkinPartition> GetSkinPartition() const;
 	void SetSkinPartition( const Ref<NiSkinPartition> & n );
 
+	/*! 
+	 * Called by skeleton root NiNode when it self destructs to inform this skin
+	 * instance that the skeleton has been lost.  Should not be called directly.
+	 */
+	void SkeletonLost();
+
 protected:
 	NI_SKIN_INSTANCE_MEMBERS
 };
-- 
GitLab