# 大致步骤
使用UnrealEditor-Cmd执行资源导入、处理、输出功能。

1. 导入资产 (这段可以参考FBX)
   1. 角色 
   2. 动画 FBX
2. 处理资产 
   1. IKRig
   2. RetargetRig
   3. ControlRig?
3. 输出资产
   1. FBX?
   2. 使用MovieRenderQueue 渲染 MP4?

## 短期需求
1. AI 动捕、或者GPT生成 BVH、FBX。
2. 特定骨骼结构。
3. UE骨骼确定。
4. 可能需要高并发(后面再考虑)
5. 输出视频。
6. 工期1~2周。

## 需要解决的问题是
- [ ] 支持多种格式的文件
- [ ] `CustomCommandFunction`编写以及生成**IKRig**、**IKRetarget**资产
- [ ] 输出FBX或者MP4

## 输出文件
- *.BVH
- 

## 生产环境
使用Docker或者其他容器工具制作处理用的镜像,之后使用群集系统控制运行?

## CustomCommandFunction
关检测 UCommandlet

# 测试方法
```bash
D:\UnrealEngine\UE_5.1\Engine\Binaries\Win64\UnrealEditor-Cmd.exe D:\UnrealEngine\Project\AIAutomationTools\AIAutomationTools.uproject -run=AIAnimationAutomationCommandlet
```

如果带有配置文件
```bash
D:\UnrealEngine\UE_5.1\Engine\Binaries\Win64\UnrealEditor-Cmd.exe D:\UnrealEngine\Project\AIAutomationTools\AIAutomationTools.uproject -run=AIAnimationAutomationCommandlet -importsettings=C:\\Users\\BlueRose\\Desktop\\ImportJson.json
```

```json
{
    "ImportGroups": [
        {
            "GroupName": "Group11",
			//导入文件名
			"Filenames": [
                "C:\\Users\\BlueRose\\Desktop\\untitled2_Anim.FBX"
            ],
            //生成资产的文件夹
            "DestinationPath": "Animation",
            //使用的Factory类名
            "FactoryName": "FbxFactory",
            "bReplaceExisting": 1,
            "bSkipReadOnly": 0,
            //导入动画资产的设置
            "ImportSettings": {
                "OriginalImportType": 2,
                "MeshTypeToImport": 2,
                "Skeleton": "/Game/1/untitled2_Skeleton.untitled2_Skeleton"
            }
        }
    ]
}
```

# 其他资料
## IKRig & IKRetarget=
`Engine\Plugins\Animation\IKRig`
- FAssetTypeActions_AnimationAssetRetarget
- FAssetTypeActions_IKRigDefinition:UIKRigDefinition
- FAssetTypeActions_IKRetargeter:UIKRetargeter

## Retarger
```c++
/** The runtime processor that converts an input pose from a source skeleton into an output pose on a target skeleton.  
 * To use: 
 * 1. Initialize a processor with a Source/Target skeletal mesh and a UIKRetargeter asset. 
 * 2. Call RunRetargeter and pass in a source pose as an array of global-space transforms
 * 3. RunRetargeter returns an array of global space transforms for the target skeleton. 
 /
```

## UEditorUtilityLibrary


##
Python参考:
`Plugins\MovieScene\MovieRenderPipeline\Content\Python`


## 渲染影片(旧版)
MovieRenderPipelineCommandLine
```bash
"D:\Program Files\UE_4.24\Engine\Binaries\Win64\UE4Editor.exe" 
D:\Projects\UnrealProjects\renderMovieTest\renderMovieTest.uproject 
/Game/maps/shot0010 
-MovieSceneCaptureType="/Script/MovieSceneCapture.AutomatedLevelSequenceCapture" 
-LevelSequence="/Game/Sequences/Shot0010" 
-MovieFolder="D:\Projects\UnrealProjects\renderMovieTest\outputs" 
-NoLoadingScreen -game
```

void FMovieRenderPipelineCoreModule::StartupModule() 处理CommandLine变量
```c++
// Look to see if they supplied arguments on the command line indicating they wish to render a movie.  
if (IsTryingToRenderMovieFromCommandLine(SequenceAssetValue, SettingsAssetValue, MoviePipelineLocalExecutorClassType, MoviePipelineClassType))  
{  
	UE_LOG(LogMovieRenderPipeline, Log, TEXT("Detected that the user intends to render a movie. Waiting until engine loop init is complete to ensure "));  
	// Register a hook to wait until the engine has finished loading to increase the likelihood that the desired classes are loaded.  
	FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FMovieRenderPipelineCoreModule::OnMapLoadFinished);  
}
```
通过委托调用渲染,最终到void FMovieRenderPipelineCoreModule::InitializeCommandLineMovieRender()

```c++
void FMovieRenderPipelineCoreModule::InitializeCommandLineMovieRender()  
{  
	#if WITH_EDITOR  
	//const bool bIsGameMode = !GEditor;  
	//if (!bIsGameMode)  
	//{  
	// UE_LOG(LogMovieRenderPipeline, Fatal, TEXT("Command Line Renders must be performed in -game mode, otherwise use the editor ui/python and PIE. Add -game to your command line arguments."));  
	// FPlatformMisc::RequestExitWithStatus(false, MoviePipelineErrorCodes::Critical);  
	// return;  
	//}  
	#endif  
	  
	// Attempt to convert their command line arguments into the required objects.  
	UMoviePipelineExecutorBase* ExecutorBase = nullptr;  
	UMoviePipelineQueue* Queue = nullptr;  
	  
	uint8 ReturnCode = ParseMovieRenderData(SequenceAssetValue, SettingsAssetValue, MoviePipelineLocalExecutorClassType, MoviePipelineClassType,  
	/*Out*/ Queue, /*Out*/ ExecutorBase);  
	if (!ensureMsgf(ExecutorBase, TEXT("There was a failure parsing the command line and a movie render cannot be started. Check the log for more details.")))  
	{  
	// Take the failure return code from the detection of our command line arguments.  
	FPlatformMisc::RequestExitWithStatus(/*Force*/ false, /*ReturnCode*/ ReturnCode);  
	return;  
	}  
	else  
	{  
	UE_LOG(LogMovieRenderPipeline, Log, TEXT("Successfully detected and loaded required movie arguments. Rendering will begin once the map is loaded."));  
	if (Queue)  
	{  
	UE_LOG(LogMovieRenderPipeline, Log, TEXT("NumJobs: %d ExecutorClass: %s"), Queue->GetJobs().Num(), *ExecutorBase->GetClass()->GetName());  
	}  
	else  
	{  
	UE_LOG(LogMovieRenderPipeline, Log, TEXT("ExecutorClass: %s"), *ExecutorBase->GetClass()->GetName());  
	}  
	  
	}  
	  
	// We add the Executor to the root set. It will own all of the configuration data so this keeps it nicely in memory until finished,  
	// and means we only have to add/remove one thing from the root set, everything else uses normal outer ownership.  
	ExecutorBase->AddToRoot();  
	ExecutorBase->OnExecutorFinished().AddRaw(this, &FMovieRenderPipelineCoreModule::OnCommandLineMovieRenderCompleted);  
	ExecutorBase->OnExecutorErrored().AddRaw(this, &FMovieRenderPipelineCoreModule::OnCommandLineMovieRenderErrored);  
	  
	ExecutorBase->Execute(Queue);  
}
```

# 多线程
https://blog.csdn.net/j756915370/article/details/122752719

Runtime\Core\Public\Async\
- FFutureState
- FTaskGraphImplementation

### 2.6 TaskGraph 系统中的 Wait
在 TaskGraph 中,无论是 Event->Wait() 还是 FTaskGraphInterface::Get().WaitUntilTaskCompletes() ,最终都是调用到 FTaskGraphImplementation::WaitUntilTasksComplete 中的。