From 0213e4829e1241ac70dd8c8dc5fabebdeb99dfb3 Mon Sep 17 00:00:00 2001
From: Shon Ferguson <shonferg@users.sourceforge.net>
Date: Sat, 7 Apr 2007 06:37:12 +0000
Subject: [PATCH] Moved ListAncestors and FindCommonAncestor helper functions
 from inside of NiGeometry to public interface.

---
 include/niflib.h         |  16 ++++++
 include/obj/NiGeometry.h |   1 -
 src/niflib.cpp           |  93 ++++++++++++++++++++++++++++++++++
 src/obj/NiGeometry.cpp   | 104 ++++-----------------------------------
 4 files changed, 118 insertions(+), 96 deletions(-)

diff --git a/include/niflib.h b/include/niflib.h
index c8a8110b..099c020d 100644
--- a/include/niflib.h
+++ b/include/niflib.h
@@ -241,6 +241,22 @@ NIFLIB_API void MergeNifTrees( NiNode * target, NiControllerSequence * right, un
  */
 NIFLIB_API void SendNifTreeToBindPos( NiNode * root );
 
+/*!
+ * Returns the common ancestor of several NiAVObjects, or NULL if there is no common
+ * ancestor.  None of the objects given can be the common ansestor, the search starts
+ * with their parents.
+ * \param[in] objects The list of NiAVObjects to try to find the commen ancestor of.
+ * \return The common anscestor if one is found, otherwise a NULL reference.
+ */
+NIFLIB_API Ref<NiNode> FindCommonAncestor( const vector< Ref<NiAVObject> > & objects );
+
+/*!
+ * Returns a list of all the ancestors of a given NiAVObject
+ * \param leaf[in] The NiAVObject to list the ancestors of.
+ * \return A list containing all the ancestors of the given NiAVObject
+ */
+NIFLIB_API list< Ref<NiNode> > ListAncestors( NiAVObject * leaf );
+
 //// Returns list of all blocks in the tree rooted by root block.
 //list<NiObjectRef> GetNifTree( NiObjectRef const & root_block );
 
diff --git a/include/obj/NiGeometry.h b/include/obj/NiGeometry.h
index 6f5688a5..cdae2934 100644
--- a/include/obj/NiGeometry.h
+++ b/include/obj/NiGeometry.h
@@ -101,7 +101,6 @@ public:
 	NIFLIB_API bool IsSkin();
 
 protected:
-	list< Ref<NiNode> > ListAncestors( const Ref<NiNode> & leaf ) const;
 	NI_GEOMETRY_MEMBERS
 private:
 	void InternalRead( istream& in, list<unsigned int> & link_stack, const NifInfo & info );
diff --git a/src/niflib.cpp b/src/niflib.cpp
index adb94311..c210c779 100644
--- a/src/niflib.cpp
+++ b/src/niflib.cpp
@@ -1163,4 +1163,97 @@ void SendNifTreeToBindPos( NiNode * root ) {
 	}
 }
 
+list< Ref<NiNode> > ListAncestors( NiAVObject * leaf ) {
+	if ( leaf == NULL ) {
+		throw runtime_error("ListAncestors called with a NULL leaf NiNode Ref");
+	}
+
+	list<NiNodeRef> ancestors;
+
+	NiNodeRef current = leaf->GetParent();
+
+	while ( current != NULL ) {
+		ancestors.push_front(current);
+
+		current = current->GetParent();
+	}
+
+	return ancestors;
+}
+
+Ref<NiNode> FindCommonAncestor( const vector< Ref<NiAVObject> > & objects ) {
+
+	//create lists of nodes that have an influence and this TriBasedGeom
+	//as decendents
+	size_t obj_count = objects.size();
+	vector< list<NiNodeRef> > ancestors( obj_count );
+	
+	//Add Ancestors of each object to its corresponding list
+	for ( size_t i = 0; i < obj_count; ++i ) {
+		ancestors[i] = ListAncestors( objects[i] );
+	}
+
+	if ( ancestors[0].size() == 0 ) {
+		//All objects must have a parent for there to be a common ancestor, so return NULL
+		return NULL;
+	}
+
+	NiNodeRef root = ancestors[0].front();
+	//Make sure bone and shapes are part of the same tree
+	for ( size_t i = 1; i < obj_count; ++i ) {
+		if ( ancestors[i].size() == 0 ) {
+			//All objects must have a parent for there to be a common ancestor, so return NULL
+			return NULL;
+		}
+		if ( ancestors[i].front() != root ) {
+			//These objects are not part of the same tree, so return NULL
+			return NULL;
+		}
+	}
+
+	//Since the first items have been shown to match, pop all the stacks
+	for ( size_t i = 0; i < obj_count; ++i ) {
+		ancestors[i].pop_front();
+	}
+
+	//Now search for the common ancestor
+	while(true) {
+		bool all_same = true;
+		if ( ancestors[0].size() == 0 ) {
+			//This list is over, so the last top is the common ancestor
+			//break out of the loop
+			break;
+		}
+		NiNodeRef first_ancestor = ancestors[0].front();
+		for ( size_t i = 1; i < obj_count; ++i ) {
+			if ( ancestors[i].size() == 0 ) {
+				//This list is over, so the last top is the common ancestor
+				//break out of the loop
+				all_same = false;
+				break;
+			}
+			if ( ancestors[i].front() != first_ancestor ) {
+				all_same = false;
+			}
+		}
+
+		if ( all_same == true ) {
+			//They're all the same, so set the top, pop all the stacks
+			//and look again
+
+			root = ancestors[0].front();
+			for ( size_t i = 0; i < obj_count; ++i ) {
+				ancestors[i].pop_front();
+			}
+		} else {
+			//One is different, so the last top is the common ancestor.
+			//break out of the loop
+			break;
+		}
+	}
+
+	//Return result
+	return root;
+}
+
 } // namespace NifLib
diff --git a/src/obj/NiGeometry.cpp b/src/obj/NiGeometry.cpp
index b01cc8d8..e2ce8c2c 100644
--- a/src/obj/NiGeometry.cpp
+++ b/src/obj/NiGeometry.cpp
@@ -7,6 +7,7 @@ All rights reserved.  Please see niflib.h for licence. */
 #include "../../include/obj/NiObject.h"
 #include "../../include/obj/NiNode.h"
 #include "../../include/obj/NiSkinData.h"
+#include "../../include/niflib.h"
 using namespace Niflib;
 
 //Definition of TYPE constant
@@ -87,83 +88,16 @@ void NiGeometry::BindSkin( vector< Ref<NiNode> > bone_nodes ) {
 
 	//--Find a suitable skeleton root--//
 
-	//create lists of nodes that have an influence and this TriBasedGeom
-	//as decendents
-	int num_lists = int(bone_nodes.size()) + 1;
-	vector< list<NiNodeRef> > ancestors( num_lists );
+	//The skeleton root will be the common ancestor of all bones which influence this skin,
+	//and the skin object itself.
 
-	if ( GetParent() == NULL ) {
-		throw runtime_error("Attempted to bind skin on a shape with no parent.");
-	}
-
-	ancestors[bone_nodes.size()] = ListAncestors( GetParent() );
-	
-	for ( unsigned int i = 0; i < bone_nodes.size(); ++i ) {
-		if ( bone_nodes[i] == NULL ) {
-			throw runtime_error("Attempted to bind skin to a NULL bone reference.");
-		}
-		NiNodeRef bonePar = bone_nodes[i]->GetParent();
-		if ( bonePar == NULL ) {
-			throw runtime_error("Attempted to bind skin to a bone with no parent.  A skeleton root cannot be a bone so all bones must have at least one parent.");
-		}
-		ancestors[i] = ListAncestors( bonePar );
+	vector<NiAVObjectRef> objects;
+	objects.push_back( NiAVObjectRef(this) );
+	for ( size_t i = 0; i < bone_nodes.size(); ++i ) {
+		objects.push_back( StaticCast<NiAVObject>(bone_nodes[i]) );
 	}
 
-	if ( ancestors[0].size() == 0 ) {
-		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 = ancestors[0].front();
-   //Make sure bone and shapes are part of the same tree
-   for ( int i = 1; i < num_lists; ++i ) {
-	   if ( ancestors[i].size() == 0 ) {
-		   throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place.");
-	   }
-	   if ( ancestors[i].front() != skeleton_root ) {
-		   throw runtime_error("Shape and all skin influence bones must be part of the same tree before skin bind can take place.");
-	   }
-   }
-
-   //Since the first items have been shown to match, pop all the stacks
-   for ( int i = 0; i < num_lists; ++i ) {
-	   ancestors[i].pop_front();
-   }
-
-   //Now search for the common ancestor
-   while(true) {
-	   bool all_same = true;
-	   if ( ancestors[0].size() == 0 ) {
-		   //This list is over, so the last top is the common ancestor
-		   //break out of the loop
-		   break;
-	   }
-	   NiNodeRef first_ancestor = ancestors[0].front();
-	   for ( int i = 1; i < num_lists; ++i ) {
-		   if ( ancestors[i].size() == 0 ) {
-			   //This list is over, so the last top is the common ancestor
-			   //break out of the loop
-			   all_same = false;
-			   break;
-		   }
-		   if ( ancestors[i].front() != first_ancestor ) {
-			   all_same = false;
-		   }
-	   }
-
-	   if ( all_same == true ) {
-		   //They're all the same, so set the top, pop all the stacks
-		   //and look again
-		
-		   skeleton_root = ancestors[0].front();
-		   for ( int i = 0; i < num_lists; ++i ) {
-			   ancestors[i].pop_front();
-		   }
-	   } else {
-		   //One is different, so the last top is the common ancestor.
-		   //break out of the loop
-		   break;
-	   }
-   }
+	NiNodeRef skeleton_root = FindCommonAncestor( objects );
 
 	if ( skeleton_root == NULL ) {
 		throw runtime_error("Failed to find suitable skeleton root.");
@@ -191,7 +125,7 @@ void NiGeometry::ApplySkinOffset() {
 	
 	//Get ancestors
 	list<NiNodeRef> ancestors;
-	ancestors = ListAncestors( GetParent() );
+	ancestors = ListAncestors( this );
 
 	//Propogate transforms on ancestors below skeleton root
 	bool below_root = false;
@@ -240,26 +174,6 @@ bool NiGeometry::IsSkin() {
 	}
 }
 
-list< Ref<NiNode> > NiGeometry::ListAncestors( const Ref<NiNode> & leaf ) const {
-	if ( leaf == NULL ) {
-		throw runtime_error("ListAncestors called with a NULL leaf NiNode Ref");
-	}
-	
-	list<NiNodeRef> ancestors;
-
-	NiNodeRef niNode = leaf;
-	while (true) {
-		ancestors.push_front(niNode);
-		if ( niNode->GetParent() == NULL ) {
-			break;
-		} else {
-			niNode = niNode->GetParent();
-		}
-	}
-
-	return ancestors;
-}
-
 void NiGeometry::UnbindSkin() {
 	//Clear skin instance
 	skinInstance = NULL;
-- 
GitLab