diff --git a/nif_math.cpp b/nif_math.cpp index cf89aed573418302ccf2747089ce5228ff01ab92..b14c294f27263ff6ff614c865a2f9db23f2d98d1 100644 --- a/nif_math.cpp +++ b/nif_math.cpp @@ -217,6 +217,46 @@ Matrix44::Matrix44( const Vector3 & t, const Matrix33 & r, float scale ) { *this = s * rt; } +Matrix33 Matrix44::GetRotation() const { + const Matrix44 & t = *this; + + Matrix33 m( t[0][0], t[0][1], t[0][2], + t[1][0], t[1][1], t[1][2], + t[2][0], t[2][1], t[2][2] + ); + + //--Extract Scale from first 3 rows--// + float scale[3]; + for (int r = 0; r < 3; ++r) { + //Get scale for this row + scale[r] = sqrt(m[r][0] * m[r][0] + m[r][1] * m[r][1] + m[r][2] * m[r][2]); + + //Normalize the row by dividing each factor by scale + m[r][0] /= scale[r]; + m[r][1] /= scale[r]; + m[r][2] /= scale[r]; + } + + //Return result + return m; +} + +Vector3 Matrix44::GetScale() const { + const Matrix44 & m = *this; + float scale[3]; + for (int r = 0; r < 3; ++r) { + //Get scale for this row + scale[r] = sqrt(m[r][0] * m[r][0] + m[r][1] * m[r][1] + m[r][2] * m[r][2]); + } + return Vector3( scale[0], scale[1], scale[2] ); +} + + +Vector3 Matrix44::GetTranslation() const { + const Matrix44 & m = *this; + return Vector3( m[3][0], m[3][1], m[3][2] ); +} + Matrix44 Matrix44::operator*( const Matrix44 & rh ) const { return Matrix44(*this) *= rh; } diff --git a/nif_math.h b/nif_math.h index b5a2c57320294ee214c5b814ccca803377c784b0..75c530ccdef5239f1e6cc58e417bca1855ee9b2d 100644 --- a/nif_math.h +++ b/nif_math.h @@ -729,7 +729,11 @@ struct Matrix44 { */ NIFLIB_API float Adjunct( int skip_r, int skip_c ) const; - //undocumented + NIFLIB_API Matrix33 GetRotation() const; + NIFLIB_API Vector3 GetScale() const; + NIFLIB_API Vector3 GetTranslation() const; + + //undocumented, may be removed NIFLIB_API void AsFloatArr( float out[4][4] ) { out[0][0] = rows[0][0]; out[0][1] = rows[0][1]; out[0][2] = rows[0][2]; out[0][3] = rows[0][3]; out[1][0] = rows[1][0]; out[1][1] = rows[1][1]; out[1][2] = rows[1][2]; out[1][3] = rows[1][3]; diff --git a/niflib.cpp b/niflib.cpp index 96234bdfe2d648379450935ea21028c826939ae1..6865c23d17afeb4c0ca3126890f4310358129e29 100644 --- a/niflib.cpp +++ b/niflib.cpp @@ -336,6 +336,14 @@ vector<NiObjectRef> ReadNifList( istream & in ) { blocks[i]->FixLinks( blocks, link_stack, version, user_version ); } + //TODO: Make this an optional step? + for (uint i = 0; i < blocks.size(); ++i) { + NiNodeRef node = DynamicCast<NiNode>(blocks[i]); + if ( node != NULL && node->IsSkeletonRoot() ) { + node->GoToSkeletonBindPosition(); + } + } + //TODO: No longer necessary? ////Build up the bind pose matricies into their world-space equivalents //NiAVObjectRef av_root = DynamicCast<NiAVObject>( FindRoot(blocks) ); diff --git a/obj/NiNode.cpp b/obj/NiNode.cpp index 5a038299db9b19e3534a2580c5528b1162ebfda4..c9974b1c146ee55ecc0068081b22f7a6dac6a5b5 100644 --- a/obj/NiNode.cpp +++ b/obj/NiNode.cpp @@ -5,6 +5,7 @@ All rights reserved. Please see niflib.h for licence. */ #include "NiAVObject.h" #include "NiDynamicEffect.h" #include "NiSkinInstance.h" +#include "NiSkinData.h" //Definition of TYPE constant const Type NiNode::TYPE("NiNode", &NI_NODE_PARENT::TYPE ); @@ -14,6 +15,11 @@ NiNode::NiNode() NI_NODE_CONSTRUCT {} NiNode::~NiNode() { //Clear Children ClearChildren(); + + //Unbind any attached skins + for ( list<NiSkinInstance*>::iterator it = skins.begin(); it != skins.end(); ++it ) { + (*it)->SkeletonLost(); + } } void NiNode::Read( istream& in, list<uint> & link_stack, unsigned int version, unsigned int user_version ) { @@ -93,10 +99,6 @@ bool NiNode::IsSkinInfluence() const { return ((flags & 8) == 0); } -void NiNode::GoToSkeletonBindPosition() { - //TODO:: Implement GoToSkeletonBindPosition() -} - void NiNode::AddSkin( NiSkinInstance * skin_inst ) { //Ensure that all bones are below this node on the scene graph vector<NiNodeRef> bones = skin_inst->GetBones(); @@ -151,4 +153,72 @@ void NiNode::SetSkinFlag( bool n ) { //Requested value is different, flip bit flags ^= 8; } +} + +void NiNode::GoToSkeletonBindPosition() { + map<NiNodeRef, Matrix44> world_positions; + + //Loop through all attached skins, straightening the skeleton on each + for ( list<NiSkinInstance*>::iterator it = skins.begin(); it != skins.end(); ++it ) { + //Get Bone list and Skin Data + vector<NiNodeRef> bones = (*it)->GetBones(); + NiSkinDataRef skin_data = (*it)->GetSkinData(); + + if ( skin_data == NULL ) { + //There's no skin data for this skin instance; skip it. + continue; + } + + //Get bone data from NiSkinData class + vector<SkinData> bone_data = skin_data->GetBoneData(); + + //Make sure the counts match + if ( bones.size() != bone_data.size() ) { + throw runtime_error( "Bone counts in NiSkinInstance and attached NiSkinData must match" ); + } + + //Loop through all bones influing this skin + for ( uint i = 0; i < bones.size(); ++i ) { + //Get current offset Matrix for this bone + Matrix44 parent_offset( bone_data[i].translation, + bone_data[i].rotation, + bone_data[i].scale ); + + //Loop through all bones again, checking for any that have this bone as a parent + for ( uint j = 0; j < bones.size(); ++j ) { + if ( bones[j]->GetParent() == bones[i] ) { + //Node 2 has node 1 as a parent + + //Get child offset Matrix33 + Matrix44 child_offset( bone_data[j].translation, + bone_data[j].rotation, + bone_data[j].scale ); + + //Do calculation to get correct bone postion in relation to parent + Matrix44 inverse_co = child_offset.Inverse(); + world_positions[bones[j]] = inverse_co * parent_offset; + } + } + } + } + + //All the world positoins have been calculated, so use them to set the local + //positoins + + //Put skeleton root into world positions if it's not already there + if ( world_positions.find( this ) == world_positions.end() ) { + world_positions[this] = GetWorldTransform(); + } + + //Now loop through all nodes in the world_positions map + for ( map< NiNodeRef, Matrix44>::iterator it = world_positions.begin(); it != world_positions.end(); ++it ) { + Matrix44 res_mat = world_positions[it->first] * world_positions[it->first->GetParent()].Inverse(); + + //Store result in node's local transforms + it->first->SetLocalRotation( res_mat.GetRotation() ); + it->first->SetLocalTranslation( res_mat.GetTranslation()); + Vector3 scale = res_mat.GetScale(); + it->first->SetLocalScale( 1.0f );//scale.x + scale.y + scale.z / 3.0f ); + } + } \ No newline at end of file diff --git a/obj/NiSkinData.cpp b/obj/NiSkinData.cpp index 4a775f423fcda5ad794f13a1908a70472181818b..fc925df7bd8d3bc4b2a4729e49d07844c296652f 100644 --- a/obj/NiSkinData.cpp +++ b/obj/NiSkinData.cpp @@ -37,3 +37,21 @@ const Type & NiSkinData::GetType() const { return TYPE; }; +void NiSkinData::SetBoneData( const vector<SkinData> & n ) { + boneList = n; +} + +vector<SkinData> NiSkinData::GetBoneData() const { + return boneList; +} + +void NiSkinData::SetOverallTransform( const Matrix44 & n ) { + translation = n.GetTranslation(); + rotation = n.GetRotation(); + Vector3 s = n.GetScale(); + scale = s.x + s.y + s.z / 3.0f; +} + +Matrix44 NiSkinData::GetOverallTransform() const { + return Matrix44( translation, rotation, scale ); +} \ No newline at end of file diff --git a/obj/NiSkinData.h b/obj/NiSkinData.h index c22a5812216ab269353a3a6a159a9d7e91f89f65..ff7fb1cef676bc5a99a3f04522042fe5c53b7241 100644 --- a/obj/NiSkinData.h +++ b/obj/NiSkinData.h @@ -33,6 +33,12 @@ public: virtual void FixLinks( const vector<NiObjectRef> & objects, list<uint> & link_stack, unsigned int version, unsigned int user_version ); virtual list<NiObjectRef> GetRefs() const; virtual const Type & GetType() const; + + void SetOverallTransform( const Matrix44 & n ); + Matrix44 GetOverallTransform() const; + + void SetBoneData( const vector<SkinData> & n ); + vector<SkinData> GetBoneData() const; protected: NI_SKIN_DATA_MEMBERS }; diff --git a/obj/NiSkinInstance.cpp b/obj/NiSkinInstance.cpp index e258d0517edf733b23483e6f68339792427d165a..67f46409b440034dd68cf2abf652ea6268a6dca1 100644 --- a/obj/NiSkinInstance.cpp +++ b/obj/NiSkinInstance.cpp @@ -28,6 +28,11 @@ string NiSkinInstance::asString( bool verbose ) const { void NiSkinInstance::FixLinks( const vector<NiObjectRef> & objects, list<uint> & link_stack, unsigned int version, unsigned int user_version ) { NI_SKIN_INSTANCE_FIXLINKS + + //Inform newly fixed skeleton root of attachment + if ( skeletonRoot != NULL ) { + skeletonRoot->AddSkin( this ); + } } list<NiObjectRef> NiSkinInstance::GetRefs() const { @@ -94,4 +99,15 @@ Ref<NiSkinPartition> NiSkinInstance::GetSkinPartition() const { void NiSkinInstance::SetSkinPartition( const Ref<NiSkinPartition> & n ) { skinPartition = n; +} + +void NiSkinInstance::SkeletonLost() { + skeletonRoot = NULL; + + //Clear bone list + bones.clear(); + + //Destroy skin data + data = NULL; + skinPartition = NULL; } \ No newline at end of file diff --git a/obj/NiSkinInstance.h b/obj/NiSkinInstance.h index 5ebe8dc9ac91ae505cdc1d44cde66b4709bb30c7..e2162550f7b5ff96bfa126024b0f1c066f234a96 100644 --- a/obj/NiSkinInstance.h +++ b/obj/NiSkinInstance.h @@ -62,6 +62,12 @@ public: Ref<NiSkinPartition> GetSkinPartition() const; void SetSkinPartition( const Ref<NiSkinPartition> & n ); + /*! + * Called by skeleton root NiNode when it self destructs to inform this skin + * instance that the skeleton has been lost. Should not be called directly. + */ + void SkeletonLost(); + protected: NI_SKIN_INSTANCE_MEMBERS };