EM_Task/UnrealEd/Private/Fbx/FbxCompareWindow.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

435 lines
25 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "FbxCompareWindow.h"
#include "Modules/ModuleManager.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Colors/SColorBlock.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Views/SListView.h"
#include "Widgets/Views/STreeView.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Layout/SSplitter.h"
#include "EditorStyleSet.h"
#include "Factories/FbxAnimSequenceImportData.h"
#include "IDocumentation.h"
#include "PropertyEditorModule.h"
#include "IDetailsView.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Factories/FbxSceneImportFactory.h"
#include "Framework/Application/SlateApplication.h"
#include "Engine/SkeletalMesh.h"
#include "Engine/StaticMesh.h"
#include "Rendering/SkeletalMeshModel.h"
#include "Materials/Material.h"
#define LOCTEXT_NAMESPACE "FBXOption"
bool SFbxSkeltonConflictWindow::HasConflict()
{
if (SourceObject->IsA(USkeletalMesh::StaticClass()))
{
for (TSharedPtr<FSkeletonCompareData> SkeletonCompareData: DisplaySkeletonTreeItem)
{
if (SkeletonCompareData->bChildConflict)
{
// We have at least one skeleton conflict
return true;
}
}
}
return false;
}
void SFbxSkeltonConflictWindow::Construct(const FArguments& InArgs)
{
bRevertReimport = false;
bShowSectionFlag[EFBXCompareSection_Skeleton] = true;
bShowSectionFlag[EFBXCompareSection_References] = true;
WidgetWindow = InArgs._WidgetWindow;
if (InArgs._AssetReferencingSkeleton != nullptr)
{
// Copy the aray
AssetReferencingSkeleton = *(InArgs._AssetReferencingSkeleton);
}
SourceData = InArgs._SourceData;
ResultData = InArgs._ResultData;
SourceObject = InArgs._SourceObject;
bIsPreviewConflict = InArgs._bIsPreviewConflict;
if (SourceObject->IsA(USkeletalMesh::StaticClass()))
{
FilSkeletonTreeItem();
}
SetMatchJointInfo();
// Skeleton comparison
TSharedPtr<SWidget> SkeletonCompareSection = ConstructSkeletonComparison();
TSharedPtr<SWidget> SkeletonReferencesSection = ConstructSkeletonReference();
this->ChildSlot
[SNew(SBox)
[SNew(SVerticalBox) + SVerticalBox::Slot().FillHeight(1.0f)[SNew(SBorder).BorderImage(FEditorStyle::GetBrush("ToolPanel.DarkGroupBorder"))[SNew(SVerticalBox) + SVerticalBox::Slot().FillHeight(1.0f).Padding(2)[SNew(SSplitter).Orientation(Orient_Vertical).ResizeMode(ESplitterResizeMode::Fill) + SSplitter::Slot().Value(0.8f)[
// Skeleton Compare section
SkeletonCompareSection.ToSharedRef()] +
SSplitter::Slot().Value(0.2f)[
// Skeleton Compare section
SkeletonReferencesSection.ToSharedRef()]]]] +
SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(2)
[SNew(SHorizontalBox) + SHorizontalBox::Slot()
.AutoWidth()
.Padding(2.0f, 0.0f)
[SNew(SButton)
.HAlign(HAlign_Center)
.Text(LOCTEXT("SFbxSkeltonConflictWindow_Preview_Done", "Done"))
.OnClicked(this, &SFbxSkeltonConflictWindow::OnDone)]]]];
}
FReply SFbxSkeltonConflictWindow::SetSectionVisible(EFBXCompareSection SectionIndex)
{
bShowSectionFlag[SectionIndex] = !bShowSectionFlag[SectionIndex];
return FReply::Handled();
}
EVisibility SFbxSkeltonConflictWindow::IsSectionVisible(EFBXCompareSection SectionIndex)
{
return bShowSectionFlag[SectionIndex] ? EVisibility::All : EVisibility::Collapsed;
}
const FSlateBrush* SFbxSkeltonConflictWindow::GetCollapsableArrow(EFBXCompareSection SectionIndex) const
{
return bShowSectionFlag[SectionIndex] ? FEditorStyle::GetBrush("Symbols.DownArrow") : FEditorStyle::GetBrush("Symbols.RightArrow");
}
void RecursivelyExpandTreeItem(TSharedPtr<STreeView<TSharedPtr<FSkeletonCompareData>>> CompareTree, TSharedPtr<FSkeletonCompareData> RowData)
{
if (RowData->bInitialAutoExpand || !RowData->bMatchJoint || !RowData->bChildConflict)
{
return;
}
RowData->bInitialAutoExpand = true;
CompareTree->SetItemExpansion(RowData, true);
for (TSharedPtr<FSkeletonCompareData> ChildRowData: RowData->ChildJoints)
{
RecursivelyExpandTreeItem(CompareTree, ChildRowData);
}
}
TSharedPtr<SWidget> SFbxSkeltonConflictWindow::ConstructSkeletonComparison()
{
if (!SourceObject->IsA(USkeletalMesh::StaticClass()))
{
// Return an empty widget, we do not show the skeleton when the mesh is not a skeletal mesh
return SNew(SBox);
}
FText SkeletonStatus = bIsPreviewConflict ? FText(LOCTEXT("SFbxSkeltonConflictWindow_ConstructSkeletonComparison_MatchAndMergePreview", "The skeleton has some conflicts")) :
FText(ResultData->CompSkeleton.bSkeletonFitMesh ? LOCTEXT("SFbxSkeltonConflictWindow_ConstructSkeletonComparison_MatchAndMerge", "The skeleton can be merged") : LOCTEXT("SFbxSkeltonConflictWindow_ConstructSkeletonComparison_CannotMatchAndMerge", "The skeleton must be regenerated, it cannot be merged"));
CompareTree = SNew(STreeView<TSharedPtr<FSkeletonCompareData>>)
.ItemHeight(24)
.SelectionMode(ESelectionMode::None)
.TreeItemsSource(&DisplaySkeletonTreeItem)
.OnGenerateRow(this, &SFbxSkeltonConflictWindow::OnGenerateRowCompareTreeView)
.OnGetChildren(this, &SFbxSkeltonConflictWindow::OnGetChildrenRowCompareTreeView);
for (TSharedPtr<FSkeletonCompareData> RowData: DisplaySkeletonTreeItem)
{
RecursivelyExpandTreeItem(CompareTree, RowData);
}
return SNew(SBox)
[SNew(SBorder)
.Padding(FMargin(3))
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
[SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().Padding(2)[SNew(SHorizontalBox) + SHorizontalBox::Slot().AutoWidth()[SNew(SButton).HAlign(HAlign_Center).VAlign(VAlign_Center).IsFocusable(false).ButtonStyle(FEditorStyle::Get(), "NoBorder").OnClicked(this, &SFbxSkeltonConflictWindow::SetSectionVisible, EFBXCompareSection_Skeleton)[SNew(SImage).Image(this, &SFbxSkeltonConflictWindow::GetCollapsableArrow, EFBXCompareSection_Skeleton)]] + SHorizontalBox::Slot().AutoWidth()[SNew(STextBlock).Font(FEditorStyle::GetFontStyle("DetailsView.CategoryFontStyle")).Text(LOCTEXT("SFbxSkeltonConflictWindow_SkeletonCompareHeader", "Skeleton"))]] + SVerticalBox::Slot().FillHeight(1.0f).Padding(2)[SNew(SBox).Visibility(TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &SFbxSkeltonConflictWindow::IsSectionVisible, EFBXCompareSection_Skeleton)))[SNew(SBorder).Padding(FMargin(3)).BorderImage(FEditorStyle::GetBrush("ToolPanel.DarkGroupBorder"))[SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().Padding(2)[SNew(STextBlock).Font(FEditorStyle::GetFontStyle("DetailsView.CategoryFontStyle")).Text(SkeletonStatus).ColorAndOpacity(ResultData->CompSkeleton.bSkeletonFitMesh ? FSlateColor::UseForeground() : FSlateColor(FLinearColor(0.7f, 0.3f, 0.0f)))] + SVerticalBox::Slot().AutoHeight().Padding(2)[SNew(SSeparator).Orientation(EOrientation::Orient_Horizontal)] + SVerticalBox::Slot().FillHeight(1.0f).Padding(2)[CompareTree.ToSharedRef()]]]]]];
}
TSharedPtr<SWidget> SFbxSkeltonConflictWindow::ConstructSkeletonReference()
{
if (!SourceObject->IsA(USkeletalMesh::StaticClass()))
{
// Return an empty widget, we do not show the skeleton when the mesh is not a skeletal mesh
return SNew(SBox);
}
FString SkeletonReferenceStatistic;
if (AssetReferencingSkeleton.Num() > 0)
{
SkeletonReferenceStatistic += TEXT("Skeleton is references by ") + FString::FromInt(AssetReferencingSkeleton.Num()) + TEXT(" assets.");
}
return SNew(SBox)
[SNew(SBorder)
.Padding(FMargin(3))
.BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
[SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().Padding(2)[SNew(SHorizontalBox) + SHorizontalBox::Slot().AutoWidth()[SNew(SButton).HAlign(HAlign_Center).VAlign(VAlign_Center).IsFocusable(false).ButtonStyle(FEditorStyle::Get(), "NoBorder").OnClicked(this, &SFbxSkeltonConflictWindow::SetSectionVisible, EFBXCompareSection_References)[SNew(SImage).Image(this, &SFbxSkeltonConflictWindow::GetCollapsableArrow, EFBXCompareSection_References)]] + SHorizontalBox::Slot().AutoWidth()[SNew(STextBlock).Font(FEditorStyle::GetFontStyle("DetailsView.CategoryFontStyle")).Text(LOCTEXT("SFbxSkeltonConflictWindow_SkeletonReferencesHeader", "References"))]] + SVerticalBox::Slot().FillHeight(1.0f).Padding(2)[SNew(SBox).Visibility(TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &SFbxSkeltonConflictWindow::IsSectionVisible, EFBXCompareSection_References)))[SNew(SBorder).Padding(FMargin(3)).BorderImage(FEditorStyle::GetBrush("ToolPanel.DarkGroupBorder"))[SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().Padding(2)[SNew(STextBlock).Font(FEditorStyle::GetFontStyle("DetailsView.CategoryFontStyle")).Text(FText::FromString(SkeletonReferenceStatistic))] + SVerticalBox::Slot().AutoHeight().Padding(2)[SNew(SSeparator).Orientation(EOrientation::Orient_Horizontal)] + SVerticalBox::Slot().FillHeight(1.0f).Padding(2)[
// Show the asset referencing this skeleton
SNew(SListView<TSharedPtr<FString>>).ListItemsSource(&AssetReferencingSkeleton).OnGenerateRow(this, &SFbxSkeltonConflictWindow::OnGenerateRowAssetReferencingSkeleton)]]]]]];
}
class SCompareSkeletonTreeViewItem: public STableRow<TSharedPtr<FSkeletonCompareData>>
{
public:
SLATE_BEGIN_ARGS(SCompareSkeletonTreeViewItem)
: _SkeletonCompareData(nullptr), _SourceData(nullptr), _ResultData(nullptr)
{}
/** The item content. */
SLATE_ARGUMENT(TSharedPtr<FSkeletonCompareData>, SkeletonCompareData)
SLATE_ARGUMENT(FCompMesh*, SourceData)
SLATE_ARGUMENT(FCompMesh*, ResultData)
SLATE_END_ARGS()
/**
* Construct the widget
*
* @param InArgs A declaration from which to construct the widget
*/
void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView)
{
SkeletonCompareData = InArgs._SkeletonCompareData;
SourceData = InArgs._SourceData;
ResultData = InArgs._ResultData;
// This is suppose to always be valid
check(SkeletonCompareData.IsValid());
check(SourceData != nullptr);
check(ResultData != nullptr);
const FSlateBrush* JointIcon = SkeletonCompareData->bMatchJoint ? FEditorStyle::GetDefaultBrush() : SkeletonCompareData->FbxJointIndex != INDEX_NONE ? FEditorStyle::GetBrush("FBXIcon.ReimportCompareAdd") :
FEditorStyle::GetBrush("FBXIcon.ReimportCompareRemoved");
// Prepare the tooltip
FString Tooltip = SkeletonCompareData->bMatchJoint ? TEXT("") : FText(SkeletonCompareData->FbxJointIndex != INDEX_NONE ? LOCTEXT("SCompareSkeletonTreeViewItem_AddJoint_tooltip", "Fbx reimport will add this joint") : LOCTEXT("SCompareSkeletonTreeViewItem_RemoveJoint_tooltip", "Fbx reimport will remove this joint")).ToString();
FSlateColor ForegroundTextColor = (SkeletonCompareData->bMatchJoint && !SkeletonCompareData->bChildConflict) ? FSlateColor::UseForeground() : (SkeletonCompareData->bMatchJoint ? FSlateColor(FLinearColor(0.9f, 0.7f, 0.5f)) : FSlateColor(FLinearColor(0.7f, 0.3f, 0.0f)));
this->ChildSlot
[SNew(SHorizontalBox) + SHorizontalBox::Slot().AutoWidth()[SNew(SExpanderArrow, SharedThis(this))]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.0f, 2.0f, 6.0f, 2.0f)
[SNew(SImage)
.Image(JointIcon)
.Visibility(JointIcon != FEditorStyle::GetDefaultBrush() ? EVisibility::Visible : EVisibility::Collapsed)] +
SHorizontalBox::Slot()
.FillWidth(1.0f)
.Padding(0.0f, 3.0f, 6.0f, 3.0f)
.VAlign(VAlign_Center)
[SNew(STextBlock)
.Text(FText::FromString(SkeletonCompareData->JointName.ToString()))
.ToolTipText(FText::FromString(Tooltip))
.ColorAndOpacity(ForegroundTextColor)]];
STableRow<TSharedPtr<FSkeletonCompareData>>::ConstructInternal(
STableRow::FArguments()
.ShowSelection(true),
InOwnerTableView);
}
private:
/** The node info to build the tree view row from. */
TSharedPtr<FSkeletonCompareData> SkeletonCompareData;
FCompMesh* SourceData;
FCompMesh* ResultData;
};
TSharedRef<ITableRow> SFbxSkeltonConflictWindow::OnGenerateRowCompareTreeView(TSharedPtr<FSkeletonCompareData> RowData, const TSharedRef<STableViewBase>& Table)
{
TSharedRef<SCompareSkeletonTreeViewItem> ReturnRow = SNew(SCompareSkeletonTreeViewItem, Table)
.SkeletonCompareData(RowData)
.SourceData(SourceData)
.ResultData(ResultData);
return ReturnRow;
}
void SFbxSkeltonConflictWindow::OnGetChildrenRowCompareTreeView(TSharedPtr<FSkeletonCompareData> InParent, TArray<TSharedPtr<FSkeletonCompareData>>& OutChildren)
{
for (int32 ChildIndex = 0; ChildIndex < InParent->ChildJoints.Num(); ++ChildIndex)
{
TSharedPtr<FSkeletonCompareData> ChildJoint = InParent->ChildJoints[ChildIndex];
if (ChildJoint.IsValid())
{
OutChildren.Add(ChildJoint);
}
}
}
TSharedRef<ITableRow> SFbxSkeltonConflictWindow::OnGenerateRowAssetReferencingSkeleton(TSharedPtr<FString> InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
int32 AssetListIndex = AssetReferencingSkeleton.Find(InItem);
bool LightBackgroundColor = AssetListIndex % 2 == 0;
return SNew(STableRow<TSharedPtr<FString>>, OwnerTable)
[SNew(SBorder)
.BorderImage(LightBackgroundColor ? FEditorStyle::GetBrush("ToolPanel.GroupBorder") : FEditorStyle::GetBrush("ToolPanel.DarkGroupBorder"))
[SNew(STextBlock)
.Text(FText::FromString(*(InItem.Get())))]];
}
void SFbxSkeltonConflictWindow::FilSkeletonTreeItem()
{
// Create all the entries
for (int32 RowIndex = 0; RowIndex < SourceData->CompSkeleton.Joints.Num(); ++RowIndex)
{
TSharedPtr<FSkeletonCompareData> CompareRowData = MakeShareable(new FSkeletonCompareData());
int32 AddedIndex = CurrentSkeletonTreeItem.Add(CompareRowData);
check(AddedIndex == RowIndex);
CompareRowData->CurrentJointIndex = RowIndex;
CompareRowData->JointName = SourceData->CompSkeleton.Joints[RowIndex].Name;
CompareRowData->ChildJointIndexes = SourceData->CompSkeleton.Joints[RowIndex].ChildIndexes;
}
// Set the childrens and parent pointer
for (int32 RowIndex = 0; RowIndex < SourceData->CompSkeleton.Joints.Num(); ++RowIndex)
{
check(CurrentSkeletonTreeItem.IsValidIndex(RowIndex));
TSharedPtr<FSkeletonCompareData> CompareRowData = CurrentSkeletonTreeItem[RowIndex];
if (CurrentSkeletonTreeItem.IsValidIndex(SourceData->CompSkeleton.Joints[RowIndex].ParentIndex))
{
CompareRowData->ParentJoint = CurrentSkeletonTreeItem[SourceData->CompSkeleton.Joints[RowIndex].ParentIndex];
}
for (int32 ChildJointIndex = 0; ChildJointIndex < CompareRowData->ChildJointIndexes.Num(); ++ChildJointIndex)
{
if (CurrentSkeletonTreeItem.IsValidIndex(CompareRowData->ChildJointIndexes[ChildJointIndex]))
{
CompareRowData->ChildJoints.Add(CurrentSkeletonTreeItem[CompareRowData->ChildJointIndexes[ChildJointIndex]]);
}
}
}
for (int32 RowIndex = 0; RowIndex < ResultData->CompSkeleton.Joints.Num(); ++RowIndex)
{
TSharedPtr<FSkeletonCompareData> CompareRowData = MakeShareable(new FSkeletonCompareData());
int32 AddedIndex = FbxSkeletonTreeItem.Add(CompareRowData);
check(AddedIndex == RowIndex);
CompareRowData->FbxJointIndex = RowIndex;
CompareRowData->JointName = ResultData->CompSkeleton.Joints[RowIndex].Name;
CompareRowData->ChildJointIndexes = ResultData->CompSkeleton.Joints[RowIndex].ChildIndexes;
}
// Set the childrens and parent pointer
for (int32 RowIndex = 0; RowIndex < ResultData->CompSkeleton.Joints.Num(); ++RowIndex)
{
check(FbxSkeletonTreeItem.IsValidIndex(RowIndex));
TSharedPtr<FSkeletonCompareData> CompareRowData = FbxSkeletonTreeItem[RowIndex];
if (FbxSkeletonTreeItem.IsValidIndex(ResultData->CompSkeleton.Joints[RowIndex].ParentIndex))
{
CompareRowData->ParentJoint = FbxSkeletonTreeItem[ResultData->CompSkeleton.Joints[RowIndex].ParentIndex];
}
for (int32 ChildJointIndex = 0; ChildJointIndex < CompareRowData->ChildJointIndexes.Num(); ++ChildJointIndex)
{
if (FbxSkeletonTreeItem.IsValidIndex(CompareRowData->ChildJointIndexes[ChildJointIndex]))
{
CompareRowData->ChildJoints.Add(FbxSkeletonTreeItem[CompareRowData->ChildJointIndexes[ChildJointIndex]]);
}
}
}
}
void SFbxSkeltonConflictWindow::RecursiveMatchJointInfo(TSharedPtr<FSkeletonCompareData> SkeletonItem)
{
TArray<TSharedPtr<FSkeletonCompareData>> DisplayChilds;
// Find the display child
if (CurrentSkeletonTreeItem.IsValidIndex(SkeletonItem->CurrentJointIndex))
{
for (int32 ChildIndex = 0; ChildIndex < CurrentSkeletonTreeItem[SkeletonItem->CurrentJointIndex]->ChildJoints.Num(); ++ChildIndex)
{
DisplayChilds.Add(CurrentSkeletonTreeItem[SkeletonItem->CurrentJointIndex]->ChildJoints[ChildIndex]);
}
}
if (FbxSkeletonTreeItem.IsValidIndex(SkeletonItem->FbxJointIndex))
{
for (int32 ChildIndex = 0; ChildIndex < FbxSkeletonTreeItem[SkeletonItem->FbxJointIndex]->ChildJoints.Num(); ++ChildIndex)
{
TSharedPtr<FSkeletonCompareData> FbxSkeletonItem = FbxSkeletonTreeItem[SkeletonItem->FbxJointIndex]->ChildJoints[ChildIndex];
bool bFoundChildMatch = false;
for (TSharedPtr<FSkeletonCompareData> DisplayChildJoint: DisplayChilds)
{
if (DisplayChildJoint->JointName == FbxSkeletonItem->JointName)
{
bFoundChildMatch = true;
DisplayChildJoint->bMatchJoint = true;
DisplayChildJoint->FbxJointIndex = FbxSkeletonItem->FbxJointIndex;
break;
}
}
if (!bFoundChildMatch)
{
DisplayChilds.Add(FbxSkeletonItem);
}
}
}
if (!SkeletonItem->bMatchJoint)
{
TSharedPtr<FSkeletonCompareData> ParentSkeletonItem = SkeletonItem->ParentJoint;
while (ParentSkeletonItem.IsValid() && ParentSkeletonItem->bChildConflict == false)
{
ParentSkeletonItem->bChildConflict = true;
ParentSkeletonItem = ParentSkeletonItem->ParentJoint;
}
}
// Set the new child list to the display joint
SkeletonItem->ChildJoints = DisplayChilds;
SkeletonItem->ChildJointIndexes.Empty();
for (TSharedPtr<FSkeletonCompareData> ChildJoint: SkeletonItem->ChildJoints)
{
ChildJoint->ParentJoint = SkeletonItem;
RecursiveMatchJointInfo(ChildJoint);
}
}
void SFbxSkeltonConflictWindow::SetMatchJointInfo()
{
TArray<TSharedPtr<FSkeletonCompareData>> RootJoint;
for (TSharedPtr<FSkeletonCompareData> CurrentSkeletonItem: CurrentSkeletonTreeItem)
{
if (!CurrentSkeletonItem->ParentJoint.IsValid())
{
DisplaySkeletonTreeItem.Add(CurrentSkeletonItem);
}
}
for (TSharedPtr<FSkeletonCompareData> CurrentSkeletonItem: FbxSkeletonTreeItem)
{
if (!CurrentSkeletonItem->ParentJoint.IsValid())
{
bool bInsertJoint = true;
for (TSharedPtr<FSkeletonCompareData> DisplayTreeItem: DisplaySkeletonTreeItem)
{
if (DisplayTreeItem->JointName == CurrentSkeletonItem->JointName)
{
DisplayTreeItem->FbxJointIndex = CurrentSkeletonItem->FbxJointIndex;
DisplayTreeItem->bMatchJoint = true;
bInsertJoint = false;
}
}
if (bInsertJoint)
{
DisplaySkeletonTreeItem.Add(CurrentSkeletonItem);
}
}
}
for (int32 SkeletonTreeIndex = 0; SkeletonTreeIndex < DisplaySkeletonTreeItem.Num(); ++SkeletonTreeIndex)
{
RecursiveMatchJointInfo(DisplaySkeletonTreeItem[SkeletonTreeIndex]);
}
}
#undef LOCTEXT_NAMESPACE