void FbxLoader::LoadAnimationClipData() { // load animation data const int animCount = scene->GetSrcObjectCount<FbxAnimStack>(); for(int ac = 0; ac < animCount; ++ac) { FbxAnimStack* currAnimStack = scene->GetSrcObject<FbxAnimStack>(ac); if(!currAnimStack) return; FbxString animStackName = currAnimStack->GetName(); FbxTakeInfo* takeInfo = scene->GetTakeInfo(animStackName); if(!takeInfo) return; FbxTime start = takeInfo->mLocalTimeSpan.GetStart(); FbxTime stop = takeInfo->mLocalTimeSpan.GetStop(); FbxTime::EMode mode = FbxTime::eDefaultMode; int startFrame = (int)start.GetFrameCount(mode); int stopFrame = (int)stop.GetFrameCount(mode); int animLength = stopFrame - startFrame + 1; shared_ptr<AnimClip> clip = make_shared<AnimClip>(); clip->name = animStackName; animationClips[clip->name] = clip; } }
std::string OsgFbxReader::readFbxAnimation(FbxNode* pNode, const char* targetName) { std::string result; for (int i = 0; i < fbxScene.GetSrcObjectCount<FbxAnimStack>(); ++i) { FbxAnimStack* pAnimStack = FbxCast<FbxAnimStack>(fbxScene.GetSrcObject<FbxAnimStack>(i)); int nbAnimLayers = pAnimStack->GetMemberCount<FbxAnimLayer>(); const char* pTakeName = pAnimStack->GetName(); if (!pTakeName || !*pTakeName) continue; for (int j = 0; j < nbAnimLayers; j++) { FbxAnimLayer* pAnimLayer = pAnimStack->GetMember<FbxAnimLayer>(j); osgAnimation::Animation* pAnimation = ::readFbxAnimation(pNode, pAnimLayer, pTakeName, targetName, pAnimationManager); if (pAnimation) { result = targetName; } } } return result; }
void Tools::DisplayAnimation::DisplayAnimation(FbxScene* i_scene) { DisplayCommon::DisplayString( "\n\n--------------------\nAnimation\n--------------------" ); int i; for ( i = 0; i < i_scene->GetSrcObjectCount<FbxAnimStack>(); i++ ) { FbxAnimStack *animStack = i_scene->GetSrcObject<FbxAnimStack>( i ); FbxString string = "Animation Stack Name: "; string += animStack->GetName(); string += "\n"; FBXSDK_printf( string ); DisplayCommon::DisplayString( string ); DisplayAnimation( animStack, i_scene->GetRootNode(), true ); DisplayAnimation( animStack, i_scene->GetRootNode() ); } }
/** * Add to the animation set, the animations contained within the FBX document, for the given skeleton */ UAnimSequence * UnFbx::FFbxImporter::ImportAnimations(USkeleton* Skeleton, UObject* Outer, TArray<FbxNode*>& SortedLinks, const FString& Name, UFbxAnimSequenceImportData* TemplateImportData, TArray<FbxNode*>& NodeArray) { // we need skeleton to create animsequence if (Skeleton == NULL) { return NULL; } int32 ValidTakeCount = 0; if (IsValidAnimationData(SortedLinks, NodeArray, ValidTakeCount) == false) { AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, LOCTEXT("FBXImport_InvalidAnimationData", "This does not contain any valid animation takes.")), FFbxErrors::Animation_InvalidData); return NULL; } UAnimSequence* LastCreatedAnim = NULL; int32 ResampleRate = DEFAULT_SAMPLERATE; if ( ImportOptions->bResample ) { // For FBX data, "Frame Rate" is just the speed at which the animation is played back. It can change // arbitrarily, and the underlying data can stay the same. What we really want here is the Sampling Rate, // ie: the number of animation keys per second. These are the individual animation curve keys // on the FBX nodes of the skeleton. So we loop through the nodes of the skeleton and find the maximum number // of keys that any node has, then divide this by the total length (in seconds) of the animation to find the // sampling rate of this set of data // we want the maximum resample rate, so that we don't lose any precision of fast anims, // and don't mind creating lerped frames for slow anims int32 MaxStackResampleRate = GetMaxSampleRate(SortedLinks, NodeArray); if(MaxStackResampleRate != 0) { ResampleRate = MaxStackResampleRate; } } int32 AnimStackCount = Scene->GetSrcObjectCount<FbxAnimStack>(); for( int32 AnimStackIndex = 0; AnimStackIndex < AnimStackCount; AnimStackIndex++ ) { FbxAnimStack* CurAnimStack = Scene->GetSrcObject<FbxAnimStack>(AnimStackIndex); FbxTimeSpan AnimTimeSpan = GetAnimationTimeSpan(SortedLinks[0], CurAnimStack); bool bValidAnimStack = ValidateAnimStack(SortedLinks, NodeArray, CurAnimStack, ResampleRate, ImportOptions->bImportMorph, AnimTimeSpan); // no animation if (!bValidAnimStack) { continue; } FString SequenceName = Name; if (ValidTakeCount > 1) { SequenceName += "_"; SequenceName += UTF8_TO_TCHAR(CurAnimStack->GetName()); } // See if this sequence already exists. SequenceName = ObjectTools::SanitizeObjectName(SequenceName); FString ParentPath = FString::Printf(TEXT("%s/%s"), *FPackageName::GetLongPackagePath(*Outer->GetName()), *SequenceName); UObject* ParentPackage = CreatePackage(NULL, *ParentPath); UObject* Object = LoadObject<UObject>(ParentPackage, *SequenceName, NULL, LOAD_None, NULL); UAnimSequence * DestSeq = Cast<UAnimSequence>(Object); // if object with same name exists, warn user if (Object && !DestSeq) { AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, LOCTEXT("Error_AssetExist", "Asset with same name exists. Can't overwrite another asset")), FFbxErrors::Generic_SameNameAssetExists); continue; // Move on to next sequence... } // If not, create new one now. if(!DestSeq) { DestSeq = NewObject<UAnimSequence>(ParentPackage, *SequenceName, RF_Public | RF_Standalone); // Notify the asset registry FAssetRegistryModule::AssetCreated(DestSeq); } else { DestSeq->RecycleAnimSequence(); } DestSeq->SetSkeleton(Skeleton); // since to know full path, reimport will need to do same UFbxAnimSequenceImportData* ImportData = UFbxAnimSequenceImportData::GetImportDataForAnimSequence(DestSeq, TemplateImportData); ImportData->Update(UFactory::CurrentFilename); ImportAnimation(Skeleton, DestSeq, Name, SortedLinks, NodeArray, CurAnimStack, ResampleRate, AnimTimeSpan); LastCreatedAnim = DestSeq; } return LastCreatedAnim; }
bool UnFbx::FFbxImporter::IsValidAnimationData(TArray<FbxNode*>& SortedLinks, TArray<FbxNode*>& NodeArray, int32& ValidTakeCount) { // If there are no valid links, then we cannot import the anim set if(SortedLinks.Num() == 0) { return false; } ValidTakeCount = 0; int32 AnimStackCount = Scene->GetSrcObjectCount<FbxAnimStack>(); int32 AnimStackIndex; for (AnimStackIndex = 0; AnimStackIndex < AnimStackCount; AnimStackIndex++ ) { FbxAnimStack* CurAnimStack = Scene->GetSrcObject<FbxAnimStack>(AnimStackIndex); // set current anim stack Scene->SetCurrentAnimationStack(CurAnimStack); // debug purpose for (int32 BoneIndex = 0; BoneIndex < SortedLinks.Num(); BoneIndex++) { FString BoneName = UTF8_TO_TCHAR(MakeName(SortedLinks[BoneIndex]->GetName())); UE_LOG(LogFbx, Log, TEXT("SortedLinks :(%d) %s"), BoneIndex, *BoneName ); } FbxTimeSpan AnimTimeSpan = GetAnimationTimeSpan(SortedLinks[0], CurAnimStack); if (AnimTimeSpan.GetDuration() <= 0) { AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("FBXImport_ZeroLength", "Animation Stack {0} does not contain any valid key. Try different time options when import."), FText::FromString(UTF8_TO_TCHAR(CurAnimStack->GetName())))), FFbxErrors::Animation_ZeroLength); continue; } ValidTakeCount++; { bool bBlendCurveFound = false; for ( int32 NodeIndex = 0; !bBlendCurveFound && NodeIndex < NodeArray.Num(); NodeIndex++ ) { // consider blendshape animation curve FbxGeometry* Geometry = (FbxGeometry*)NodeArray[NodeIndex]->GetNodeAttribute(); if (Geometry) { int32 BlendShapeDeformerCount = Geometry->GetDeformerCount(FbxDeformer::eBlendShape); for(int32 BlendShapeIndex = 0; BlendShapeIndex<BlendShapeDeformerCount; ++BlendShapeIndex) { FbxBlendShape* BlendShape = (FbxBlendShape*)Geometry->GetDeformer(BlendShapeIndex, FbxDeformer::eBlendShape); int32 BlendShapeChannelCount = BlendShape->GetBlendShapeChannelCount(); for(int32 ChannelIndex = 0; ChannelIndex<BlendShapeChannelCount; ++ChannelIndex) { FbxBlendShapeChannel* Channel = BlendShape->GetBlendShapeChannel(ChannelIndex); if(Channel) { // Get the percentage of influence of the shape. FbxAnimCurve* Curve = Geometry->GetShapeChannel(BlendShapeIndex, ChannelIndex, (FbxAnimLayer*)CurAnimStack->GetMember(0)); if (Curve && Curve->KeyGetCount() > 0) { bBlendCurveFound = true; break; } } } } } } } } return ( ValidTakeCount != 0 ); }
// This method is templated on the implementation of hctMayaSceneExporter/hctMaxSceneExporter::createScene() bool FbxToHkxConverter::createSceneStack(int animStackIndex) { hkxScene *scene = new hkxScene; scene->m_modeller.set(m_modeller.cString()); scene->m_asset = m_curFbxScene->GetSceneInfo()->Original_FileName.Get(); if (m_rootNode) { // create root node hkxNode* rootNode = new hkxNode; bool rigPass = (animStackIndex == -1); int currentAnimStackIndex = -1; FbxAnimStack* lAnimStack = NULL; if (rigPass && m_numAnimStacks > 0) { currentAnimStackIndex = 0; } else if (animStackIndex >= 0) { currentAnimStackIndex = animStackIndex; } if (currentAnimStackIndex != -1) { lAnimStack = m_curFbxScene->GetSrcObject<FbxAnimStack>(currentAnimStackIndex); m_curFbxScene->GetEvaluator()->SetContext(lAnimStack); } if (rigPass) { rootNode->m_name = "ROOT_NODE"; scene->m_sceneLength = 0.f; scene->m_numFrames = 1; printf("Converting nodes for root...\n"); } else { rootNode->m_name = lAnimStack->GetName(); const FbxTimeSpan animTimeSpan = lAnimStack->GetLocalTimeSpan(); scene->m_sceneLength = static_cast<hkReal>( animTimeSpan.GetDuration().GetSecondDouble() ); scene->m_numFrames = static_cast<hkUint32>( animTimeSpan.GetDuration().GetFrameCount(m_curFbxScene->GetGlobalSettings().GetTimeMode()) ); printf("Converting nodes for [%s]...\n", rootNode->m_name.cString()); } scene->m_rootNode = rootNode; rootNode->removeReference(); // Setup (identity) keyframes(s) for the 'static' root node rootNode->m_keyFrames.setSize( scene->m_numFrames > 1 ? 2 : 1, hkMatrix4::getIdentity() ); addNodesRecursive(scene, m_rootNode, scene->m_rootNode, currentAnimStackIndex); } m_scenes.pushBack(scene); return true; }
//=============================================================================================================================== void FBXLoader::LoadJointsAndAnimation(FbxNode* inNode) { // This is how each subset gets its bone/joint for animation FBXSubsets* subset = mSubsets[iCurrentSubset]; FbxMesh* mesh = inNode->GetMesh(); uint32 numOfDeformers = mesh->GetDeformerCount(); FbxAMatrix geomTrans = ZShadeSandboxMesh::FBXHelper::GetGeometryTransformation(inNode); // A deformer contains clusters. // A cluster contains a link, which is a joint. // Normally, There is only one deformer in a mesh but Maya has many types. for (uint32 deformerIndex = 0; deformerIndex < numOfDeformers; ++deformerIndex) { // Lets see if this deformer is a skin FbxSkin* skin = reinterpret_cast<FbxSkin*>(mesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); if (!skin) continue; uint32 numOfClusters = skin->GetClusterCount(); for (uint32 clusterIndex = 0; clusterIndex < numOfClusters; ++clusterIndex) { FbxCluster* cluster = skin->GetCluster(clusterIndex); string jointName = cluster->GetLink()->GetName(); uint32 jointIndex = FindJointIndexUsingName(jointName); subset->mJoints.push_back(jointIndex); FbxAMatrix transform; FbxAMatrix transformLink; FbxAMatrix globalBindposeInverse; // The transformation of the mesh at binding time cluster->GetTransformMatrix(transform); // The transformation of the cluster (joint) at binding time from joint space to world space cluster->GetTransformLinkMatrix(transformLink); globalBindposeInverse = transformLink.Inverse() * transform * geomTrans; // Update skeletal information mSkeleton.joints[jointIndex].globalBindposeInverse = globalBindposeInverse; mSkeleton.joints[jointIndex].node = cluster->GetLink(); // Associate each joint with the control points it affects uint32 numOfIndices = cluster->GetControlPointIndicesCount(); for (uint32 i = 0; i < numOfIndices; ++i) { ZShadeSandboxMesh::BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.blendingIndex = jointIndex; currBlendingIndexWeightPair.blendingWeight = cluster->GetControlPointWeights()[i]; mControlPoints[cluster->GetControlPointIndices()[i]]->blendingInfo.push_back(currBlendingIndexWeightPair); } // Animation information FbxAnimStack* animStack = m_pFbxScene->GetSrcObject<FbxAnimStack>(0); FbxString animStackName = animStack->GetName(); mAnimationName = animStackName.Buffer(); FbxTakeInfo* takeInfo = m_pFbxScene->GetTakeInfo(animStackName); FbxTime start = takeInfo->mLocalTimeSpan.GetStart(); FbxTime end = takeInfo->mLocalTimeSpan.GetStop(); mAnimationLength = end.GetFrameCount(FbxTime::eFrames24) - start.GetFrameCount(FbxTime::eFrames24) + 1; FBXKeyframe** anim = &mSkeleton.joints[jointIndex].animation; for (FbxLongLong i = start.GetFrameCount(FbxTime::eFrames24); i <= end.GetFrameCount(FbxTime::eFrames24); ++i) { FbxTime time; time.SetFrame(i, FbxTime::eFrames24); *anim = new FBXKeyframe(); (*anim)->frameNum = i; FbxAMatrix currentTransformOffset = inNode->EvaluateGlobalTransform(time) * geomTrans; (*anim)->globalTransform = currentTransformOffset.Inverse() * cluster->GetLink()->EvaluateGlobalTransform(time); anim = &((*anim)->next); } } } // Some control points have less than 4 joints // For a normal renderer, there are usually 4 joints ZShadeSandboxMesh::BlendingIndexWeightPair currBlendingIndexWeightPair; currBlendingIndexWeightPair.blendingIndex = 0; currBlendingIndexWeightPair.blendingWeight = 0; for (auto itr = mControlPoints.begin(); itr != mControlPoints.end(); ++itr) { for (unsigned int i = itr->second->blendingInfo.size(); i <= 4; ++i) { itr->second->blendingInfo.push_back(currBlendingIndexWeightPair); } } }
//----------------------------------------------------------------------------------- static void ImportMotions(SceneImport* import, FbxScene* scene, Matrix4x4& matrixStackTop, std::map<int, FbxNode*>& map, float framerate) { int animationCount = scene->GetSrcObjectCount<FbxAnimStack>(); if (animationCount == 0) { return; } if (import->skeletons.size() == 0) { return; } //Timing information for animation in this scene. How fast is the framerate in this file? FbxGlobalSettings& settings = scene->GetGlobalSettings(); FbxTime::EMode timeMode = settings.GetTimeMode(); double sceneFramerate; if (timeMode == FbxTime::eCustom) { sceneFramerate = settings.GetCustomFrameRate(); } else { sceneFramerate = FbxTime::GetFrameRate(timeMode); } //Only supporting one skeleton for now, update when needed. uint32_t skeletonCount = import->skeletons.size(); Skeleton* skeleton= import->skeletons.at(0); ASSERT_OR_DIE(skeletonCount == 1, "Had multiple skeletons, we only support 1!"); //Time between frames FbxTime advance; advance.SetSecondDouble((double)(1.0f / framerate)); for (int animIndex = 0; animIndex < animationCount; ++animIndex) { //Import Motions FbxAnimStack* anim = scene->GetSrcObject<FbxAnimStack>(); if (nullptr == anim) { continue; } FbxTime startTime = anim->LocalStart; FbxTime endTime = anim->LocalStop; FbxTime duration = endTime - startTime; scene->SetCurrentAnimationStack(anim); const char* motionName = anim->GetName(); float timeSpan = duration.GetSecondDouble(); AnimationMotion* motion = new AnimationMotion(motionName, timeSpan, framerate, skeleton); int jointCount = skeleton->GetJointCount(); for (int jointIndex = 0; jointIndex < jointCount; ++jointIndex) { FbxNode* node = map[jointIndex]; //Extracting world position //local, you would need to grab parent as well Matrix4x4* boneKeyframes = motion->GetJointKeyframes(jointIndex); FbxTime evalTime = FbxTime(0); for (uint32_t frameIndex = 0; frameIndex < motion->m_frameCount; ++frameIndex) { Matrix4x4* boneKeyframe = boneKeyframes + frameIndex; Matrix4x4 boneTransform = GetNodeWorldTransformAtTime(node, evalTime, matrixStackTop); double seconds = evalTime.GetSecondDouble(); seconds = seconds; *boneKeyframe = boneTransform; evalTime += advance; } } import->motions.push_back(motion); } }