bool UAnimExporterITP::ExportText(const FExportObjectInnerContext* Context, UObject* Object, const TCHAR* Type, FOutputDevice& Ar, FFeedbackContext* Warn, uint32 PortFlags /*= 0*/) { UAnimSequence* AnimSeq = CastChecked<UAnimSequence>(Object); USkeleton* Skeleton = AnimSeq->GetSkeleton(); const FReferenceSkeleton& RefSkeleton = Skeleton->GetReferenceSkeleton(); USkeletalMesh* SkelMesh = Skeleton->GetPreviewMesh(); if (AnimSeq->SequenceLength == 0.f) { // something is wrong return false; } const float FrameRate = AnimSeq->NumFrames / AnimSeq->SequenceLength; // Open another archive FArchive* File = IFileManager::Get().CreateFileWriter(*UExporter::CurrentFilename); // Let's try the header... File->Logf(TEXT("{")); File->Logf(TEXT("\t\"metadata\":{")); File->Logf(TEXT("\t\t\"type\":\"itpanim\",")); File->Logf(TEXT("\t\t\"version\":2")); File->Logf(TEXT("\t},")); File->Logf(TEXT("\t\"sequence\":{")); File->Logf(TEXT("\t\t\"frames\":%d,"), AnimSeq->NumFrames); File->Logf(TEXT("\t\t\"length\":%f,"), AnimSeq->SequenceLength); File->Logf(TEXT("\t\t\"bonecount\":%d,"), RefSkeleton.GetNum()); File->Logf(TEXT("\t\t\"tracks\":[")); bool firstOutput = false; for (int32 BoneIndex = 0; BoneIndex < RefSkeleton.GetNum(); ++BoneIndex) { //int32 BoneTreeIndex = Skeleton->GetSkeletonBoneIndexFromMeshBoneIndex(SkelMesh, BoneIndex); int32 BoneTrackIndex = Skeleton->GetAnimationTrackIndex(BoneIndex, AnimSeq); if (BoneTrackIndex == INDEX_NONE) { // If this sequence does not have a track for the current bone, then skip it continue; } if (firstOutput) { File->Logf(TEXT("\t\t\t},")); } firstOutput = true; File->Logf(TEXT("\t\t\t{")); File->Logf(TEXT("\t\t\t\t\"bone\":%d,"), BoneIndex); File->Logf(TEXT("\t\t\t\t\"transforms\":[")); float AnimTime = 0.0f; float AnimEndTime = AnimSeq->SequenceLength; // Subtracts 1 because NumFrames includes an initial pose for 0.0 second double TimePerKey = (AnimSeq->SequenceLength / (AnimSeq->NumFrames - 1)); const float AnimTimeIncrement = TimePerKey; bool bLastKey = false; // Step through each frame and add the bone's transformation data while (!bLastKey) { const TArray<FBoneNode>& BoneTree = Skeleton->GetBoneTree(); FTransform BoneAtom; AnimSeq->GetBoneTransform(BoneAtom, BoneTrackIndex, AnimTime, true); bLastKey = AnimTime >= AnimEndTime; File->Logf(TEXT("\t\t\t\t\t{")); FQuat rot = BoneAtom.GetRotation(); // For the root bone, we need to fix-up the rotation because Unreal exports // animations with Y-forward for some reason (maybe because Maya?) if (BoneIndex == 0) { FQuat addRot(FVector(0.0f, 0.0f, 1.0f), -1.57f); rot = addRot * rot; } File->Logf(TEXT("\t\t\t\t\t\t\"rot\":[%f,%f,%f,%f],"), rot.X, rot.Y, rot.Z, rot.W); FVector trans = BoneAtom.GetTranslation(); // Sanjay: If it's skeleton retargeting, change the translation to be from the ref pose skeleton if (BoneTree[BoneIndex].TranslationRetargetingMode == EBoneTranslationRetargetingMode::Skeleton) { const FTransform& BoneTransform = RefSkeleton.GetRefBonePose()[BoneIndex]; trans = BoneTransform.GetTranslation(); } File->Logf(TEXT("\t\t\t\t\t\t\"trans\":[%f,%f,%f]"), trans.X, trans.Y, trans.Z); if (!bLastKey) { File->Logf(TEXT("\t\t\t\t\t},")); } else { File->Logf(TEXT("\t\t\t\t\t}")); } AnimTime += AnimTimeIncrement; } File->Logf(TEXT("\t\t\t\t]"), BoneIndex); } File->Logf(TEXT("\t\t\t}")); File->Logf(TEXT("\t\t]")); File->Logf(TEXT("\t}")); File->Logf(TEXT("}")); delete File; return true; }
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(); } } }