From ff0a0d057ca82fc5af2145f87ea74c9074d0aa68 Mon Sep 17 00:00:00 2001 From: Tazpn <tazpn@users.sourceforge.net> Date: Mon, 18 Sep 2006 03:14:54 +0000 Subject: [PATCH] Miscellaneous fixes: 1. Update NiStencilProperty based on Shon's changes 2. Rewrote large chunks of WriteFileGroup related routines. 3. Added a WriteNifTree which allows for multiple roots (not exposed publically) 4. Added option to BindSkin to scene instead of skeleton root. 5. Fixed bug in RemoveController 6. Added helpers to NiControllerManager for sequence management 7. Propegated user_version in routines which take a version --- include/niflib.h | 2 +- include/obj/NiControllerManager.h | 3 + include/obj/NiControllerSequence.h | 5 +- include/obj/NiTriBasedGeom.h | 2 +- src/niflib.cpp | 282 ++++++++++++++++++++--------- src/obj/NiControllerManager.cpp | 33 ++++ src/obj/NiControllerSequence.cpp | 9 +- src/obj/NiObjectNET.cpp | 18 +- src/obj/NiTriBasedGeom.cpp | 116 ++++++------ 9 files changed, 317 insertions(+), 153 deletions(-) diff --git a/include/niflib.h b/include/niflib.h index 6e391789..38381caf 100644 --- a/include/niflib.h +++ b/include/niflib.h @@ -209,7 +209,7 @@ NIFLIB_API void WriteNifTree( ostream & stream, Ref<NiObject> const & root, unsi * \param export_files What files to write: NIF, NIF + KF + KFM, NIF + KF's + KFM, KF only, KF's only * \param kf_type The KF type (Morrowind style, DAoC style, CivIV style, ...) */ -NIFLIB_API void WriteFileGroup( string const & file_name, Ref<NiObject> const & root, unsigned int version, ExportOptions export_files, NifGame kf_type ); +NIFLIB_API void WriteFileGroup( string const & file_name, Ref<NiObject> const & root, unsigned int version = VER_4_0_0_2, unsigned int user_version = 0, ExportOptions export_files = EXPORT_NIF, NifGame kf_type = KF_MW); /*! * Creates a clone of an entire tree of objects. diff --git a/include/obj/NiControllerManager.h b/include/obj/NiControllerManager.h index 9fa69342..53ed0967 100644 --- a/include/obj/NiControllerManager.h +++ b/include/obj/NiControllerManager.h @@ -50,6 +50,9 @@ public: */ vector<Ref<NiControllerSequence > > GetControllerSequences() const; void SetControllerSequences( const vector<Ref<NiControllerSequence > >& value ); + void AddSequence( Ref<NiControllerSequence > & obj ); + void RemoveSequence( Ref<NiControllerSequence > obj ); + void ClearSequences(); /*! * Refers to a NiDefaultAVObjectPalette. diff --git a/include/obj/NiControllerSequence.h b/include/obj/NiControllerSequence.h index 51a086fe..0905b62b 100644 --- a/include/obj/NiControllerSequence.h +++ b/include/obj/NiControllerSequence.h @@ -139,7 +139,10 @@ public: void SetTargetName( const string & value ); protected: - NiControllerManager * NiControllerSequence::Parent() const; + friend class NiControllerManager; + NiControllerManager * GetParent() const; + void SetParent(NiControllerManager *parent); + NI_CONTROLLER_SEQUENCE_MEMBERS STANDARD_INTERNAL_METHODS }; diff --git a/include/obj/NiTriBasedGeom.h b/include/obj/NiTriBasedGeom.h index 7c6f56f1..66c543e9 100644 --- a/include/obj/NiTriBasedGeom.h +++ b/include/obj/NiTriBasedGeom.h @@ -47,7 +47,7 @@ public: * NiSkinInstance and NiSkinData class. The bones must have a common * ancestor in the scenegraph. This becomes the skeleton root. */ - void BindSkin( vector< Ref<NiNode> > bone_nodes ); + void BindSkin( vector< Ref<NiNode> > bone_nodes, bool bind_to_scene = false ); void UnbindSkin(); /*! * Sets the skin weights in the attached NiSkinData object. diff --git a/src/niflib.cpp b/src/niflib.cpp index 8aa16109..d13542ab 100644 --- a/src/niflib.cpp +++ b/src/niflib.cpp @@ -15,6 +15,7 @@ All rights reserved. Please see niflib.h for licence. */ #include "../include/obj/NiAVObject.h" #include "../include/obj/NiTextKeyExtraData.h" #include "../include/obj/NiSequenceStreamHelper.h" +#include "../include/obj/NiControllerManager.h" #include "../include/obj/NiControllerSequence.h" #include "../include/obj/NiStringPalette.h" #include "../include/obj/NiSkinPartition.h" @@ -23,6 +24,9 @@ All rights reserved. Please see niflib.h for licence. */ #include "../include/obj/NiInterpolator.h" #include "../include/obj/NiKeyframeController.h" #include "../include/obj/NiKeyframeData.h" +#include "../include/obj/NiTransformInterpolator.h" +#include "../include/obj/NiTransformController.h" +#include "../include/obj/NiTransformData.h" #include "../include/obj/NiStringExtraData.h" #include "../include/obj/NiExtraData.h" #include "../include/obj/bhkRigidBody.h" @@ -285,19 +289,8 @@ vector<NiObjectRef> ReadNifList( istream & in ) { return blocks; } -// Writes a valid Nif File given a file name, a pointer to the root block of a file tree -void WriteNifTree( string const & file_name, NiObjectRef const & root_block, unsigned int version, unsigned int user_version ) { - //Open output file - ofstream out( file_name.c_str(), ofstream::binary ); - - WriteNifTree( out, root_block, version, user_version ); - - //Close file - out.close(); -} - -// Writes a valid Nif File given an ostream, a pointer to the root block of a file tree -void WriteNifTree( ostream & out, NiObjectRef const & root, unsigned int version, unsigned int user_version ) { +// Writes a valid Nif File given an ostream, a list to the root objects of a file tree +void WriteNifTree( ostream & out, list<NiObjectRef> const & roots, unsigned int version, unsigned int user_version ) { // Walk tree, resetting all block numbers //int block_count = ResetBlockNums( 0, root_block ); @@ -305,7 +298,9 @@ void WriteNifTree( ostream & out, NiObjectRef const & root, unsigned int version map<Type*,uint> type_map; map<NiObjectRef, uint> link_map; - EnumerateObjects( root, type_map, link_map ); + for (list<NiObjectRef>::const_iterator it = roots.begin(); it != roots.end(); ++it) { + EnumerateObjects( (*it), type_map, link_map ); + } //Build vectors for reverse look-up vector<NiObjectRef> objects(link_map.size()); @@ -365,22 +360,58 @@ void WriteNifTree( ostream & out, NiObjectRef const & root, unsigned int version //--Write Footer--// Footer footer; footer.numRoots = 0; - if (root->IsDerivedType(NiAVObject::TypeConst())) { - // Handle most NIF file formats - footer.numRoots = 1; - footer.roots.resize(1); - footer.roots[0] = StaticCast<NiAVObject>(root); - } else if (root->IsDerivedType(NiControllerSequence::TypeConst())) { - // KF animation files allow for multiple roots of type NiControllerSequence - for ( uint i = 0; i < objects.size(); ++i ) { - if (objects[i]->IsDerivedType(NiControllerSequence::TypeConst())) { - footer.roots.push_back(objects[i]); + if (roots.size() == 1) { + const NiObjectRef& root = roots.front(); + if (root->IsDerivedType(NiAVObject::TypeConst())) { + // Handle most NIF file formats + footer.numRoots = 1; + footer.roots.resize(1); + footer.roots[0] = StaticCast<NiObject>(root); + } else if (root->IsDerivedType(NiControllerSequence::TypeConst())) { + // KF animation files allow for multiple roots of type NiControllerSequence + for ( uint i = 0; i < objects.size(); ++i ) { + if (objects[i]->IsDerivedType(NiControllerSequence::TypeConst())) { + footer.roots.push_back(objects[i]); + } } } + } else { + footer.numRoots = roots.size(); + footer.roots.insert(footer.roots.end(), roots.begin(), roots.end()); } footer.Write( out, link_map, version, user_version ); } +// Writes a valid Nif File given a file name, a pointer to the root block of a file tree +void WriteNifTree( string const & file_name, NiObjectRef const & root_block, unsigned int version, unsigned int user_version ) { + //Open output file + ofstream out( file_name.c_str(), ofstream::binary ); + + list<NiObjectRef> roots; + roots.push_back(root_block); + WriteNifTree( out, roots, version, user_version ); + + //Close file + out.close(); +} + +void WriteNifTree( string const & file_name, list<NiObjectRef> const & roots, unsigned int version, unsigned int user_version ) { + //Open output file + ofstream out( file_name.c_str(), ofstream::binary ); + + WriteNifTree( out, roots, version, user_version ); + + //Close file + out.close(); +} + +// Writes a valid Nif File given an ostream, a pointer to the root object of a file tree +void WriteNifTree( ostream & out, NiObjectRef const & root, unsigned int version, unsigned int user_version ) { + list<NiObjectRef> roots; + roots.push_back(root); + WriteNifTree( out, roots, version, user_version ); +} + void EnumerateObjects( NiObjectRef const & root, map<Type*,uint> & type_map, map<NiObjectRef, uint> & link_map, bool reverse ) { //Ensure that this object has not already been visited if ( link_map.find( root ) != link_map.end() ) { @@ -500,16 +531,31 @@ list<NiObjectRef> GetAllObjectsByType( NiObjectRef const & root, const Type & ty // return result; //}; +// Create a valid +static std::string CreateFileName(std::string name) { + std::string retname = name; + std::string::size_type off = 0; + std::string::size_type pos = 0; + for (;;) { + pos = retname.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_^$~!#%&-{}()@'` ", off); + if (pos == std::string::npos) + break; + retname[pos] = '_'; + off = pos; + } + return retname; +} + //TODO: This was written by Amorilia. Figure out how to fix it. /*! * Helper function to split off animation from a nif tree. If no animation groups are defined, then both xnif_root and xkf_root will be null blocks. * \param root_block The root block of the full tree. - * \param xnif_root The root block of the tree without animation. - * \param xkf_root The root block of the animation tree. + * \param xnif_root The root object of the tree without animation. + * \param xkf_roots The root objects of the animation trees. * \param kfm The KFM structure (if required by style). * \param kf_type What type of keyframe tree to write (Morrowind style, DAoC style, ...). */ -void SplitNifTree( NiObjectRef const & root_block, NiObjectRef & xnif_root, NiObjectRef & xkf_root, Kfm & kfm, int kf_type ) { +static void SplitNifTree( NiObjectRef const & root_block, NiObjectRef & xnif_root, list<NiObjectRef> & xkf_roots, Kfm & kfm, int kf_type, unsigned int version, unsigned int user_version ) { // Do we have animation groups (a NiTextKeyExtraData block)? // If so, create XNif and XKf trees. NiObjectRef txtkey = GetObjectByType( root_block, NiTextKeyExtraData::TypeConst() ); @@ -520,29 +566,34 @@ void SplitNifTree( NiObjectRef const & root_block, NiObjectRef & xnif_root, NiOb if ( txtkey_block != NULL ) { if ( kf_type == KF_MW ) { // Construct the XNif file... - // We are lazy. (TODO: clone & remove keyframe controllers & keyframe data) - xnif_root = root_block; + + xnif_root = CloneNifTree(root_block, version, user_version); + // Now search and locate newer timeframe controllers and convert to keyframecontrollers + list<NiObjectRef> mgrs = GetAllObjectsByType( xnif_root, NiControllerManager::TypeConst() ); + for ( list<NiObjectRef>::iterator it = mgrs.begin(); it != mgrs.end(); ++it) { + NiControllerManagerRef mgr = DynamicCast<NiControllerManager>(*it); + if ( mgr == NULL ) { + continue; + } + NiObjectNETRef target = mgr->GetTarget(); + target->RemoveController( StaticCast<NiTimeController>(mgr) ); + vector<NiControllerSequenceRef> seqs = mgr->GetControllerSequences(); + for (vector<NiControllerSequenceRef>::iterator itr = seqs.begin(); itr != seqs.end(); ++itr) { + NiControllerSequenceRef seq = (*itr); + MergeNifTrees(DynamicCast<NiNode>(target), seq, version, user_version); + } + } + // Now the XKf file... // Create xkf root header. NiSequenceStreamHelperRef xkf_stream_helper = new NiSequenceStreamHelper; - xkf_root = xkf_stream_helper; - - // Add a copy of the NiTextKeyExtraData block to the XKf header. - NiTextKeyExtraDataRef xkf_txtkey_block = new NiTextKeyExtraData; - - //TODO: Have Amorilia fix this - xkf_stream_helper->AddExtraData( StaticCast<NiExtraData>(xkf_txtkey_block) ); - - //ITextKeyExtraData const *itxtkey_block = QueryTextKeyExtraData(txtkey_block); - //ITextKeyExtraData *ixkf_txtkey_block = QueryTextKeyExtraData(xkf_txtkey_block); - - xkf_txtkey_block->SetKeys( txtkey_block->GetKeys() ); - + xkf_roots.push_back( StaticCast<NiObject>(xkf_stream_helper) ); + // Append NiNodes with a NiKeyFrameController as NiStringExtraData blocks. list< pair< NiNodeRef, NiKeyframeControllerRef> > node_controllers; - list<NiObjectRef> nodes = GetAllObjectsByType( root_block, NiNode::TypeConst() ); + list<NiObjectRef> nodes = GetAllObjectsByType( xnif_root, NiNode::TypeConst() ); for ( list<NiObjectRef>::iterator it = nodes.begin(); it != nodes.end(); ++it) { NiNodeRef node = DynamicCast<NiNode>(*it); if ( node == NULL ) { @@ -552,58 +603,89 @@ void SplitNifTree( NiObjectRef const & root_block, NiObjectRef & xnif_root, NiOb //Find the first NiKeyframeController in the controller list, if any list<NiTimeControllerRef> controllers = node->GetControllers(); NiKeyframeControllerRef key_controller; - for ( list<NiTimeControllerRef>::iterator controller = controllers.begin(); controller != controllers.end(); ++controller ) { - key_controller = DynamicCast<NiKeyframeController>(*it); - if ( key_controller != NULL ) { - break; - } + for ( list<NiTimeControllerRef>::iterator it = controllers.begin(); it != controllers.end(); ++it ) { + + if ((*it)->IsDerivedType(NiKeyframeController::TypeConst())) { + key_controller = StaticCast<NiKeyframeController>(*it); + } else if ((*it)->IsDerivedType(NiTransformController::TypeConst())) { + NiTransformControllerRef trans = StaticCast<NiTransformController>(*it); + NiTransformInterpolatorRef interp = DynamicCast<NiTransformInterpolator>(trans->GetInterpolator()); + if (interp != NULL) { + NiTransformDataRef transData = interp->GetData(); + if (transData != NULL) { + NiKeyframeDataRef data = new NiKeyframeData(); + data->SetRotateType( transData->GetRotateType() ); + data->SetTranslateType( transData->GetTranslateType() ); + data->SetScaleType( transData->GetScaleType() ); + data->SetXRotateType( transData->GetXRotateType() ); + data->SetYRotateType( transData->GetYRotateType() ); + data->SetZRotateType( transData->GetZRotateType() ); + data->SetTranslateKeys( transData->GetTranslateKeys() ); + data->SetQuatRotateKeys( transData->GetQuatRotateKeys() ); + data->SetScaleKeys( transData->GetScaleKeys() ); + data->SetXRotateKeys( transData->GetXRotateKeys() ); + data->SetYRotateKeys( transData->GetYRotateKeys() ); + data->SetZRotateKeys( transData->GetZRotateKeys() ); + + key_controller = new NiKeyframeController(); + key_controller->SetFlags( trans->GetFlags() ); + key_controller->SetFrequency( trans->GetFrequency() ); + key_controller->SetPhase( trans->GetPhase() ); + key_controller->SetStartTime( trans->GetStartTime() ); + key_controller->SetStopTime( trans->GetStopTime() ); + key_controller->SetData( data ); + break; + } + } + } } //If this node has no keyframe controller, put it in the list if ( key_controller != NULL ) { node_controllers.push_back( pair<NiNodeRef,NiKeyframeControllerRef>( node, key_controller ) ); } - }; - + } for ( list< pair< NiNodeRef, NiKeyframeControllerRef> >::iterator it = node_controllers.begin(); it != node_controllers.end(); ++it ) { //Add string data NiStringExtraDataRef nodextra = new NiStringExtraData; nodextra->SetData( it->first->GetName() ); - xkf_stream_helper->AddExtraData( StaticCast<NiExtraData>(nodextra) ); - - // Add controllers & controller data. - NiKeyframeControllerRef controller = it->second; - NiKeyframeControllerRef xkf_controller = new NiKeyframeController; - - xkf_controller->SetFlags( controller->GetFlags() ); - xkf_controller->SetFrequency( controller->GetFrequency() ); - xkf_controller->SetPhase( controller->GetPhase() ); - xkf_controller->SetStartTime( controller->GetStartTime() ); - xkf_controller->SetStopTime( controller->GetStopTime() ); - - NiKeyframeDataRef xkf_data = new NiKeyframeData; - NiKeyframeDataRef kfdata = controller->GetData(); - xkf_controller->SetData( xkf_data ); - - xkf_data->SetRotateType( kfdata->GetRotateType() ); - xkf_data->SetTranslateType( kfdata->GetTranslateType() ); - xkf_data->SetScaleType( kfdata->GetScaleType() ); - xkf_data->SetQuatRotateKeys( kfdata->GetQuatRotateKeys() ); - xkf_data->SetXRotateKeys( kfdata->GetXRotateKeys() ); - xkf_data->SetYRotateKeys( kfdata->GetYRotateKeys() ); - xkf_data->SetZRotateKeys( kfdata->GetZRotateKeys() ); - xkf_data->SetTranslateKeys( kfdata->GetTranslateKeys() ); - xkf_data->SetScaleKeys( kfdata->GetScaleKeys() ); - + xkf_stream_helper->AddExtraData( StaticCast<NiExtraData>(nodextra), version ); + + NiKeyframeControllerRef controller = it->second; + (it->first)->RemoveController( StaticCast<NiTimeController>(controller) ); + xkf_stream_helper->AddController( StaticCast<NiTimeController>(controller) ); - }; - } else // TODO other games + } + + // Add a copy of the NiTextKeyExtraData block to the XKf header. + NiTextKeyExtraDataRef xkf_txtkey_block = new NiTextKeyExtraData; + xkf_stream_helper->AddExtraData( StaticCast<NiExtraData>(xkf_txtkey_block), version ); + xkf_txtkey_block->SetKeys( txtkey_block->GetKeys() ); + + } else if (kf_type == KF_CIV4) { + // Construct the Nif file without transform controllers ... + xnif_root = CloneNifTree(root_block, version, user_version); + + list<NiObjectRef> mgrs = GetAllObjectsByType( xnif_root, NiControllerManager::TypeConst() ); + for ( list<NiObjectRef>::iterator it = mgrs.begin(); it != mgrs.end(); ++it) { + NiControllerManagerRef mgr = DynamicCast<NiControllerManager>(*it); + if ( mgr == NULL ) { + continue; + } + NiObjectNETRef target = mgr->GetTarget(); + target->RemoveController( StaticCast<NiTimeController>(mgr) ); + vector<NiControllerSequenceRef> seqs = mgr->GetControllerSequences(); + for (vector<NiControllerSequenceRef>::iterator itr = seqs.begin(); itr != seqs.end(); ++itr) { + xkf_roots.push_back( StaticCast<NiObject>(*itr) ); + } + } + + } else throw runtime_error("KF splitting for the requested game is not yet implemented."); } else { // no animation groups: nothing to do - xnif_root = NULL; - xkf_root = NULL; + xnif_root = root_block; }; } @@ -618,7 +700,7 @@ void SplitNifTree( NiObjectRef const & root_block, NiObjectRef & xnif_root, NiOb //}; //TODO: This was written by Amorilia. Figure out how to fix it. -void WriteFileGroup( string const & file_name, NiObjectRef const & root_block, unsigned int version, ExportOptions export_files, NifGame kf_type ) { +void WriteFileGroup( string const & file_name, NiObjectRef const & root_block, unsigned int version, unsigned int user_version, ExportOptions export_files, NifGame kf_type ) { // Get base filename. uint file_name_slash = uint(file_name.rfind("\\") + 1); string file_name_path = file_name.substr(0, file_name_slash); @@ -628,23 +710,43 @@ void WriteFileGroup( string const & file_name, NiObjectRef const & root_block, u // Deal with the simple case first if ( export_files == EXPORT_NIF ) - WriteNifTree( file_name_path + file_name_base + ".nif", root_block, version ); // simply export the NIF file! + WriteNifTree( file_name_path + file_name_base + ".nif", root_block, version, user_version ); // simply export the NIF file! // Now consider all other cases else if ( kf_type == KF_MW ) { if ( export_files == EXPORT_NIF_KF ) { // for Morrowind we must also write the full NIF file - WriteNifTree( file_name_path + file_name_base + ".nif", root_block, version ); // simply export the NIF file! + WriteNifTree( file_name_path + file_name_base + ".nif", root_block, version, user_version ); // simply export the NIF file! NiObjectRef xnif_root; - NiObjectRef xkf_root; + list<NiObjectRef> xkf_roots; Kfm kfm; // dummy - SplitNifTree( root_block, xnif_root, xkf_root, kfm, KF_MW ); - if ( xnif_root != NULL ) { - WriteNifTree( file_name_path + "x" + file_name_base + ".nif", xnif_root, version ); - WriteNifTree( file_name_path + "x" + file_name_base + ".kf", xkf_root, version ); + SplitNifTree( root_block, xnif_root, xkf_roots, kfm, kf_type, version, user_version ); + if ( xnif_root != NULL && !xkf_roots.empty()) { + WriteNifTree( file_name_path + "x" + file_name_base + ".nif", xnif_root, version, user_version ); + WriteNifTree( file_name_path + "x" + file_name_base + ".kf", xkf_roots.front(), version, user_version ); }; } else throw runtime_error("Invalid export option."); - } else + } else if (kf_type == KF_CIV4) { + + NiObjectRef xnif_root; + list<NiObjectRef> xkf_roots; + Kfm kfm; // dummy + SplitNifTree( root_block, xnif_root, xkf_roots, kfm, kf_type, version, user_version ); + if ( export_files == EXPORT_NIF || export_files == EXPORT_NIF_KF || export_files == EXPORT_NIF_KF_MULTI ) { + WriteNifTree( file_name_path + file_name_base + ".nif", xnif_root, version, user_version ); + } + if ( export_files == EXPORT_NIF_KF || export_files == EXPORT_KF ) { + WriteNifTree( file_name_path + file_name_base + ".kf", xkf_roots, version, user_version ); + } else if ( export_files == EXPORT_NIF_KF_MULTI || export_files == EXPORT_KF_MULTI ) { + for ( list<NiObjectRef>::iterator it = xkf_roots.begin(); it != xkf_roots.end(); ++it ) { + NiControllerSequenceRef seq = DynamicCast<NiControllerSequence>(*it); + if (seq == NULL) + continue; + string path = file_name_path + file_name_base + "_" + CreateFileName(seq->GetTargetName()) + "_" + CreateFileName(seq->GetName()) + ".kf"; + WriteNifTree( path, StaticCast<NiObject>(seq), version, user_version ); + } + } + } else throw runtime_error("Not yet implemented."); }; @@ -780,7 +882,7 @@ void MergeNifTrees( const Ref<NiNode> & target, const Ref<NiControllerSequence> NiObjectRef tx_clone = txt_key->Clone( version, user_version ); NiExtraDataRef ext_dat = DynamicCast<NiExtraData>(tx_clone); if ( ext_dat != NULL ) { - target->AddExtraData( ext_dat ); + target->AddExtraData( ext_dat, version ); } } diff --git a/src/obj/NiControllerManager.cpp b/src/obj/NiControllerManager.cpp index 7dfd00cd..8cc85f49 100644 --- a/src/obj/NiControllerManager.cpp +++ b/src/obj/NiControllerManager.cpp @@ -4,6 +4,7 @@ All rights reserved. Please see niflib.h for licence. */ #include "../../include/obj/NiControllerManager.h" #include "../../include/obj/NiControllerSequence.h" #include "../../include/obj/NiDefaultAVObjectPalette.h" +#include <algorithm> using namespace Niflib; //Definition of TYPE constant @@ -50,9 +51,41 @@ vector<Ref<NiControllerSequence > > NiControllerManager::GetControllerSequences( } void NiControllerManager::SetControllerSequences( const vector<Ref<NiControllerSequence > >& value ) { + ClearSequences(); controllerSequences = value; + for (vector<NiControllerSequenceRef>::iterator it = controllerSequences.begin(); it != controllerSequences.end(); ++it) { + (*it)->SetParent(this); + } } +void NiControllerManager::AddSequence( Ref<NiControllerSequence > & obj ) { + vector<NiControllerSequenceRef>::iterator begin = controllerSequences.begin(); + vector<NiControllerSequenceRef>::iterator end = controllerSequences.end(); + vector<NiControllerSequenceRef>::iterator it = std::find(begin, end, obj); + if (it == end) { + controllerSequences.insert(end, obj); + obj->SetParent(this); + } +} + +void NiControllerManager::RemoveSequence( Ref<NiControllerSequence > obj ) { + vector<NiControllerSequenceRef>::iterator begin = controllerSequences.begin(); + vector<NiControllerSequenceRef>::iterator end = controllerSequences.end(); + vector<NiControllerSequenceRef>::iterator it = std::find(begin, end, obj); + if (it != end) { + (*it)->SetParent(NULL); + controllerSequences.erase(it); + } +} + +void NiControllerManager::ClearSequences() { + for (vector<NiControllerSequenceRef>::iterator it = controllerSequences.begin(); it != controllerSequences.end(); ++it) { + (*it)->SetParent(NULL); + } + controllerSequences.clear(); +} + + Ref<NiDefaultAVObjectPalette > NiControllerManager::GetObjectPalette() const { return objectPalette; } diff --git a/src/obj/NiControllerSequence.cpp b/src/obj/NiControllerSequence.cpp index 0abb5c94..e0d68c90 100644 --- a/src/obj/NiControllerSequence.cpp +++ b/src/obj/NiControllerSequence.cpp @@ -47,7 +47,14 @@ const Type & NiControllerSequence::GetType() const { return TYPE; }; -NiControllerManager * NiControllerSequence::Parent() const { return NULL; } +NiControllerManager * NiControllerSequence::GetParent() const { + return manager; +} + +void NiControllerSequence::SetParent(NiControllerManager * parent) { + manager = parent; +} + void NiControllerSequence::SetTextKey( const Ref<NiTextKeyExtraData> & txt_key ) { //Set new name diff --git a/src/obj/NiObjectNET.cpp b/src/obj/NiObjectNET.cpp index b0b42d35..dc039c02 100644 --- a/src/obj/NiObjectNET.cpp +++ b/src/obj/NiObjectNET.cpp @@ -140,15 +140,19 @@ void NiObjectNET::AddController( Ref<NiTimeController> & obj ) { } void NiObjectNET::RemoveController( Ref<NiTimeController> obj ) { - NiTimeControllerRef * cont = &controller; - while ( (*cont) != NULL ) { - if ( (*cont) == obj ) { + for(NiTimeControllerRef last = controller, cont = last, next; cont != NULL; cont = next ) { + next = cont->GetNextController(); + if ( cont == obj ) { //Cut this reference out of the list - (*cont)->SetTarget( NULL ); - (*cont) = (*cont)->GetNextController(); + cont->SetTarget( NULL ); + cont->SetNextController( NiTimeControllerRef() ); + if (cont == controller) + controller = next; + else + last->SetNextController(next); } else { - //Advance to the next controller - cont = &((*cont)->GetNextController()); + //Advance last to current controller + last = cont; } } } diff --git a/src/obj/NiTriBasedGeom.cpp b/src/obj/NiTriBasedGeom.cpp index a35d9c25..be4ab01a 100644 --- a/src/obj/NiTriBasedGeom.cpp +++ b/src/obj/NiTriBasedGeom.cpp @@ -74,7 +74,7 @@ Ref<NiSkinInstance> NiTriBasedGeom::GetSkinInstance() const { return skinInstance; } -void NiTriBasedGeom::BindSkin( vector< Ref<NiNode> > bone_nodes ) { +void NiTriBasedGeom::BindSkin( vector< Ref<NiNode> > bone_nodes, bool bind_to_scene ) { //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."); @@ -110,58 +110,70 @@ void NiTriBasedGeom::BindSkin( vector< Ref<NiNode> > bone_nodes ) { 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; - } - } + NiNodeRef skeleton_root; + if (bind_to_scene) { + // Just parent to the scene + NiNodeRef parent = GetParent(); + while (parent != NULL) { + skeleton_root = parent; + parent = parent->GetParent(); + } - 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; - } - } + } else { + 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; + } + } + } if ( skeleton_root == NULL ) { throw runtime_error("Failed to find suitable skeleton root."); -- GitLab