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