void FFbxExporter::ExportAnimSequenceToFbx(const UAnimSequence* AnimSeq, const USkeletalMesh* SkelMesh, TArray<FbxNode*>& BoneNodes, FbxAnimLayer* InAnimLayer, float AnimStartOffset, float AnimEndOffset, float AnimPlayRate, float StartTime) { USkeleton* Skeleton = AnimSeq->GetSkeleton(); if (AnimSeq->SequenceLength == 0.f) { // something is wrong return; } const float FrameRate = AnimSeq->NumFrames / AnimSeq->SequenceLength; // set time correctly FbxTime ExportedStartTime, ExportedStopTime; if ( FMath::IsNearlyEqual(FrameRate, DEFAULT_SAMPLERATE, 1.f) ) { ExportedStartTime.SetGlobalTimeMode(FbxTime::eFrames30); ExportedStopTime.SetGlobalTimeMode(FbxTime::eFrames30); } else { ExportedStartTime.SetGlobalTimeMode(FbxTime::eCustom, FrameRate); ExportedStopTime.SetGlobalTimeMode(FbxTime::eCustom, FrameRate); } ExportedStartTime.SetSecondDouble(0.f); ExportedStopTime.SetSecondDouble(AnimSeq->SequenceLength); FbxTimeSpan ExportedTimeSpan; ExportedTimeSpan.Set(ExportedStartTime, ExportedStopTime); AnimStack->SetLocalTimeSpan(ExportedTimeSpan); // Add the animation data to the bone nodes for(int32 BoneIndex = 0; BoneIndex < BoneNodes.Num(); ++BoneIndex) { FbxNode* CurrentBoneNode = BoneNodes[BoneIndex]; // Create the AnimCurves FbxAnimCurve* Curves[6]; Curves[0] = CurrentBoneNode->LclTranslation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); Curves[1] = CurrentBoneNode->LclTranslation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); Curves[2] = CurrentBoneNode->LclTranslation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); Curves[3] = CurrentBoneNode->LclRotation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); Curves[4] = CurrentBoneNode->LclRotation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); Curves[5] = CurrentBoneNode->LclRotation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); float AnimTime = AnimStartOffset; float AnimEndTime = (AnimSeq->SequenceLength - AnimEndOffset); // Subtracts 1 because NumFrames includes an initial pose for 0.0 second double TimePerKey = (AnimSeq->SequenceLength / (AnimSeq->NumFrames-1)); const float AnimTimeIncrement = TimePerKey * AnimPlayRate; FbxTime ExportTime; ExportTime.SetSecondDouble(StartTime); FbxTime ExportTimeIncrement; ExportTimeIncrement.SetSecondDouble( TimePerKey ); int32 BoneTreeIndex = Skeleton->GetSkeletonBoneIndexFromMeshBoneIndex(SkelMesh, BoneIndex); int32 BoneTrackIndex = Skeleton->GetAnimationTrackIndex(BoneTreeIndex, AnimSeq); if(BoneTrackIndex == INDEX_NONE) { // If this sequence does not have a track for the current bone, then skip it continue; } for(int32 i = 0; i < 6; ++i) { Curves[i]->KeyModifyBegin(); } bool bLastKey = false; // Step through each frame and add the bone's transformation data while (!bLastKey) { FTransform BoneAtom; AnimSeq->GetBoneTransform(BoneAtom, BoneTrackIndex, AnimTime, true); FbxVector4 Translation = Converter.ConvertToFbxPos(BoneAtom.GetTranslation()); FbxVector4 Rotation = Converter.ConvertToFbxRot(BoneAtom.GetRotation().Euler()); int32 lKeyIndex; bLastKey = AnimTime >= AnimEndTime; for(int32 i = 0, j=3; i < 3; ++i, ++j) { lKeyIndex = Curves[i]->KeyAdd(ExportTime); Curves[i]->KeySetValue(lKeyIndex, Translation[i]); Curves[i]->KeySetInterpolation(lKeyIndex, bLastKey ? FbxAnimCurveDef::eInterpolationConstant : FbxAnimCurveDef::eInterpolationCubic); if( bLastKey ) { Curves[i]->KeySetConstantMode( lKeyIndex, FbxAnimCurveDef::eConstantStandard ); } lKeyIndex = Curves[j]->KeyAdd(ExportTime); Curves[j]->KeySetValue(lKeyIndex, Rotation[i]); Curves[j]->KeySetInterpolation(lKeyIndex, bLastKey ? FbxAnimCurveDef::eInterpolationConstant : FbxAnimCurveDef::eInterpolationCubic); if( bLastKey ) { Curves[j]->KeySetConstantMode( lKeyIndex, FbxAnimCurveDef::eConstantStandard ); } } ExportTime += ExportTimeIncrement; AnimTime += AnimTimeIncrement; } for(int32 i = 0; i < 6; ++i) { Curves[i]->KeyModifyEnd(); } } }