--- title: FBXAnimation导入逻辑 date: 2023-12-11 11:28:36 excerpt: tags: rating: ⭐ --- # BVHImport插件中的案例 ```c++ UObject* UBVHImportFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& FileName, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) { if (!shouldImport) { return nullptr; } bool fileSuccess = BVHFile.ImportData(TCHAR_TO_ANSI(*FileName)); const bool bIsUnattended = (IsAutomatedImport() || FApp::IsUnattended() || IsRunningCommandlet() || GIsRunningUnattendedScript); // Check if it's a re-import if (InParent != nullptr) { UObject* ExistingObject = StaticFindObject(UObject::StaticClass(), InParent, *InName.ToString()); if (ExistingObject) { //reimport逻辑略 } } if (fileSuccess) { UAnimSequence* AnimSequence = NewObject(InParent, InName, Flags & ~EObjectFlags::RF_Transactional); if (Skeleton == NULL) { //创建骨骼资产略 } else { AnimSequence->SetSkeleton(Skeleton);//Skeleton bvhSkel AnimSequence->SetPreviewMesh(PreviewMesh); } ExtractAnimDataFromBVHFile(AnimSequence); return AnimSequence; } return nullptr; } ``` ```c++ void UBVHImportFactory::ExtractAnimDataFromBVHFile(UAnimSequence* AnimSequence) { if (AnimSequence) { // create animation IAnimationDataController& Controller = AnimSequence->GetController(); Controller.OpenBracket(LOCTEXT("ImportBVHAnimation", "Importing BVH Animation")); Controller.ResetModel(); // Write animation data into animation sequence. // Extract transform of hip to create root motion. const FReferenceSkeleton& RefSkeleton = AnimSequence->GetSkeleton()->GetReferenceSkeleton(); const FName RootName = RefSkeleton.GetBoneName(0); const int32 NumOfKeys = BVHFile.Root->FrameOffset.Num(); AnimSequence->ImportFileFramerate = (float)BVHFile.Header.DataRate; AnimSequence->ImportResampleFramerate = BVHFile.Header.DataRate; Controller.SetPlayLength(float(NumOfKeys - 1) / (float)BVHFile.Header.DataRate); Controller.SetFrameRate(FFrameRate(BVHFile.Header.DataRate, 1)); RecursiveReadKeysFromNode(Controller, BVHFile.Root.Get()); Controller.NotifyPopulated(); Controller.CloseBracket(); AnimSequence->AdditiveAnimType = EAdditiveAnimationType::AAT_None; AnimSequence->PostEditChange(); FAssetRegistryModule::AssetCreated(AnimSequence); AnimSequence->MarkPackageDirty(); } } ``` ```c++ void UBVHImportFactory::RecursiveReadKeysFromNode(IAnimationDataController& Controller, FNode* Node) { if (Node) { Controller.AddBoneTrack(Node->Name,false); Controller.SetBoneTrackKeys(Node->Name, Node->FrameOffset, Node->FrameQuat, Node->FrameScale,false); if (Node->Children.Num() > 0) { for (NodePtr Child : Node->Children) { RecursiveReadKeysFromNode(Controller, Child.Get()); } } } } ``` # UE5中使用FBXSDK导入动画逻辑 - UAnimSequence * UEditorEngine::ImportFbxAnimation - UAnimSequence * UnFbx::FFbxImporter::ImportAnimations UE5中FBXSDK相关函数调用方式: - bool FFbxImporter::OpenFile(FString Filename) - bool FFbxImporter::ImportFile(FString Filename, bool bPreventMaterialNameClash /*=false*/) # 参考 1. Interchange\\Runtime\\Source\\Parsers 1. InterchangeFbxParser.Build.cs 2. FbxInclude.h:FBXSDK头文件包含问题。 # UE5中使用FBXSDK导出动画逻辑 1. FFbxExporter::ExportSkeletalMeshToFbx => FFbxExporter::ExportAnimSequence => FFbxExporter::ExportAnimSequenceToFbx 2. FFbxExporter::CorrectAnimTrackInterpolation 直接导出会有问题,所以UE在这里做了一步Correct: ```c++ // The curve code doesn't differentiate between angles and other data, so an interpolation from 179 to -179 // will cause the bone to rotate all the way around through 0 degrees. So here we make a second pass over the // rotation tracks to convert the angles into a more interpolation-friendly format. FFbxExporter::CorrectAnimTrackInterpolation() { void FFbxExporter::CorrectAnimTrackInterpolation( TArray& BoneNodes, FbxAnimLayer* InAnimLayer ) { // Add the animation data to the bone nodes for(int32 BoneIndex = 0; BoneIndex < BoneNodes.Num(); ++BoneIndex) { FbxNode* CurrentBoneNode = BoneNodes[BoneIndex]; // Fetch the AnimCurves FbxAnimCurve* Curves[3]; Curves[0] = CurrentBoneNode->LclRotation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_X, true); Curves[1] = CurrentBoneNode->LclRotation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_Y, true); Curves[2] = CurrentBoneNode->LclRotation.GetCurve(InAnimLayer, FBXSDK_CURVENODE_COMPONENT_Z, true); for(int32 CurveIndex = 0; CurveIndex < 3; ++CurveIndex) { FbxAnimCurve* CurrentCurve = Curves[CurveIndex]; CurrentCurve->KeyModifyBegin(); float CurrentAngleOffset = 0.f; for(int32 KeyIndex = 1; KeyIndex < CurrentCurve->KeyGetCount(); ++KeyIndex) { float PreviousOutVal = CurrentCurve->KeyGetValue( KeyIndex-1 ); float CurrentOutVal = CurrentCurve->KeyGetValue( KeyIndex ); float DeltaAngle = (CurrentOutVal + CurrentAngleOffset) - PreviousOutVal; if(DeltaAngle >= 180) { CurrentAngleOffset -= 360; } else if(DeltaAngle <= -180) { CurrentAngleOffset += 360; } CurrentOutVal += CurrentAngleOffset; CurrentCurve->KeySetValue(KeyIndex, CurrentOutVal); } CurrentCurve->KeyModifyEnd(); } } } ``` ## 导入Curve相关逻辑 1. UnFbx::FFbxImporter::ImportAnimation() 2. UnFbx::FFbxImporter::ImportBlendShapeCurves() 3. UnFbx::FFbxImporter::ImportCurveToAnimSequence() => UnFbx::FFbxImporter::ImportCurve() ## 异步机制改进 ```c++ void URuntimeAudioImporterLibrary::ImportAudioFromFile(const FString& FilePath, ERuntimeAudioFormat AudioFormat) { AsyncTask(ENamedThreads::AnyBackgroundHiPriTask, [WeakThis = TWeakObjectPtr(this), FilePath, AudioFormat]() mutable { if (!WeakThis.IsValid()) { UE_LOG(LogRuntimeAudioImporter, Error, TEXT("Failed to import audio from file '%s' because the RuntimeAudioImporterLibrary object has been destroyed"), *FilePath); return; } FGCObjectScopeGuard Guard(WeakThis.Get()); if (!FPaths::FileExists(FilePath)) { WeakThis->OnResult_Internal(nullptr, ERuntimeImportStatus::AudioDoesNotExist); return; } AudioFormat = AudioFormat == ERuntimeAudioFormat::Auto ? GetAudioFormat(FilePath) : AudioFormat; AudioFormat = AudioFormat == ERuntimeAudioFormat::Invalid ? ERuntimeAudioFormat::Auto : AudioFormat; TArray64 AudioBuffer; if (!LoadAudioFileToArray(AudioBuffer, *FilePath)) { WeakThis->OnResult_Internal(nullptr, ERuntimeImportStatus::LoadFileToArrayError); return; } WeakThis->ImportAudioFromBuffer(MoveTemp(AudioBuffer), AudioFormat); });} ``` # 其他导入代码 ```c++ USoundWave* FFacialAnimationImportItem::ImportSoundWave(const FString& InSoundWavePackageName, const FString& InSoundWaveAssetName, const FString& InWavFilename) const { } ```