From af1ac563fa8158549157e1f2a7fa8384640ad2ab Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Thu, 13 Oct 2005 04:57:36 +0000
Subject: [PATCH] Made NiSkinData and the bone nodes it references aware of
 each other so they can be properly destroyed.

---
 NIF_Blocks.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++-------
 NIF_Blocks.h   | 26 ++++++++++----
 2 files changed, 101 insertions(+), 18 deletions(-)

diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp
index a80bce6e..892f28a0 100644
--- a/NIF_Blocks.cpp
+++ b/NIF_Blocks.cpp
@@ -38,7 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */
 #include <sstream>
 
 extern bool verbose;
-extern int blocks_in_memory;
+extern unsigned int blocks_in_memory;
 
 #define endl "\r\n"
 
@@ -284,6 +284,8 @@ void * ANode::QueryInterface( int id ) {
 	// Contains INode Interface
 	if ( id == Node ) {
 		return (void*)static_cast<INode*>(this);
+	} else if (id == NodeInternal ) {
+		return (void*)static_cast<INodeInternal*>(this);
 	} else {
 		return ABlock::QueryInterface( id );
 	}
@@ -374,6 +376,25 @@ void ANode::SetBindPosition( float in_matrix[4][4] ) {
 	}
 }
 
+void ANode::IncSkinRef( IBlock * skin_data ) {
+	skin_refs.push_back(skin_data);
+}
+
+void ANode::DecSkinRef( IBlock * skin_data ) {
+	skin_refs.remove(skin_data);
+}
+
+ANode::~ANode() {
+	// Inform all NiSkinData blocks that have added their references that this block is dying
+	list<IBlock*>::iterator it;
+	for (it = skin_refs.begin(); it != skin_refs.end(); ++it) {
+		ISkinDataInternal * data = (ISkinDataInternal*)(*it)->QueryInterface(SkinDataInternal);
+		if ( data != NULL ) {
+			data->RemoveBoneByPtr(this);
+		}
+	}
+}
+
 /***********************************************************
  * NiTriShapeData methods
  **********************************************************/
@@ -764,7 +785,7 @@ void NiSkinData::Write( ofstream& out ) {
 	WriteUInt(short(bones.size()), out);
 	unknown.Write( out );
 
-	map<blk_ref, Bone>::iterator it;
+	map<IBlock*, Bone>::iterator it;
 	for( it = bone_map.begin(); it != bone_map.end(); ++it ) {
 		for (int c = 0; c < 3; ++c) {
 			for (int r = 0; r < 3; ++r) {
@@ -840,7 +861,7 @@ string NiSkinData::asString() {
 	//	<< "Unknown Index:  " << unknown << endl
 	//	<< "Bones:" << endl;
 
-	map<blk_ref, Bone>::iterator it;
+	map<IBlock*, Bone>::iterator it;
 	int num = 0;
 	for( it = bone_map.begin(); it != bone_map.end(); ++it ) {
 		//Friendlier name
@@ -848,7 +869,7 @@ string NiSkinData::asString() {
 
 		num++;
 		out << "Bone " << num << ":" << endl
-			<< "   Block:  " << it->first << endl
+			<< "   Block:  " << blk_ref(it->first) << endl
 			<< "   Bone Offset Transforms:" << endl
 			<< "      Rotation:" << endl
 			<< "         |" << setw(6) << bone.rotation[0][0] << "," << setw(6) << bone.rotation[0][1] << "," << setw(6) << bone.rotation[0][2] << " |" << endl
@@ -997,9 +1018,21 @@ void * NiSkinData::QueryInterface( int id ) {
 void NiSkinData::SetBones( vector<blk_ref> bone_blocks ) {
 	//Move bones from temproary vector to map, sorted by blk_ref
 	for (uint i = 0; i < bones.size(); ++i) {
-		bone_map.insert( pair<blk_ref, Bone>(bone_blocks[i], bones[i]) );
+			//Make sure bone is a node
+			INodeInternal * node_int = (INodeInternal*)bone_blocks[i]->QueryInterface(NodeInternal);
+
+			if (node_int == NULL)
+				throw runtime_error("Attempted to add a block as a bone that is not a node.");
+
+			//move the data
+			bone_map.insert( pair<IBlock*, Bone>(bone_blocks[i].get_block(), bones[i]) );
+			
+			//Increment reference at bone node site
+			node_int->IncSkinRef(this);
 	}
-	bones.clear();	
+
+	//Clear temporary vector data
+	bones.clear();
 }
 
 void NiSkinData::StraightenSkeleton() {
@@ -1019,7 +1052,7 @@ void NiSkinData::StraightenSkeleton() {
 	//}
 
 	//Loop through all bones
-	map<blk_ref, Bone>::iterator it;
+	map<IBlock*, Bone>::iterator it;
 	for ( it = bone_map.begin(); it != bone_map.end(); ++it ) {
 		//Friendlier name for current bone
 		Bone & bone = it->second;
@@ -1032,7 +1065,7 @@ void NiSkinData::StraightenSkeleton() {
 			bone.translation[0], bone.translation[1], bone.translation[2], 1.0f
 		}; 
 		//Loop through all bones again, checking for any that have this bone as a parent
-		map<blk_ref, Bone>::iterator it2;
+		map<IBlock*, Bone>::iterator it2;
 		for ( it2 = bone_map.begin(); it2 != bone_map.end(); ++it2 ) {
 			if ( it2->first->GetParent() == it->first ) {
 				//Block 2 has block 1 as a parent
@@ -1158,10 +1191,10 @@ vector<blk_ref> NiSkinData::GetBones() {
 	//Put all the valid bones from the map into a vector to return
 	vector<blk_ref> bone_blks( bone_map.size() );
 
-	map<blk_ref, Bone>::iterator it;
+	map<IBlock*, Bone>::iterator it;
 	int count = 0;
 	for (it = bone_map.begin(); it != bone_map.end(); ++it ) {
-		bone_blks[count] = it->first;
+		bone_blks[count] = blk_ref(it->first);
 		count++;
 	}
 
@@ -1169,12 +1202,48 @@ vector<blk_ref> NiSkinData::GetBones() {
 }
 
 map<int, float> NiSkinData::GetWeights( blk_ref bone ) {
-	return bone_map[bone].weights;
+	return bone_map[bone.get_block()].weights;
 }
 
 void NiSkinData::AddBone( blk_ref bone, map<int, float> in ) {
-	bone_map[bone].weights = in;
+	//Make sure bone is a node
+	INodeInternal * node_int = (INodeInternal*)bone->QueryInterface(NodeInternal);
+
+	if (node_int == NULL)
+		throw runtime_error("Attempted to add a block as a bone that is not a node.");
+	
+	//Add bone to internal list
+	bone_map[bone.get_block()].weights = in;
+	
+	//Increment reference at bone node site
+	node_int->IncSkinRef(this);
+}
+
+void NiSkinData::RemoveBoneByPtr( IBlock * bone ) {
+	//Remove bone from internal list
+	bone_map.erase( bone );
+
+	//Do not decrement bone node locatoin because it is already dead
+}
+
+void NiSkinData::RemoveBone( blk_ref bone ) {
+	//Remove bone from internal list
+	bone_map.erase( bone.get_block() );
+
+	//Decrement reference at bone node site
+	INodeInternal * node_int = (INodeInternal*)bone->QueryInterface(NodeInternal);
+	node_int->DecSkinRef(this);
+}
+
+NiSkinData::~NiSkinData() {
+	//Inform all linked bone nodes that this NiSkinData block is dying
+	map<IBlock*, Bone>::iterator it;
+	for (it = bone_map.begin(); it != bone_map.end(); ++it) {
+		INodeInternal * node_int = (INodeInternal*)it->first->QueryInterface(NodeInternal);
+		node_int->DecSkinRef(this);
+	}
 }
+
 /***********************************************************
  * NiGeomMorpherController methods
  **********************************************************/
diff --git a/NIF_Blocks.h b/NIF_Blocks.h
index 95585574..c6d52a48 100644
--- a/NIF_Blocks.h
+++ b/NIF_Blocks.h
@@ -56,6 +56,7 @@ typedef pair<LinkMapIt,LinkMapIt> LinkMapRange;
 const int BlockInternal = -1;
 const int SkinInstInternal = -2;
 const int SkinDataInternal = -3;
+const int NodeInternal = -4;
 
 
 //void GetBuiltUpTransform(blk_ref block, Matrix & m/*, blk_ref stop*/);
@@ -128,7 +129,13 @@ public:
 	~ANamed(){}
 };
 
-class ANode : public ANamed, public INode {
+class INodeInternal {
+public:
+	virtual void IncSkinRef( IBlock * skin_data ) = 0;
+	virtual void DecSkinRef( IBlock * skin_data ) = 0;
+};
+
+class ANode : public ANamed, public INode, public INodeInternal {
 public:
 	ANode(){
 		AddAttr( "flags", "Flags" );
@@ -141,7 +148,7 @@ public:
 
 		SetIdentity44(bindPosition);
 	}
-	~ANode(){}
+	~ANode();
 	void * QueryInterface( int id );
 	void Read( ifstream& in ) {
 		ABlock::Read( in );
@@ -150,15 +157,20 @@ public:
 		SetBindPosition( transform );
 	}
 
-	//Node Functions
+	//INode Functions
 	void GetLocalTransform( float out_matrix[4][4] );
 	void GetWorldTransform( float out_matrix[4][4] );
 	void GetBindPosition( float out_matrix[4][4] );
 	void GetLocalBindPos( float out_matrix[4][4] );
 	void SetBindPosition( float in_matrix[4][4] );
 
+	//INodeInternal Functions
+	void IncSkinRef( IBlock * skin_data );
+	void DecSkinRef( IBlock * skin_data );
+
 private:
 	float bindPosition[4][4];
+	list<IBlock*> skin_refs;
 };
 
 class AParentNode : public ANode {
@@ -1095,6 +1107,7 @@ public:
 	virtual void SetBones( vector<blk_ref> bone_blocks ) = 0;
 	virtual void RepositionTriShape() = 0;
 	virtual void StraightenSkeleton() = 0;
+	virtual void RemoveBoneByPtr( IBlock * bone ) = 0;
 };
 
 class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
@@ -1102,7 +1115,7 @@ class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
 	public:
 
 		NiSkinData(){}
-		~NiSkinData(){}
+		~NiSkinData();
 
 		void Read( ifstream& in );
 		void Write( ofstream& out );
@@ -1114,12 +1127,13 @@ class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
 		void SetBones( vector<blk_ref> bone_blocks );
 		void RepositionTriShape();
 		void StraightenSkeleton();
+		void RemoveBoneByPtr( IBlock * bone );
 
         //ISkinData
 		vector<blk_ref> GetBones();
 		map<int, float> GetWeights( blk_ref bone );
 		void AddBone( blk_ref bone, map<int, float> in );
-		void RemoveBone( blk_ref bone ) { bone_map.erase( bone ); }
+		void RemoveBone( blk_ref bone );
 	private:
 		struct Bone {
 			matrix rotation;
@@ -1133,7 +1147,7 @@ class NiSkinData : public ABlock, public ISkinData, public ISkinDataInternal {
 		fVector3 translation;
 		float  scale;
 		nifIndex unknown;
-		map<blk_ref, Bone> bone_map;
+		map<IBlock*, Bone> bone_map;
 		vector<Bone> bones;		
 };
 
-- 
GitLab