236 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						||
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.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<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
 | 
						||
{
 | 
						||
 | 
						||
}
 | 
						||
``` |