BlueRoseNote/03-UnrealEngine/Editor/FBXAnimation导入&导出逻辑.md

236 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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<UAnimSequence>(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.hFBXSDK头文件包含问题。
# 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<FbxNode*>& 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();
}
}
}
```
## AnimSequence生成逻辑
```c++
//创建UAnimSequence
FString ParentPath = FString::Printf(TEXT("%s/%s"), *FPackageName::GetLongPackagePath(*Outer->GetName()), *SequenceName);
UObject* ParentPackage = CreatePackage( *ParentPath);
UObject* Object = LoadObject<UObject>(ParentPackage, *SequenceName, NULL, (LOAD_Quiet | LOAD_NoWarn), NULL);
UAnimSequence * DestSeq = Cast<UAnimSequence>(Object);
//设置骨骼
DestSeq->SetSkeleton(Skeleton);
//设置文件帧率率与重采样率修改帧率记得调用IAnimationDataController.OpenBracket()、NotifyPopulated()、CloseBracket()
IAnimationDataController& AnimationDataController = AnimSequence->GetController();
AnimationDataController.OpenBracket(FText::FromString("Importing Animation"));
DestSeq->ImportFileFramerate = GetOriginalFbxFramerate();
DestSeq->ImportResampleFramerate = ResampleRate;
AnimationDataController.SetFrameRate(FFrameRate(AnimSequencesFPS,1));
AnimationDataController.SetPlayLength(FacialTime);
AnimationDataController.NotifyPopulated();
AnimationDataController.CloseBracket();
```
### 导入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<URuntimeAudioImporterLibrary>(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<uint8> 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
{
}
```