You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Fixed check when blending curves when the required bones of linked anim instances were inconsistent with the main instance This was caused by skeletal meshes streaming out setting the predicted LOD level without flagging required bones for recalculation, which meant that when a linked anim instance was created at a point where a LOD streamed out it could then build required bones at a different LOD to the main instance (e.g. main instance's required bones would be left at LOD 0, but linked instance's would be rebuilt at LOD 1). Added accessors to the PredictedLODLevel and deprecated direct access to the member due to the potential for it to cause havoc if it is abused. and Prevent first-frame rendering issues when spawning LOD stripped skeletal meshes #jira MH-3083 #jira UE-107627 #jira UE-78773 #jira FORT-354970 #jira FORT-345744 #jira FORT-355705 #rb Jurre.deBaare [CL 15676600 by Thomas Sarkanen in ue5-main branch]
498 lines
17 KiB
C++
498 lines
17 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SkeletonTreeBoneItem.h"
|
|
#include "SSkeletonTreeRow.h"
|
|
#include "IPersonaPreviewScene.h"
|
|
#include "IDocumentation.h"
|
|
#include "Animation/BlendProfile.h"
|
|
#include "IEditableSkeleton.h"
|
|
#include "EditorStyleSet.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "BoneDragDropOp.h"
|
|
#include "Animation/DebugSkelMeshComponent.h"
|
|
#include "SocketDragDropOp.h"
|
|
#include "DragAndDrop/AssetDragDropOp.h"
|
|
#include "Widgets/Input/SSpinBox.h"
|
|
#include "Widgets/SToolTip.h"
|
|
#include "Textures/SlateIcon.h"
|
|
#include "Framework/Commands/UIAction.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Rendering/SkeletalMeshRenderData.h"
|
|
#include "UObject/Package.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "FSkeletonTreeBoneItem"
|
|
|
|
FSkeletonTreeBoneItem::FSkeletonTreeBoneItem(const FName& InBoneName, const TSharedRef<class ISkeletonTree>& InSkeletonTree)
|
|
: FSkeletonTreeItem(InSkeletonTree)
|
|
, BoneName(InBoneName)
|
|
, bWeightedBone(false)
|
|
, bRequiredBone(false)
|
|
{
|
|
static const FString BoneProxyPrefix(TEXT("BONEPROXY_"));
|
|
|
|
BoneProxy = NewObject<UBoneProxy>(GetTransientPackage(), *(BoneProxyPrefix + FString::Printf(TEXT("%p"), &InSkeletonTree.Get()) + InBoneName.ToString()));
|
|
BoneProxy->SetFlags(RF_Transactional);
|
|
BoneProxy->BoneName = InBoneName;
|
|
TSharedPtr<IPersonaPreviewScene> PreviewScene = InSkeletonTree->GetPreviewScene();
|
|
if (PreviewScene.IsValid())
|
|
{
|
|
BoneProxy->SkelMeshComponent = PreviewScene->GetPreviewMeshComponent();
|
|
}
|
|
}
|
|
|
|
const FSlateBrush* FSkeletonTreeBoneItem::GetLODIcon() const
|
|
{
|
|
if (!bRequiredBone)
|
|
{
|
|
return FAppStyle::Get().GetBrush("SkeletonTree.NonRequiredBone");
|
|
}
|
|
|
|
else if (!bWeightedBone)
|
|
{
|
|
return FAppStyle::Get().GetBrush("SkeletonTree.BoneNonWeighted");
|
|
}
|
|
|
|
return FAppStyle::Get().GetBrush("SkeletonTree.Bone");
|
|
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::GenerateWidgetForNameColumn( TSharedPtr< SHorizontalBox > Box, const TAttribute<FText>& FilterText, FIsSelected InIsSelected )
|
|
{
|
|
const FSlateBrush* LODIcon = FEditorStyle::GetBrush("SkeletonTree.Bone");
|
|
|
|
Box->AddSlot()
|
|
.AutoWidth()
|
|
.Padding(FMargin(0.0f, 2.0f))
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(SImage)
|
|
.ColorAndOpacity(this, &FSkeletonTreeBoneItem::GetBoneTextColor, InIsSelected)
|
|
.Image(this, &FSkeletonTreeBoneItem::GetLODIcon)
|
|
];
|
|
|
|
if (GetSkeletonTree()->GetPreviewScene().IsValid())
|
|
{
|
|
UDebugSkelMeshComponent* PreviewComponent = GetSkeletonTree()->GetPreviewScene()->GetPreviewMeshComponent();
|
|
CacheLODChange(PreviewComponent);
|
|
}
|
|
|
|
FText ToolTip = GetBoneToolTip();
|
|
Box->AddSlot()
|
|
.AutoWidth()
|
|
.Padding(4, 0, 0, 0)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew( STextBlock )
|
|
.ColorAndOpacity(this, &FSkeletonTreeBoneItem::GetBoneTextColor, InIsSelected)
|
|
.Text( FText::FromName(BoneName) )
|
|
.HighlightText( FilterText )
|
|
.Font(this, &FSkeletonTreeBoneItem::GetBoneTextFont)
|
|
.ToolTipText( ToolTip )
|
|
];
|
|
}
|
|
|
|
TSharedRef< SWidget > FSkeletonTreeBoneItem::GenerateWidgetForDataColumn(const FName& DataColumnName, FIsSelected InIsSelected)
|
|
{
|
|
if(DataColumnName == ISkeletonTree::Columns::Retargeting)
|
|
{
|
|
return
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.HAlign(HAlign_Left)
|
|
.Padding(0.0f)
|
|
[
|
|
SAssignNew(RetargetingComboButton, SComboButton)
|
|
.ComboButtonStyle( &FAppStyle::Get().GetWidgetStyle< FComboButtonStyle >("SkeletonTree.RetargetingComboButton"))
|
|
.ForegroundColor(this, &FSkeletonTreeBoneItem::GetBoneTextColor, InIsSelected)
|
|
.ContentPadding(0)
|
|
.OnGetMenuContent(this, &FSkeletonTreeBoneItem::CreateBoneTranslationRetargetingModeMenu)
|
|
.ToolTip(IDocumentation::Get()->CreateToolTip(
|
|
LOCTEXT("RetargetingToolTip", "Set bone translation retargeting mode"),
|
|
nullptr,
|
|
TEXT("Shared/Editors/Persona"),
|
|
TEXT("TranslationRetargeting")))
|
|
.VAlign(VAlign_Center)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FSkeletonTreeBoneItem::GetTranslationRetargetingModeMenuTitle)
|
|
]
|
|
];
|
|
}
|
|
else if(DataColumnName == ISkeletonTree::Columns::BlendProfile)
|
|
{
|
|
return SNew(SBox)
|
|
.Padding(0.0f)
|
|
.HAlign(HAlign_Fill)
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(SSpinBox<float>)
|
|
.Visibility(this, &FSkeletonTreeBoneItem::GetBoneBlendProfileVisibility)
|
|
.Style(&FAppStyle::Get(), "SkeletonTree.HyperlinkSpinBox")
|
|
.Font(FAppStyle::Get().GetFontStyle("SmallFont"))
|
|
.ContentPadding(0.0f)
|
|
.Delta(0.01f)
|
|
.MinValue(0.0f)
|
|
.MinSliderValue(this, &FSkeletonTreeBoneItem::GetBlendProfileMinSliderValue)
|
|
.MaxSliderValue(this, &FSkeletonTreeBoneItem::GetBlendProfileMaxSliderValue)
|
|
.Value(this, &FSkeletonTreeBoneItem::GetBoneBlendProfileScale)
|
|
.OnValueCommitted(this, &FSkeletonTreeBoneItem::OnBlendSliderCommitted)
|
|
.OnValueChanged(this, &FSkeletonTreeBoneItem::OnBlendSliderCommitted, ETextCommit::OnEnter)
|
|
];
|
|
}
|
|
|
|
return SNullWidget::NullWidget;
|
|
}
|
|
|
|
EVisibility FSkeletonTreeBoneItem::GetBoneBlendProfileVisibility() const
|
|
{
|
|
return GetSkeletonTree()->GetSelectedBlendProfile() ? EVisibility::Visible : EVisibility::Collapsed;
|
|
}
|
|
|
|
float FSkeletonTreeBoneItem::GetBoneBlendProfileScale() const
|
|
{
|
|
if (UBlendProfile* CurrentProfile = GetSkeletonTree()->GetSelectedBlendProfile())
|
|
{
|
|
return CurrentProfile->GetBoneBlendScale(BoneName);
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
TOptional<float> FSkeletonTreeBoneItem::GetBlendProfileMaxSliderValue() const
|
|
{
|
|
if (UBlendProfile* CurrentProfile = GetSkeletonTree()->GetSelectedBlendProfile())
|
|
{
|
|
return (CurrentProfile->GetMode() == EBlendProfileMode::WeightFactor) ? 10.0f : 1.0f;
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
TOptional<float> FSkeletonTreeBoneItem::GetBlendProfileMinSliderValue() const
|
|
{
|
|
if (UBlendProfile* CurrentProfile = GetSkeletonTree()->GetSelectedBlendProfile())
|
|
{
|
|
return (CurrentProfile->GetMode() == EBlendProfileMode::WeightFactor) ? 1.0f : 0.0f;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
FSlateColor FSkeletonTreeBoneItem::GetRetargetingComboButtonForegroundColor() const
|
|
{
|
|
static const FName InvertedForegroundName("InvertedForeground");
|
|
static const FName DefaultForegroundName("DefaultForeground");
|
|
|
|
if (RetargetingComboButton.IsValid())
|
|
{
|
|
return RetargetingComboButton->IsHovered() ? FEditorStyle::GetSlateColor(InvertedForegroundName) : FEditorStyle::GetSlateColor(DefaultForegroundName);
|
|
}
|
|
return FSlateColor::UseForeground();
|
|
}
|
|
|
|
TSharedRef< SWidget > FSkeletonTreeBoneItem::CreateBoneTranslationRetargetingModeMenu()
|
|
{
|
|
FMenuBuilder MenuBuilder(true, nullptr);
|
|
|
|
MenuBuilder.BeginSection("BoneTranslationRetargetingMode", LOCTEXT( "BoneTranslationRetargetingModeMenuHeading", "Bone Translation Retargeting Mode" ) );
|
|
{
|
|
UEnum* const Enum = StaticEnum<EBoneTranslationRetargetingMode::Type>();
|
|
check(Enum);
|
|
|
|
FUIAction ActionRetargetingAnimation = FUIAction(FExecuteAction::CreateSP(this, &FSkeletonTreeBoneItem::SetBoneTranslationRetargetingMode, EBoneTranslationRetargetingMode::Animation));
|
|
MenuBuilder.AddMenuEntry( Enum->GetDisplayNameTextByValue(EBoneTranslationRetargetingMode::Animation), LOCTEXT( "BoneTranslationRetargetingAnimationToolTip", "Use translation from animation." ), FSlateIcon(), ActionRetargetingAnimation);
|
|
|
|
FUIAction ActionRetargetingSkeleton = FUIAction(FExecuteAction::CreateSP(this, &FSkeletonTreeBoneItem::SetBoneTranslationRetargetingMode, EBoneTranslationRetargetingMode::Skeleton));
|
|
MenuBuilder.AddMenuEntry( Enum->GetDisplayNameTextByValue(EBoneTranslationRetargetingMode::Skeleton), LOCTEXT( "BoneTranslationRetargetingSkeletonToolTip", "Use translation from Skeleton." ), FSlateIcon(), ActionRetargetingSkeleton);
|
|
|
|
FUIAction ActionRetargetingLengthScale = FUIAction(FExecuteAction::CreateSP(this, &FSkeletonTreeBoneItem::SetBoneTranslationRetargetingMode, EBoneTranslationRetargetingMode::AnimationScaled));
|
|
MenuBuilder.AddMenuEntry( Enum->GetDisplayNameTextByValue(EBoneTranslationRetargetingMode::AnimationScaled), LOCTEXT( "BoneTranslationRetargetingAnimationScaledToolTip", "Use translation from animation, scale length by Skeleton's proportions." ), FSlateIcon(), ActionRetargetingLengthScale);
|
|
|
|
FUIAction ActionRetargetingAnimationRelative = FUIAction(FExecuteAction::CreateSP(this, &FSkeletonTreeBoneItem::SetBoneTranslationRetargetingMode, EBoneTranslationRetargetingMode::AnimationRelative));
|
|
MenuBuilder.AddMenuEntry( Enum->GetDisplayNameTextByValue(EBoneTranslationRetargetingMode::AnimationRelative), LOCTEXT("BoneTranslationRetargetingAnimationRelativeToolTip", "Use relative translation from animation similar to an additive animation."), FSlateIcon(), ActionRetargetingAnimationRelative);
|
|
|
|
FUIAction ActionRetargetingOrientAndScale = FUIAction(FExecuteAction::CreateSP(this, &FSkeletonTreeBoneItem::SetBoneTranslationRetargetingMode, EBoneTranslationRetargetingMode::OrientAndScale));
|
|
MenuBuilder.AddMenuEntry(Enum->GetDisplayNameTextByValue(EBoneTranslationRetargetingMode::OrientAndScale), LOCTEXT("BoneTranslationRetargetingOrientAndScaleToolTip", "Orient And Scale Translation."), FSlateIcon(), ActionRetargetingOrientAndScale);
|
|
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
FText FSkeletonTreeBoneItem::GetTranslationRetargetingModeMenuTitle() const
|
|
{
|
|
const USkeleton& Skeleton = GetEditableSkeleton()->GetSkeleton();
|
|
|
|
const int32 BoneIndex = Skeleton.GetReferenceSkeleton().FindBoneIndex( BoneName );
|
|
if( BoneIndex != INDEX_NONE )
|
|
{
|
|
const EBoneTranslationRetargetingMode::Type RetargetingMode = Skeleton.GetBoneTranslationRetargetingMode(BoneIndex);
|
|
UEnum* const Enum = StaticEnum<EBoneTranslationRetargetingMode::Type>();
|
|
if (Enum)
|
|
{
|
|
return Enum->GetDisplayNameTextByValue(RetargetingMode);
|
|
}
|
|
}
|
|
|
|
return LOCTEXT("None", "None");
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::SetBoneTranslationRetargetingMode(EBoneTranslationRetargetingMode::Type NewRetargetingMode)
|
|
{
|
|
GetEditableSkeleton()->SetBoneTranslationRetargetingMode(BoneName, NewRetargetingMode);
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::SetBoneBlendProfileScale(float NewScale, bool bRecurse)
|
|
{
|
|
FName BlendProfileName = GetSkeletonTree()->GetSelectedBlendProfile()->GetFName();
|
|
GetEditableSkeleton()->SetBlendProfileScale(BlendProfileName, BoneName, NewScale, bRecurse);
|
|
}
|
|
|
|
FSlateFontInfo FSkeletonTreeBoneItem::GetBoneTextFont() const
|
|
{
|
|
if (!bRequiredBone)
|
|
{
|
|
return FEditorStyle::GetWidgetStyle<FTextBlockStyle>("SkeletonTree.ItalicFont").Font;
|
|
}
|
|
else
|
|
{
|
|
return FEditorStyle::GetWidgetStyle<FTextBlockStyle>("SkeletonTree.NormalFont").Font;
|
|
}
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::CacheLODChange(UDebugSkelMeshComponent* PreviewComponent)
|
|
{
|
|
bWeightedBone = false;
|
|
bRequiredBone = false;
|
|
if (PreviewComponent)
|
|
{
|
|
int32 BoneIndex = PreviewComponent->GetBoneIndex(BoneName);
|
|
|
|
if (BoneIndex != INDEX_NONE)
|
|
{
|
|
if (IsBoneWeighted(BoneIndex, PreviewComponent))
|
|
{
|
|
//Bone is vertex weighted
|
|
bWeightedBone = true;
|
|
}
|
|
if (IsBoneRequired(BoneIndex, PreviewComponent))
|
|
{
|
|
bRequiredBone = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::EnableBoneProxyTick(bool bEnable)
|
|
{
|
|
BoneProxy->bIsTickable = bEnable;
|
|
}
|
|
|
|
FSlateColor FSkeletonTreeBoneItem::GetBoneTextColor(FIsSelected InIsSelected) const
|
|
{
|
|
if (FilterResult == ESkeletonTreeFilterResult::ShownDescendant)
|
|
{
|
|
return FSlateColor(FLinearColor::Gray * 0.5f);
|
|
}
|
|
|
|
bool bIsSelected = InIsSelected.IsBound() ? InIsSelected.Execute() : false;
|
|
if (bIsSelected)
|
|
{
|
|
return FAppStyle::Get().GetSlateColor("Colors.ForegroundInverted");
|
|
}
|
|
else if (bRequiredBone && bWeightedBone)
|
|
{
|
|
return FSlateColor::UseForeground();
|
|
}
|
|
else
|
|
{
|
|
return FSlateColor::UseSubduedForeground();
|
|
}
|
|
}
|
|
|
|
FReply FSkeletonTreeBoneItem::OnDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
if ( MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton ) )
|
|
{
|
|
return FReply::Handled().BeginDragDrop( FBoneDragDropOp::New(GetEditableSkeleton(), BoneName ) );
|
|
}
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
FText FSkeletonTreeBoneItem::GetBoneToolTip()
|
|
{
|
|
bool bIsMeshBone = false;
|
|
bool bIsWeightedBone = false;
|
|
bool bMeshExists = false;
|
|
|
|
FText ToolTip;
|
|
|
|
if (GetSkeletonTree()->GetPreviewScene().IsValid())
|
|
{
|
|
UDebugSkelMeshComponent* PreviewComponent = GetSkeletonTree()->GetPreviewScene()->GetPreviewMeshComponent();
|
|
|
|
if (PreviewComponent)
|
|
{
|
|
bMeshExists = true;
|
|
|
|
int32 BoneIndex = PreviewComponent->GetBoneIndex(BoneName);
|
|
|
|
if (BoneIndex != INDEX_NONE)
|
|
{
|
|
bIsMeshBone = true;
|
|
|
|
bIsWeightedBone = IsBoneWeighted(BoneIndex, PreviewComponent);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bMeshExists )
|
|
{
|
|
ToolTip = LOCTEXT( "BoneToolTipNoMeshAvailable", "This bone exists only on the skeleton as there is no current mesh set" );
|
|
}
|
|
else
|
|
{
|
|
if ( !bIsMeshBone )
|
|
{
|
|
ToolTip = LOCTEXT( "BoneToolTipSkeletonOnly", "This bone exists only on the skeleton, but not on the current mesh" );
|
|
}
|
|
else
|
|
{
|
|
if ( !bIsWeightedBone )
|
|
{
|
|
ToolTip = LOCTEXT( "BoneToolTipSkeletonAndMesh", "This bone is used by the current mesh, but has no vertices weighted against it" );
|
|
}
|
|
else
|
|
{
|
|
ToolTip = LOCTEXT( "BoneToolTipWeighted", "This bone has vertices weighted against it" );
|
|
}
|
|
}
|
|
}
|
|
|
|
return ToolTip;
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::OnBlendSliderCommitted(float NewValue, ETextCommit::Type CommitType)
|
|
{
|
|
SetBoneBlendProfileScale(NewValue, false);
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::HandleDragEnter(const FDragDropEvent& DragDropEvent)
|
|
{
|
|
TSharedPtr<FSocketDragDropOp> DragConnectionOp = DragDropEvent.GetOperationAs<FSocketDragDropOp>();
|
|
|
|
// Is someone dragging a socket onto a bone?
|
|
if (DragConnectionOp.IsValid())
|
|
{
|
|
if (BoneName != DragConnectionOp->GetSocketInfo().Socket->BoneName)
|
|
{
|
|
// The socket can be dropped here if we're a bone and NOT the socket's existing parent
|
|
DragConnectionOp->SetIcon( FEditorStyle::GetBrush( TEXT( "Graph.ConnectorFeedback.Ok" ) ) );
|
|
}
|
|
else if (DragConnectionOp->IsAltDrag())
|
|
{
|
|
// For Alt-Drag, dropping onto the existing parent is fine, as we're going to copy, not move the socket
|
|
DragConnectionOp->SetIcon( FEditorStyle::GetBrush( TEXT( "Graph.ConnectorFeedback.Ok" ) ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::HandleDragLeave(const FDragDropEvent& DragDropEvent)
|
|
{
|
|
TSharedPtr<FSocketDragDropOp> DragConnectionOp = DragDropEvent.GetOperationAs<FSocketDragDropOp>();
|
|
if (DragConnectionOp.IsValid())
|
|
{
|
|
// Reset the drag/drop icon when leaving this row
|
|
DragConnectionOp->SetIcon( FEditorStyle::GetBrush( TEXT( "Graph.ConnectorFeedback.Error" ) ) );
|
|
}
|
|
}
|
|
|
|
FReply FSkeletonTreeBoneItem::HandleDrop(const FDragDropEvent& DragDropEvent)
|
|
{
|
|
TSharedPtr<FSocketDragDropOp> DragConnectionOp = DragDropEvent.GetOperationAs<FSocketDragDropOp>();
|
|
if (DragConnectionOp.IsValid())
|
|
{
|
|
FSelectedSocketInfo SocketInfo = DragConnectionOp->GetSocketInfo();
|
|
|
|
if (DragConnectionOp->IsAltDrag())
|
|
{
|
|
// In an alt-drag, the socket can be dropped on any bone
|
|
// (including its existing parent) to create a uniquely named copy
|
|
GetSkeletonTree()->DuplicateAndSelectSocket( SocketInfo, BoneName);
|
|
}
|
|
else if (BoneName != SocketInfo.Socket->BoneName)
|
|
{
|
|
// The socket can be dropped here if we're a bone and NOT the socket's existing parent
|
|
USkeletalMesh* SkeletalMesh = GetSkeletonTree()->GetPreviewScene().IsValid() ? ToRawPtr(GetSkeletonTree()->GetPreviewScene()->GetPreviewMeshComponent()->SkeletalMesh) : nullptr;
|
|
GetEditableSkeleton()->SetSocketParent(SocketInfo.Socket->SocketName, BoneName, SkeletalMesh);
|
|
|
|
return FReply::Handled();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TSharedPtr<FAssetDragDropOp> DragDropOp = DragDropEvent.GetOperationAs<FAssetDragDropOp>();
|
|
if (DragDropOp.IsValid())
|
|
{
|
|
//Do we have some assets to attach?
|
|
if (DragDropOp->HasAssets())
|
|
{
|
|
GetSkeletonTree()->AttachAssets(SharedThis(this), DragDropOp->GetAssets());
|
|
}
|
|
return FReply::Handled();
|
|
}
|
|
}
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
bool FSkeletonTreeBoneItem::IsBoneWeighted(int32 MeshBoneIndex, UDebugSkelMeshComponent* PreviewComponent)
|
|
{
|
|
// MeshBoneIndex must be an index into the mesh's skeleton, *not* the source skeleton!!!
|
|
|
|
if (!PreviewComponent || !PreviewComponent->SkeletalMesh || !PreviewComponent->SkeletalMesh->GetResourceForRendering() || !PreviewComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData.Num())
|
|
{
|
|
// If there's no mesh, then this bone can't possibly be weighted!
|
|
return false;
|
|
}
|
|
|
|
//Get current LOD
|
|
const int32 LODIndex = FMath::Clamp(PreviewComponent->GetPredictedLODLevel(), 0, PreviewComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData.Num() - 1);
|
|
FSkeletalMeshLODRenderData& LODData = PreviewComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData[LODIndex];
|
|
|
|
//Check whether the bone is vertex weighted
|
|
int32 Index = LODData.ActiveBoneIndices.Find(MeshBoneIndex);
|
|
|
|
return Index != INDEX_NONE;
|
|
}
|
|
|
|
bool FSkeletonTreeBoneItem::IsBoneRequired(int32 MeshBoneIndex, UDebugSkelMeshComponent* PreviewComponent)
|
|
{
|
|
// MeshBoneIndex must be an index into the mesh's skeleton, *not* the source skeleton!!!
|
|
|
|
if (!PreviewComponent || !PreviewComponent->SkeletalMesh || !PreviewComponent->SkeletalMesh->GetResourceForRendering() || !PreviewComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData.Num())
|
|
{
|
|
// If there's no mesh, then this bone can't possibly be weighted!
|
|
return false;
|
|
}
|
|
|
|
//Get current LOD
|
|
const int32 LODIndex = FMath::Clamp(PreviewComponent->GetPredictedLODLevel(), 0, PreviewComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData.Num() - 1);
|
|
FSkeletalMeshLODRenderData& LODData = PreviewComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData[LODIndex];
|
|
|
|
//Check whether the bone is vertex weighted
|
|
int32 Index = LODData.RequiredBones.Find(MeshBoneIndex);
|
|
|
|
return Index != INDEX_NONE;
|
|
}
|
|
|
|
void FSkeletonTreeBoneItem::AddReferencedObjects( FReferenceCollector& Collector )
|
|
{
|
|
Collector.AddReferencedObject(BoneProxy);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |