diff --git a/include/niflib.h b/include/niflib.h index 6e391789f414d29748b0c9802471cdebe93dd8e7..38381caf1a074de87e3dd3e522cdbc65e5da7751 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 9fa69342d1877eb21b4b115d78610c172413c205..53ed09677e64dc7bc3cbf5008e643761e0ce7a6a 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 51a086fe13aceff75a2846fa35d8518d3a3c17fe..0905b62b69b829702e4cf27d8e4baaac0385ed87 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 7c6f56f15df16839ea26e1da5b15dccdd73b50e4..66c543e9206a929ddc3b8422a3496593be4b82a8 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 8aa16109cd0e7ae166552d2014489d053f01a211..d13542ab58b4e0d3840819fb42bc23cbe3152bea 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 7dfd00cd9dff960370110855b3f69674b0f50745..8cc85f49732195b9f9a0b05b8c958a18bd5e61bd 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 0abb5c94c024d358880842d7917e56ea50371c69..e0d68c90e821da0947892c4f03535178a3a8a710 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 b0b42d350c59280f12220575acbd0514f85916c1..dc039c025a167f6b5866b96e2917ba0dd118c108 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 a35d9c2580c27077d431dcb731a2430c5b794526..be4ab01aaa182afd33653486db5efac1072fa28f 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.");