Up to Unreal 5.7

This commit is contained in:
Simeon "Waldo" Wallrath 2025-11-20 16:26:22 +01:00
parent 8a63902c06
commit 1a64805d43
239 changed files with 3947 additions and 296 deletions

View file

@ -0,0 +1,849 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HandSocketComponentDetails.h"
#include "HandSocketVisualizer.h"
//#include "PropertyEditing.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
#include "PropertyHandle.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "DetailCategoryBuilder.h"
#include "IDetailsView.h"
#include "Developer/AssetTools/Public/IAssetTools.h"
#include "Developer/AssetTools/Public/AssetToolsModule.h"
#include "Editor/ContentBrowser/Public/IContentBrowserSingleton.h"
#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
#include "AnimationUtils.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "UObject/SavePackage.h"
#include "Misc/MessageDialog.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Layout/SUniformGridPanel.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Editor.h"
#include "EditorStyleSet.h"
#include "Styling/CoreStyle.h"
#include "Animation/AnimData/AnimDataModel.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#define LOCTEXT_NAMESPACE "HandSocketComponentDetails"
FText SCreateHandAnimationDlg::LastUsedAssetPath;
static bool PromptUserForAssetPath(FString& AssetPath, FString& AssetName)
{
TSharedRef<SCreateHandAnimationDlg> NewAnimDlg = SNew(SCreateHandAnimationDlg);
if (NewAnimDlg->ShowModal() != EAppReturnType::Cancel)
{
AssetPath = NewAnimDlg->GetFullAssetPath();
AssetName = NewAnimDlg->GetAssetName();
return true;
}
return false;
}
TWeakObjectPtr<UAnimSequence> FHandSocketComponentDetails::SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName)
{
TWeakObjectPtr<UAnimSequence> FinalAnimation;
// Replace when this moves to custom display
if (!HandSocketComponent.IsValid())
return FinalAnimation;
/*if (!HandSocketComponent->HandVisualizerComponent)// || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh->Skeleton)
{
return false;
}*/
if (!HandSocketComponent->HandTargetAnimation && (!HandSocketComponent->VisualizationMesh || !HandSocketComponent->VisualizationMesh->GetSkeleton()))
{
return FinalAnimation;
}
// create the asset
FText InvalidPathReason;
bool const bValidPackageName = FPackageName::IsValidLongPackageName(InAssetPath, false, &InvalidPathReason);
if (bValidPackageName == false)
{
UE_LOG(LogAnimation, Log, TEXT("%s is an invalid asset path, prompting user for new asset path. Reason: %s"), *InAssetPath, *InvalidPathReason.ToString());
}
FString ValidatedAssetPath = InAssetPath;
FString ValidatedAssetName = InAssetName;
UObject* Parent = bValidPackageName ? CreatePackage(*ValidatedAssetPath) : nullptr;
if (Parent == nullptr)
{
// bad or no path passed in, do the popup
if (PromptUserForAssetPath(ValidatedAssetPath, ValidatedAssetName) == false)
{
return FinalAnimation;
}
Parent = CreatePackage(*ValidatedAssetPath);
}
UObject* const Object = LoadObject<UObject>(Parent, *ValidatedAssetName, nullptr, LOAD_Quiet, nullptr);
// if object with same name exists, warn user
if (Object)
{
EAppReturnType::Type ReturnValue = FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT("Error_AssetExist", "Asset with same name exists. Do you wish to overwrite it?"));
if (ReturnValue == EAppReturnType::No)
{
return FinalAnimation; // failed
}
}
UAnimSequence* BaseAnimation = HandSocketComponent->HandTargetAnimation;
TArray<FTransform> LocalPoses;
if (!BaseAnimation)
{
LocalPoses = HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetRefBonePose();
}
// If not, create new one now.
UAnimSequence* const NewSeq = NewObject<UAnimSequence>(Parent, *ValidatedAssetName, RF_Public | RF_Standalone);
if (NewSeq)
{
// set skeleton
if (BaseAnimation)
{
NewSeq->SetSkeleton(BaseAnimation->GetSkeleton());
}
else
{
NewSeq->SetSkeleton(HandSocketComponent->VisualizationMesh->GetSkeleton());
}
// Notify the asset registry
FAssetRegistryModule::AssetCreated(NewSeq);
//StartRecord(Component, NewSeq);
//return true;
UAnimSequence* AnimationObject = NewSeq;
IAnimationDataController& AnimController = AnimationObject->GetController();
{
IAnimationDataController::FScopedBracket ScopedBracket(AnimController, LOCTEXT("SaveAnimationAsset_VRE", "Creating Animation Sequence based on hand pose"));
AnimationObject->ResetAnimation();
if (BaseAnimation)
{
AnimationObject->BoneCompressionSettings = BaseAnimation->BoneCompressionSettings;
}
else
{
AnimationObject->BoneCompressionSettings = FAnimationUtils::GetDefaultAnimationBoneCompressionSettings();
}
AnimController.InitializeModel();
AnimController.RemoveAllBoneTracks(false);
//checkf(MovieScene, TEXT("No Movie Scene found for SequencerDataModel"));
//AnimController.SetPlayLength(4.f);
AnimController.SetNumberOfFrames(FFrameNumber(1), false);
// Set frame rate to 1 to 1 as we don't animate
AnimController.SetFrameRate(FFrameRate(1, 1));
TArray<FName> TrackNames;
const IAnimationDataModel* BaseDataModel = BaseAnimation ? BaseAnimation->GetController().GetModel() : nullptr;
if (BaseAnimation)
{
if (BaseDataModel)
{
BaseDataModel->GetBoneTrackNames(TrackNames);
for (FName TrackName : TrackNames)
{
AnimController.AddBoneCurve(TrackName);
}
}
else
{
return FinalAnimation;
}
}
else
{
int numBones = HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetNum();
for (int i = 0; i < LocalPoses.Num() && i < numBones; ++i)
{
AnimController.AddBoneCurve(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
//AnimController.AddBoneTrack(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
}
}
if (BaseAnimation)
{
AnimationObject->RetargetSource = BaseAnimation->RetargetSource;
}
else
{
AnimationObject->RetargetSource = HandSocketComponent->VisualizationMesh ? HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRetargetSourceForMesh(HandSocketComponent->VisualizationMesh) : NAME_None;
}
const IAnimationDataModel* DataModel = AnimController.GetModel();
/// SAVE POSE
if (BaseAnimation && DataModel && BaseDataModel)
{
for (int32 TrackIndex = 0; TrackIndex < /*DataModel->GetBoneAnimationTracks().Num()*/BaseDataModel->GetNumBoneTracks(); ++TrackIndex)
{
FName TrackName = TrackIndex < TrackNames.Num() ? TrackNames[TrackIndex] : NAME_None;
if (!BaseDataModel->IsValidBoneTrackName(TrackName))
{
continue;
}
FTransform FinalTrans = BaseDataModel->GetBoneTrackTransform(TrackName, 0);
//FTransform FinalTrans(Rot, Loc, Scale);
FQuat DeltaQuat = FQuat::Identity;
for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
{
if (HandPair.BoneName == TrackName)
{
DeltaQuat = HandPair.DeltaPose;
break;
}
}
FinalTrans.ConcatenateRotation(DeltaQuat);
FinalTrans.NormalizeRotation();
//FRawAnimSequenceTrack& RawNewTrack = DataModel->GetBoneTrackByIndex(TrackIndex).InternalTrackData;
AnimController.SetBoneTrackKeys(TrackName, { FinalTrans.GetTranslation() }, { FinalTrans.GetRotation() }, { FinalTrans.GetScale3D() });
}
}
else if(DataModel)
{
USkeletalMesh* SkeletalMesh = HandSocketComponent->VisualizationMesh;
FReferenceSkeleton RefSkeleton = SkeletalMesh->GetRefSkeleton();
USkeleton* AnimSkeleton = SkeletalMesh->GetSkeleton();
for (int32 TrackIndex = 0; TrackIndex < RefSkeleton.GetNum(); ++TrackIndex)
{
FName TrackName = RefSkeleton.GetBoneName(TrackIndex);
if (!DataModel->IsValidBoneTrackName(TrackName))
{
continue;
}
int32 BoneTreeIndex = RefSkeleton.FindBoneIndex(TrackName);
// verify if this bone exists in skeleton
//int32 BoneTreeIndex = DataModel->GetBoneTrackByIndex(TrackIndex).BoneTreeIndex;
if (BoneTreeIndex != INDEX_NONE)
{
int32 BoneIndex = BoneTreeIndex;//AnimSkeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkeletalMesh, BoneTreeIndex);
//int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
FTransform LocalTransform = LocalPoses[BoneIndex];
//FTransform LocalTransform = RefSkeleton.GetRefBonePose()[BoneIndex];
FName BoneName = AnimSkeleton->GetReferenceSkeleton().GetBoneName(BoneIndex);
FQuat DeltaQuat = FQuat::Identity;
for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
{
if (HandPair.BoneName == BoneName)
{
DeltaQuat = HandPair.DeltaPose;
}
}
LocalTransform.ConcatenateRotation(DeltaQuat);
LocalTransform.NormalizeRotation();
AnimController.SetBoneTrackKeys(BoneName, { LocalTransform.GetTranslation() }, { LocalTransform.GetRotation() }, { LocalTransform.GetScale3D() });
}
}
}
AnimController.NotifyPopulated();
}
/// END SAVE POSE
///
///
///
// init notifies
AnimationObject->InitializeNotifyTrack();
//#TODO: 5.1, need to figure out what they replaced this with
//PRAGMA_DISABLE_DEPRECATION_WARNINGS
//AnimationObject->PostProcessSequence();
//PRAGMA_ENABLE_DEPRECATION_WARNINGS
AnimationObject->MarkPackageDirty();
//if (bAutoSaveAsset)
{
UPackage* const Package = AnimationObject->GetOutermost();
FString const PackageName = Package->GetName();
FString const PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
double StartTime = FPlatformTime::Seconds();
FSavePackageArgs PackageArguments;
PackageArguments.SaveFlags = RF_Standalone;
PackageArguments.SaveFlags = SAVE_NoError;
UPackage::SavePackage(Package, NULL, *PackageFileName, PackageArguments);
//UPackage::SavePackage(Package, NULL, RF_Standalone, *PackageFileName, GError, nullptr, false, true, SAVE_NoError);
double ElapsedTime = FPlatformTime::Seconds() - StartTime;
UE_LOG(LogAnimation, Log, TEXT("Animation Recorder saved %s in %0.2f seconds"), *PackageName, ElapsedTime);
}
FinalAnimation = AnimationObject;
return FinalAnimation;
}
return FinalAnimation;
}
TSharedRef< IDetailCustomization > FHandSocketComponentDetails::MakeInstance()
{
return MakeShareable(new FHandSocketComponentDetails);
}
void FHandSocketComponentDetails::OnHandRelativeUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
}
}
FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
void FHandSocketComponentDetails::OnLeftDominantUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
// Default to always flipping this
//if (HandSocketComponent->bFlipForLeftHand)
{
FTransform relTrans = HandSocketComponent->GetRelativeTransform();
FTransform HandPlacement = HandSocketComponent->GetHandRelativePlacement();
if (HandSocketComponent->bDecoupleMeshPlacement)
{
relTrans = FTransform::Identity;
}
FTransform ReturnTrans = (HandPlacement * relTrans);
HandSocketComponent->MirrorHandTransform(ReturnTrans, relTrans);
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
ReturnTrans = ReturnTrans.GetRelativeTransform(relTrans);
HandSocketComponent->HandRelativePlacement = ReturnTrans;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
void FHandSocketComponentDetails::OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
{
return;
}
if (HandSocketComponent->bDecoupleMeshPlacement)
{
//FTransform RelTrans = HandSocketComponent->GetRelativeTransform();
//FTransform WorldTrans = HandSocketComponent->GetComponentTransform();
//if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement * HandSocketComponent->GetRelativeTransform();// HandSocketComponent->GetComponentTransform();
HandSocketComponent->bDecoupled = true;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
RefHand->bDecoupled = true;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
}
}
else
{
//if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement.GetRelativeTransform(HandSocketComponent->GetRelativeTransform());
HandSocketComponent->bDecoupled = false;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
RefHand->bDecoupled = false;
//FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
}
}
}
}
TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupled)));
FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
}
void FHandSocketComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
// Hide the SplineCurves property
//TSharedPtr<IPropertyHandle> HandPlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
//HandPlacementProperty->MarkHiddenByCustomization();
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
if (ObjectsBeingCustomized.Num() == 1)
{
UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(ObjectsBeingCustomized[0]);
if (CurrentHandSocket != NULL)
{
if (HandSocketComponent != CurrentHandSocket)
{
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
//HandVisualizer->OldHandSocketComp = CurrentHandSocket;
}
HandSocketComponent = CurrentHandSocket;
}
}
}
/*const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetSelectedObjects();
for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex)
{
const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex];
if (CurrentObject.IsValid())
{
UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(CurrentObject.Get());
if (CurrentHandSocket != NULL)
{
if (HandSocketComponent != CurrentHandSocket)
{
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
//HandVisualizer->OldHandSocketComp = CurrentHandSocket;
}
HandSocketComponent = CurrentHandSocket;
}
break;
}
}
}*/
DetailBuilder.HideCategory(FName("ComponentTick"));
DetailBuilder.HideCategory(FName("GameplayTags"));
DetailBuilder.HideCategory(FName("VRGripInterface"));
DetailBuilder.HideCategory(FName("VRGripInterface|Replication"));
DetailBuilder.HideCategory(FName("Tags"));
DetailBuilder.HideCategory(FName("AssetUserData"));
DetailBuilder.HideCategory(FName("Events"));
DetailBuilder.HideCategory(FName("Activation"));
DetailBuilder.HideCategory(FName("Cooking"));
DetailBuilder.HideCategory(FName("ComponentReplication"));
TSharedPtr<IPropertyHandle> LockedLocationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupleMeshPlacement));
TSharedPtr<IPropertyHandle> HandRelativePlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
TSharedPtr<IPropertyHandle> LeftHandDominateProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bLeftHandDominant));
FSimpleDelegate OnHandRelativeChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnHandRelativeUpdated, &DetailBuilder);
HandRelativePlacementProperty->SetOnPropertyValueChanged(OnHandRelativeChangedDelegate);
FSimpleDelegate OnLockedStateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLockedStateUpdated, &DetailBuilder);
LockedLocationProperty->SetOnPropertyValueChanged(OnLockedStateChangedDelegate);
FSimpleDelegate OnLeftDominateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLeftDominantUpdated, &DetailBuilder);
LeftHandDominateProperty->SetOnPropertyValueChanged(OnLeftDominateChangedDelegate);
TSharedPtr<IPropertyHandle> ShowVisualizationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bShowVisualizationMesh));
FSimpleDelegate OnShowVisChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnUpdateShowMesh, &DetailBuilder);
ShowVisualizationProperty->SetOnPropertyValueChanged(OnShowVisChangedDelegate);
DetailBuilder.EditCategory("Hand Animation")
.AddCustomRow(LOCTEXT("UpdateHandSocketRow", "Save Current Pose"))
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("UpdateHandSocketText", "Save Current Pose"))
]
.ValueContent()
.MaxDesiredWidth(125.f)
.MinDesiredWidth(125.f)
[
SNew(SButton)
.ContentPadding(2)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.OnClicked(this, &FHandSocketComponentDetails::OnUpdateSavePose)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("UpdateHandSocketButton", "Save"))
]
];
}
void FHandSocketComponentDetails::OnUpdateShowMesh(IDetailLayoutBuilder* LayoutBuilder)
{
if (!HandSocketComponent.IsValid())
return;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
HandVisualizer->CurrentlySelectedBone = NAME_None;
HandVisualizer->HandPropertyPath = FComponentPropertyPath();
}
}
FReply FHandSocketComponentDetails::OnUpdateSavePose()
{
if (HandSocketComponent.IsValid() && HandSocketComponent->CustomPoseDeltas.Num() > 0)
{
if (HandSocketComponent->HandTargetAnimation || HandSocketComponent->VisualizationMesh)
{
// Save Animation Pose here
FString AssetPath;
FString AssetName;
PromptUserForAssetPath(AssetPath, AssetName);
TWeakObjectPtr<UAnimSequence> NewAnim = SaveAnimationAsset(AssetPath, AssetName);
// Finally remove the deltas
if (NewAnim.IsValid())
{
HandSocketComponent->Modify();
if (AActor* Owner = HandSocketComponent->GetOwner())
{
Owner->Modify();
}
HandSocketComponent->HandTargetAnimation = NewAnim.Get();
HandSocketComponent->CustomPoseDeltas.Empty();
HandSocketComponent->bUseCustomPoseDeltas = false;
TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
if (HandVisualizer)
{
if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
{
RefHand->HandTargetAnimation = NewAnim.Get();
RefHand->CustomPoseDeltas.Empty();
RefHand->bUseCustomPoseDeltas = false;
/*TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
FComponentVisualizer::NotifyPropertiesModified(RefHand, PropertiesToModify);*/
}
}
// Modify all of the properties at once
TArray<FProperty*> PropertiesToModify;
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
}
}
}
return FReply::Handled();
}
void SCreateHandAnimationDlg::Construct(const FArguments& InArgs)
{
AssetPath = FText::FromString(FPackageName::GetLongPackagePath(InArgs._DefaultAssetPath.ToString()));
AssetName = FText::FromString(FPackageName::GetLongPackageAssetName(InArgs._DefaultAssetPath.ToString()));
if (AssetPath.IsEmpty())
{
AssetPath = LastUsedAssetPath;
// still empty?
if (AssetPath.IsEmpty())
{
AssetPath = FText::FromString(TEXT("/Game"));
}
}
else
{
LastUsedAssetPath = AssetPath;
}
if (AssetName.IsEmpty())
{
// find default name for them
FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
FString OutPackageName, OutAssetName;
FString PackageName = AssetPath.ToString() + TEXT("/NewAnimation");
AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), OutPackageName, OutAssetName);
AssetName = FText::FromString(OutAssetName);
}
FPathPickerConfig PathPickerConfig;
PathPickerConfig.DefaultPath = AssetPath.ToString();
PathPickerConfig.OnPathSelected = FOnPathSelected::CreateSP(this, &SCreateHandAnimationDlg::OnPathChange);
PathPickerConfig.bAddDefaultPath = true;
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
SWindow::Construct(SWindow::FArguments()
.Title(LOCTEXT("SCreateHandAnimationDlg_Title", "Create New Animation Object"))
.SupportsMinimize(false)
.SupportsMaximize(false)
//.SizingRule( ESizingRule::Autosized )
.ClientSize(FVector2D(450, 450))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot() // Add user input block
.Padding(2)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(STextBlock)
.Text(LOCTEXT("SelectPath", "Select Path to create animation"))
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 14))
]
+ SVerticalBox::Slot()
.FillHeight(1)
.Padding(3)
[
ContentBrowserModule.Get().CreatePathPicker(PathPickerConfig)
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SSeparator)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(3)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 10, 0)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("AnimationName", "Animation Name"))
]
+ SHorizontalBox::Slot()
[
SNew(SEditableTextBox)
.Text(AssetName)
.OnTextCommitted(this, &SCreateHandAnimationDlg::OnNameChange)
.MinDesiredWidth(250)
]
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(5)
[
SNew(SUniformGridPanel)
.SlotPadding(FAppStyle::GetMargin("StandardDialog.SlotPadding"))
.MinDesiredSlotWidth(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
.MinDesiredSlotHeight(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotHeight"))
+ SUniformGridPanel::Slot(0, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding"))
.Text(LOCTEXT("OK", "OK"))
.OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Ok)
]
+ SUniformGridPanel::Slot(1, 0)
[
SNew(SButton)
.HAlign(HAlign_Center)
.ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding"))
.Text(LOCTEXT("Cancel", "Cancel"))
.OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Cancel)
]
]
]);
}
void SCreateHandAnimationDlg::OnNameChange(const FText& NewName, ETextCommit::Type CommitInfo)
{
AssetName = NewName;
}
void SCreateHandAnimationDlg::OnPathChange(const FString& NewPath)
{
AssetPath = FText::FromString(NewPath);
LastUsedAssetPath = AssetPath;
}
FReply SCreateHandAnimationDlg::OnButtonClick(EAppReturnType::Type ButtonID)
{
UserResponse = ButtonID;
if (ButtonID != EAppReturnType::Cancel)
{
if (!ValidatePackage())
{
// reject the request
return FReply::Handled();
}
}
RequestDestroyWindow();
return FReply::Handled();
}
/** Ensures supplied package name information is valid */
bool SCreateHandAnimationDlg::ValidatePackage()
{
FText Reason;
FString FullPath = GetFullAssetPath();
if (!FPackageName::IsValidLongPackageName(FullPath, false, &Reason)
|| !FName(*AssetName.ToString()).IsValidObjectName(Reason))
{
FMessageDialog::Open(EAppMsgType::Ok, Reason);
return false;
}
return true;
}
EAppReturnType::Type SCreateHandAnimationDlg::ShowModal()
{
GEditor->EditorAddModalWindow(SharedThis(this));
return UserResponse;
}
FString SCreateHandAnimationDlg::GetAssetPath()
{
return AssetPath.ToString();
}
FString SCreateHandAnimationDlg::GetAssetName()
{
return AssetName.ToString();
}
FString SCreateHandAnimationDlg::GetFullAssetPath()
{
return AssetPath.ToString() + "/" + AssetName.ToString();
}
#undef LOCTEXT_NAMESPACE

View file

@ -0,0 +1,484 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "HandSocketVisualizer.h"
#include "CanvasItem.h"
#include "CanvasTypes.h"
#include "SceneManagement.h"
//#include "UObject/Field.h"
#include "VRBPDatatypes.h"
#include "ScopedTransaction.h"
#include "Modules/ModuleManager.h"
#include "EditorViewportClient.h"
#include "Components/PoseableMeshComponent.h"
#include "Misc/PackageName.h"
//#include "Persona.h"
IMPLEMENT_HIT_PROXY(HHandSocketVisProxy, HComponentVisProxy);
#define LOCTEXT_NAMESPACE "HandSocketVisualizer"
bool FHandSocketVisualizer::VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click)
{
bool bEditing = false;
if (VisProxy && VisProxy->Component.IsValid())
{
bEditing = true;
if (VisProxy->IsA(HHandSocketVisProxy::StaticGetType()))
{
if( const UHandSocketComponent * HandComp = UpdateSelectedHandComponent(VisProxy))
{
HHandSocketVisProxy* Proxy = (HHandSocketVisProxy*)VisProxy;
if (Proxy)
{
CurrentlySelectedBone = Proxy->TargetBoneName;
CurrentlySelectedBoneIdx = Proxy->BoneIdx;
TargetViewport = InViewportClient->Viewport;
}
}
}
}
return bEditing;
}
bool FHandSocketVisualizer::GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const
{
if (TargetViewport == nullptr || TargetViewport != ViewportClient->Viewport)
{
return false;
}
if (HandPropertyPath.IsValid() && CurrentlySelectedBone != NAME_None/* && CurrentlySelectedBone != "HandSocket"*/)
{
if (CurrentlySelectedBone == "HandSocket")
{
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (CurrentlyEditingComponent)
{
if (CurrentlyEditingComponent->bMirrorVisualizationMesh)
{
FTransform NewTrans = CurrentlyEditingComponent->GetRelativeTransform();
NewTrans.Mirror(CurrentlyEditingComponent->GetAsEAxis(CurrentlyEditingComponent->MirrorAxis), CurrentlyEditingComponent->GetAsEAxis(CurrentlyEditingComponent->FlipAxis));
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
NewTrans = NewTrans * ParentComp->GetComponentTransform();
}
OutMatrix = FRotationMatrix::Make(NewTrans.GetRotation());
}
}
return false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
FTransform newTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
newTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
newTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
OutMatrix = FRotationMatrix::Make(newTrans.GetRotation());
}
}
else
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
if (IsValid(CurrentlyEditingComponent->HandVisualizerComponent))
{
FTransform newTrans = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx);
FQuat Rot = newTrans.GetRotation();
if (!newTrans.GetRotation().ContainsNaN())
{
Rot.Normalize();
OutMatrix = FRotationMatrix::Make(Rot);
return true;
}
}
return false;
}
}
return true;
}
return false;
}
bool FHandSocketVisualizer::IsVisualizingArchetype() const
{
return (HandPropertyPath.IsValid() && HandPropertyPath.GetParentOwningActor() && FActorEditorUtils::IsAPreviewOrInactiveActor(HandPropertyPath.GetParentOwningActor()));
}
void FHandSocketVisualizer::DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas)
{
if (TargetViewport == nullptr || TargetViewport != Viewport)
{
return;
}
if (const UHandSocketComponent* HandComp = Cast<const UHandSocketComponent>(Component))
{
if (CurrentlySelectedBone != NAME_None)
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
if (!IsValid(CurrentlyEditingComponent->HandVisualizerComponent))
{
return;
}
int32 XL;
int32 YL;
const FIntRect CanvasRect = Canvas->GetViewRect();
FPlane location = View->Project(CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx).GetLocation());
StringSize(GEngine->GetLargeFont(), XL, YL, *CurrentlySelectedBone.ToString());
//const float DrawPositionX = location.X - XL;
//const float DrawPositionY = location.Y - YL;
const float DrawPositionX = FMath::FloorToFloat(CanvasRect.Min.X + (CanvasRect.Width() - XL) * 0.5f);
const float DrawPositionY = CanvasRect.Min.Y + 50.0f;
Canvas->DrawShadowedString(DrawPositionX, DrawPositionY, *CurrentlySelectedBone.ToString(), GEngine->GetLargeFont(), FLinearColor::Yellow);
}
}
}
}
void FHandSocketVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI)
{
//UWorld* World = Component->GetWorld();
//return World && (World->WorldType == EWorldType::EditorPreview || World->WorldType == EWorldType::Inactive);
//cast the component into the expected component type
if (const UHandSocketComponent* HandComponent = Cast<UHandSocketComponent>(Component))
{
if (!HandComponent->HandVisualizerComponent)
return;
//This is an editor only uproperty of our targeting component, that way we can change the colors if we can't see them against the background
const FLinearColor SelectedColor = FLinearColor::Yellow;//TargetingComponent->EditorSelectedColor;
const FLinearColor UnselectedColor = FLinearColor::White;//TargetingComponent->EditorUnselectedColor;
const FVector Location = HandComponent->HandVisualizerComponent->GetComponentLocation();
float BoneScale = 1.0f - ((View->ViewLocation - Location).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.2f, 1.0f);
HHandSocketVisProxy* newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = "Visualizer";
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(Location, CurrentlySelectedBone == newHitProxy->TargetBoneName ? SelectedColor : FLinearColor::Red, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = "HandSocket";
BoneScale = 1.0f - ((View->ViewLocation - HandComponent->GetComponentLocation()).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.2f, 1.0f);
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(HandComponent->GetComponentLocation(), FLinearColor::Green, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
if (HandComponent->bUseCustomPoseDeltas)
{
TArray<FTransform> BoneTransforms = HandComponent->HandVisualizerComponent->GetBoneSpaceTransforms();
FTransform ParentTrans = HandComponent->HandVisualizerComponent->GetComponentTransform();
// We skip root bone, moving the visualizer itself handles that
for (int i = 1; i < HandComponent->HandVisualizerComponent->GetNumBones(); i++)
{
FName BoneName = HandComponent->HandVisualizerComponent->GetBoneName(i);
if (HandComponent->bFilterBonesByPostfix)
{
if (BoneName.ToString().Right(2) != HandComponent->FilterPostfix)
{
// Skip visualizing this bone its the incorrect side
continue;
}
}
if (HandComponent->BonesToSkip.Contains(BoneName))
{
// Skip visualizing this bone as its in the ignore array
continue;
}
FTransform BoneTransform = HandComponent->HandVisualizerComponent->GetBoneTransform(i);
FVector BoneLoc = BoneTransform.GetLocation();
BoneScale = 1.0f - ((View->ViewLocation - BoneLoc).SizeSquared() / FMath::Square(100.0f));
BoneScale = FMath::Clamp(BoneScale, 0.1f, 0.9f);
newHitProxy = new HHandSocketVisProxy(Component);
newHitProxy->TargetBoneName = BoneName;
newHitProxy->BoneIdx = i;
PDI->SetHitProxy(newHitProxy);
PDI->DrawPoint(BoneLoc, CurrentlySelectedBone == newHitProxy->TargetBoneName ? SelectedColor : UnselectedColor, 20.f * BoneScale, SDPG_Foreground);
PDI->SetHitProxy(NULL);
newHitProxy = nullptr;
}
}
if (HandComponent->bShowRangeVisualization)
{
float RangeVisualization = HandComponent->OverrideDistance;
if (RangeVisualization <= 0.0f)
{
if (USceneComponent* Parent = Cast<USceneComponent>(HandComponent->GetAttachParent()))
{
FStructProperty* ObjectProperty = CastField<FStructProperty>(Parent->GetClass()->FindPropertyByName("VRGripInterfaceSettings"));
AActor* ParentsActor = nullptr;
if (!ObjectProperty)
{
ParentsActor = Parent->GetOwner();
if (ParentsActor)
{
ObjectProperty = CastField<FStructProperty>(Parent->GetOwner()->GetClass()->FindPropertyByName("VRGripInterfaceSettings"));
}
}
if (ObjectProperty)
{
UObject* Target = ParentsActor;
if (Target == nullptr)
{
Target = Parent;
}
if (const FBPInterfaceProperties* Curve = ObjectProperty->ContainerPtrToValuePtr<FBPInterfaceProperties>(Target))
{
if (HandComponent->SlotPrefix == "VRGripS")
{
RangeVisualization = Curve->SecondarySlotRange;
}
else
{
RangeVisualization = Curve->PrimarySlotRange;
}
}
}
}
}
// Scale into our parents space as that is actually what the range is based on
FBox BoxToDraw = FBox::BuildAABB(FVector::ZeroVector, FVector(RangeVisualization) * HandComponent->GetAttachParent()->GetComponentScale());
BoxToDraw.Min += HandComponent->GetComponentLocation();
BoxToDraw.Max += HandComponent->GetComponentLocation();
DrawWireBox(PDI, BoxToDraw, FColor::Green, 0.0f);
}
}
}
bool FHandSocketVisualizer::GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const
{
if (TargetViewport == nullptr || TargetViewport != ViewportClient->Viewport)
{
return false;
}
if (HandPropertyPath.IsValid() && CurrentlySelectedBone != NAME_None && CurrentlySelectedBone != "HandSocket")
{
if (CurrentlySelectedBone == "HandSocket")
{
return false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
FTransform newTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
newTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
newTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
OutLocation = newTrans.GetLocation();
}
}
else
{
if (UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent())
{
if (IsValid(CurrentlyEditingComponent->HandVisualizerComponent))
{
OutLocation = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx).GetLocation();
}
else
{
return false;
}
}
}
return true;
}
return false;
}
bool FHandSocketVisualizer::HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltaRotate, FVector& DeltaScale)
{
if (TargetViewport == nullptr || TargetViewport != Viewport)
{
return false;
}
bool bHandled = false;
if (HandPropertyPath.IsValid())
{
if (CurrentlySelectedBone == "HandSocket" || CurrentlySelectedBone == NAME_None)
{
bHandled = false;
}
else if (CurrentlySelectedBone == "Visualizer")
{
const FScopedTransaction Transaction(LOCTEXT("ChangingComp", "ChangingComp"));
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (!CurrentlyEditingComponent)
{
return false;
}
CurrentlyEditingComponent->Modify();
if (AActor* Owner = CurrentlyEditingComponent->GetOwner())
{
Owner->Modify();
}
bool bLevelEdit = ViewportClient->IsLevelEditorClient();
FTransform CurrentTrans = FTransform::Identity;
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
CurrentTrans = CurrentlyEditingComponent->HandRelativePlacement * ParentComp->GetComponentTransform();
}
}
else
{
CurrentTrans = CurrentlyEditingComponent->GetHandRelativePlacement() * CurrentlyEditingComponent->GetComponentTransform();
}
if (!DeltaTranslate.IsNearlyZero())
{
CurrentTrans.AddToTranslation(DeltaTranslate);
}
if (!DeltaRotate.IsNearlyZero())
{
CurrentTrans.SetRotation(DeltaRotate.Quaternion() * CurrentTrans.GetRotation());
}
if (!DeltaScale.IsNearlyZero())
{
CurrentTrans.MultiplyScale3D(DeltaScale);
}
if (CurrentlyEditingComponent->bDecoupleMeshPlacement)
{
if (USceneComponent* ParentComp = CurrentlyEditingComponent->GetAttachParent())
{
CurrentlyEditingComponent->HandRelativePlacement = CurrentTrans.GetRelativeTransform(ParentComp->GetComponentTransform());
}
}
else
{
CurrentlyEditingComponent->HandRelativePlacement = CurrentTrans.GetRelativeTransform(CurrentlyEditingComponent->GetComponentTransform());
}
NotifyPropertyModified(CurrentlyEditingComponent, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
//GEditor->RedrawLevelEditingViewports(true);
bHandled = true;
}
else
{
UHandSocketComponent* CurrentlyEditingComponent = GetCurrentlyEditingComponent();
if (!CurrentlyEditingComponent || !CurrentlyEditingComponent->HandVisualizerComponent)
{
return false;
}
const FScopedTransaction Transaction(LOCTEXT("ChangingComp", "ChangingComp"));
CurrentlyEditingComponent->Modify();
if (AActor* Owner = CurrentlyEditingComponent->GetOwner())
{
Owner->Modify();
}
bool bLevelEdit = ViewportClient->IsLevelEditorClient();
FTransform BoneTrans = CurrentlyEditingComponent->HandVisualizerComponent->GetBoneTransform(CurrentlySelectedBoneIdx);
FTransform NewTrans = BoneTrans;
NewTrans.SetRotation(DeltaRotate.Quaternion() * NewTrans.GetRotation());
FQuat DeltaRotateMod = NewTrans.GetRelativeTransform(BoneTrans).GetRotation();
bool bFoundBone = false;
for (FBPVRHandPoseBonePair& BonePair : CurrentlyEditingComponent->CustomPoseDeltas)
{
if (BonePair.BoneName == CurrentlySelectedBone)
{
bFoundBone = true;
BonePair.DeltaPose *= DeltaRotateMod;
break;
}
}
if (!bFoundBone)
{
FBPVRHandPoseBonePair newBonePair;
newBonePair.BoneName = CurrentlySelectedBone;
newBonePair.DeltaPose *= DeltaRotateMod;
CurrentlyEditingComponent->CustomPoseDeltas.Add(newBonePair);
bFoundBone = true;
}
if (bFoundBone)
{
NotifyPropertyModified(CurrentlyEditingComponent, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
}
//GEditor->RedrawLevelEditingViewports(true);
bHandled = true;
}
}
return bHandled;
}
void FHandSocketVisualizer::EndEditing()
{
HandPropertyPath = FComponentPropertyPath();
CurrentlySelectedBone = NAME_None;
CurrentlySelectedBoneIdx = INDEX_NONE;
TargetViewport = nullptr;
}
#undef LOCTEXT_NAMESPACE

View file

@ -0,0 +1,73 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "VRExpansionEditor.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#include "Grippables/HandSocketComponent.h"
#include "PropertyEditorModule.h"
#include "HandSocketVisualizer.h"
#include "HandSocketComponentDetails.h"
#include "VRGlobalSettingsDetails.h"
#include "VRGlobalSettings.h"
IMPLEMENT_MODULE(FVRExpansionEditorModule, VRExpansionEditor);
void FVRExpansionEditorModule::StartupModule()
{
RegisterComponentVisualizer(UHandSocketComponent::StaticClass()->GetFName(), MakeShareable(new FHandSocketVisualizer));
// Register detail customizations
{
auto& PropertyModule = FModuleManager::LoadModuleChecked< FPropertyEditorModule >("PropertyEditor");
// Register our customization to be used by a class 'UMyClass' or 'AMyClass'. Note the prefix must be dropped.
PropertyModule.RegisterCustomClassLayout(
UHandSocketComponent::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FHandSocketComponentDetails::MakeInstance)
);
PropertyModule.RegisterCustomClassLayout(
UVRGlobalSettings::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FVRGlobalSettingsDetails::MakeInstance)
);
PropertyModule.NotifyCustomizationModuleChanged();
}
}
void FVRExpansionEditorModule::ShutdownModule()
{
if (GUnrealEd != NULL)
{
// Iterate over all class names we registered for
for (FName ClassName : RegisteredComponentClassNames)
{
GUnrealEd->UnregisterComponentVisualizer(ClassName);
}
}
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
{
auto& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomClassLayout(UHandSocketComponent::StaticClass()->GetFName());
PropertyModule.UnregisterCustomClassLayout(UVRGlobalSettings::StaticClass()->GetFName());
}
}
void FVRExpansionEditorModule::RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer)
{
if (GUnrealEd != NULL)
{
GUnrealEd->RegisterComponentVisualizer(ComponentClassName, Visualizer);
}
RegisteredComponentClassNames.Add(ComponentClassName);
if (Visualizer.IsValid())
{
Visualizer->OnRegister();
}
}

View file

@ -0,0 +1,205 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "VRGlobalSettingsDetails.h"
#include "VRGlobalSettings.h"
//#include "PropertyEditing.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SButton.h"
//#include "PropertyHandle.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "DetailCategoryBuilder.h"
#include "IDetailsView.h"
//#include "Developer/AssetTools/Public/IAssetTools.h"
//#include "Developer/AssetTools/Public/AssetToolsModule.h"
//#include "Editor/ContentBrowser/Public/IContentBrowserSingleton.h"
//#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
//#include "AnimationUtils.h"
#include "UObject/SavePackage.h"
#include "Misc/MessageDialog.h"
//#include "Widgets/Layout/SBorder.h"
//#include "Widgets/Layout/SSeparator.h"
//#include "Widgets/Layout/SUniformGridPanel.h"
//#include "Widgets/Input/SEditableTextBox.h"
#include "Editor.h"
#include "EditorStyleSet.h"
#include "Styling/CoreStyle.h"
#include "Animation/AnimData/AnimDataModel.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/ARFilter.h"
#include "FileHelpers.h"
#include "Engine/Engine.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#define LOCTEXT_NAMESPACE "VRGlobalSettingsDetails"
TSharedRef< IDetailCustomization > FVRGlobalSettingsDetails::MakeInstance()
{
return MakeShareable(new FVRGlobalSettingsDetails);
}
void FVRGlobalSettingsDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
//DetailBuilder.HideCategory(FName("ComponentTick"));
//DetailBuilder.HideCategory(FName("GameplayTags"));
//TSharedPtr<IPropertyHandle> LockedLocationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupleMeshPlacement));
//FSimpleDelegate OnLockedStateChangedDelegate = FSimpleDelegate::CreateSP(this, &FVRGlobalSettingsDetails::OnLockedStateUpdated, &DetailBuilder);
//LockedLocationProperty->SetOnPropertyValueChanged(OnLockedStateChangedDelegate);
DetailBuilder.EditCategory("Utilities")
.AddCustomRow(LOCTEXT("FixInvalidAnimationAssets", "Fix Invalid 5.2 Animation Assets"))
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("FixInvalidAnimationAssets", "Fix Invalid 5.2 Animation Assets"))
]
.ValueContent()
.MaxDesiredWidth(125.f)
.MinDesiredWidth(125.f)
[
SNew(SButton)
.ContentPadding(2)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.OnClicked(this, &FVRGlobalSettingsDetails::OnCorrectInvalidAnimationAssets)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("FixInvalidAnimationAssetsButton", "Fix Animation Assets"))
]
];
/*DetailBuilder.EditCategory("Utilities")
.AddCustomRow(LOCTEXT("Fix 5.4.0 Shadow Shader", "Fix Broken 5.4.0 Shadow Shader"))
.NameContent()
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("Fix54ShadowShader", "Fix Broken 5.4.0 Shadow Shader"))
]
.ValueContent()
.MaxDesiredWidth(125.f)
.MinDesiredWidth(125.f)
[
SNew(SButton)
.ContentPadding(2)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.OnClicked(this, &FVRGlobalSettingsDetails::OnFixShadowShader)
[
SNew(STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(LOCTEXT("Fix54ShadowShaderButton", "Fix Broken 5.4.0 Shadow Shader"))
]
];*/
}
/*FReply FVRGlobalSettingsDetails::OnFixShadowShader()
{
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION == 4
FString EnginePath = FPaths::EngineDir();
EnginePath += "/Shaders/Private/ShadowProjectionPixelShader.usf";
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*EnginePath))
{
FString FileStr = "";
if (FFileHelper::LoadFileToString(FileStr, *EnginePath))
{
// Don't need to do anything complicated, the shader only contains one line with this define in it
FileStr = FileStr.Replace(TEXT("#if MOBILE_MULTI_VIEW || INSTANCED_STEREO"),TEXT("#if ((MOBILE_MULTI_VIEW || INSTANCED_STEREO) && SHADING_PATH_MOBILE)"));
FFileHelper::SaveStringToFile(FileStr, *EnginePath);
GEngine->Exec(GEngine->GetWorld(), TEXT("recompileshaders changed"));
UE_LOG(LogPath, Log, TEXT("ShadowProjectionPixelShader.usf resaved and recompiled!"));
return FReply::Handled();
}
}
#endif
UE_LOG(LogPath, Error, TEXT("ShadowProjectionPixelShader.usf could not be found, edited, or the engine version is incorrect!"));
return FReply::Handled();
}*/
FReply FVRGlobalSettingsDetails::OnCorrectInvalidAnimationAssets()
{
// Load the asset registry module
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked< FAssetRegistryModule >(FName("AssetRegistry"));
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
TArray< FString > ContentPaths;
ContentPaths.Add(TEXT("/Game"));
AssetRegistry.ScanPathsSynchronous(ContentPaths);
FARFilter Filter;
Filter.ClassPaths.Add(UAnimSequence::StaticClass()->GetClassPathName());
//Filter.ClassNames.Add(UAnimSequence::StaticClass()->GetFName());
Filter.bRecursiveClasses = true;
//if (!Path.IsEmpty())
//{
// Filter.PackagePaths.Add(*Path);
//}
Filter.bRecursivePaths = true;
TArray< FAssetData > AssetList;
AssetRegistry.GetAssets(Filter, AssetList);
// Iterate over retrieved blueprint assets
for (auto& Asset : AssetList)
{
// Check the anim sequence for invalid data
if (UAnimSequence* AnimSeq = Cast<UAnimSequence>(Asset.GetAsset()))
{
IAnimationDataController& AnimController = AnimSeq->GetController();
{
IAnimationDataController::FScopedBracket ScopedBracket(AnimController, LOCTEXT("FixAnimationAsset_VRE", "Fixing invalid anim sequences"));
const IAnimationDataModel* AnimModel = AnimController.GetModel();
FFrameRate FrameRate = AnimModel->GetFrameRate();
//int32 NumFrames = AnimModel->GetNumberOfFrames();
double FrameRateD = FrameRate.AsDecimal();
// I was saving with a below 1.0 frame rate and 1 frame
if (FrameRateD < 1.0f)
{
// We have an invalid frame rate for 5.2
AnimController.SetFrameRate(FFrameRate(1, 1));
AnimSeq->MarkPackageDirty();
UPackage* const Package = AnimSeq->GetOutermost();
FString const PackageName = Package->GetName();
FString const PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
double StartTime = FPlatformTime::Seconds();
FSavePackageArgs PackageArguments;
PackageArguments.SaveFlags = RF_Standalone;
PackageArguments.SaveFlags = SAVE_NoError;
UPackage::SavePackage(Package, NULL, *PackageFileName, PackageArguments);
//UPackage::SavePackage(Package, NULL, RF_Standalone, *PackageFileName, GError, nullptr, false, true, SAVE_NoError);
double ElapsedTime = FPlatformTime::Seconds() - StartTime;
UE_LOG(LogAnimation, Log, TEXT("Animation re-saved %s in %0.2f seconds"), *PackageName, ElapsedTime);
}
}
}
}
return FReply::Handled();
}
#undef LOCTEXT_NAMESPACE

View file

@ -0,0 +1,83 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Grippables/HandSocketComponent.h"
#include "IDetailCustomization.h"
#include "Input/Reply.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWindow.h"
class IDetailLayoutBuilder;
class FHandSocketComponentDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
//void SortCategories(const TMap<FName, IDetailCategoryBuilder*>& AllCategoryMap);
// The selected hand component
TWeakObjectPtr<UHandSocketComponent> HandSocketComponent;
FReply OnUpdateSavePose();
TWeakObjectPtr<UAnimSequence> SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName);
void OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder);
void OnLeftDominantUpdated(IDetailLayoutBuilder* LayoutBuilder);
void OnHandRelativeUpdated(IDetailLayoutBuilder* LayoutBuilder);
void OnUpdateShowMesh(IDetailLayoutBuilder* LayoutBuilder);
FHandSocketComponentDetails()
{
}
};
class SCreateHandAnimationDlg : public SWindow
{
public:
SLATE_BEGIN_ARGS(SCreateHandAnimationDlg)
{
}
SLATE_ARGUMENT(FText, DefaultAssetPath)
SLATE_END_ARGS()
SCreateHandAnimationDlg()
: UserResponse(EAppReturnType::Cancel)
{
}
void Construct(const FArguments& InArgs);
public:
/** Displays the dialog in a blocking fashion */
EAppReturnType::Type ShowModal();
/** Gets the resulting asset path */
FString GetAssetPath();
/** Gets the resulting asset name */
FString GetAssetName();
/** Gets the resulting full asset path (path+'/'+name) */
FString GetFullAssetPath();
protected:
void OnPathChange(const FString& NewPath);
void OnNameChange(const FText& NewName, ETextCommit::Type CommitInfo);
FReply OnButtonClick(EAppReturnType::Type ButtonID);
bool ValidatePackage();
EAppReturnType::Type UserResponse;
FText AssetPath;
FText AssetName;
static FText LastUsedAssetPath;
};

View file

@ -0,0 +1,95 @@
#pragma once
#include "ComponentVisualizer.h"
#include "Grippables/HandSocketComponent.h"
#include "ActorEditorUtils.h"
class FEditorViewportClient;
/**Base class for clickable targeting editing proxies*/
struct VREXPANSIONEDITOR_API HHandSocketVisProxy : public HComponentVisProxy
{
DECLARE_HIT_PROXY();
HHandSocketVisProxy(const UActorComponent* InComponent)
: HComponentVisProxy(InComponent, HPP_Wireframe)
{
BoneIdx = 0;
TargetBoneName = NAME_None;
}
uint32 BoneIdx;
FName TargetBoneName;
};
class VREXPANSIONEDITOR_API FHandSocketVisualizer : public FComponentVisualizer
{
public:
FHandSocketVisualizer()
{
CurrentlySelectedBone = NAME_None;
CurrentlySelectedBoneIdx = INDEX_NONE;
HandPropertyPath = FComponentPropertyPath();
TargetViewport = nullptr;
}
virtual ~FHandSocketVisualizer()
{
}
UPROPERTY()
FComponentPropertyPath HandPropertyPath;
FName CurrentlySelectedBone;
uint32 CurrentlySelectedBoneIdx;
UPROPERTY()
FViewport* TargetViewport;
UHandSocketComponent* GetCurrentlyEditingComponent() const
{
return Cast<UHandSocketComponent>(HandPropertyPath.GetComponent());;
}
const UHandSocketComponent* UpdateSelectedHandComponent(HComponentVisProxy* VisProxy)
{
const UHandSocketComponent* HandComp = CastChecked<const UHandSocketComponent>(VisProxy->Component.Get());
UHandSocketComponent* OldHandComp = Cast<UHandSocketComponent>(HandPropertyPath.GetComponent());
AActor* OldOwningActor = HandPropertyPath.GetParentOwningActor();
HandPropertyPath = FComponentPropertyPath(HandComp);
AActor* NewOwningActor = HandPropertyPath.GetParentOwningActor();
if (HandPropertyPath.IsValid())
{
if (OldOwningActor != NewOwningActor || OldHandComp != HandComp)
{
// Reset selection state if we are selecting a different actor to the one previously selected
CurrentlySelectedBoneIdx = INDEX_NONE;
CurrentlySelectedBone = NAME_None;
}
return HandComp;
}
HandPropertyPath = FComponentPropertyPath();
return nullptr;
}
bool SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName);
bool GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const override;
bool IsVisualizingArchetype() const override;
virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) override;
virtual void DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas) override;
virtual bool VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click) override;
bool GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const override;
bool HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltaRotate, FVector& DeltaScale) override;
virtual void EndEditing() override;
private:
};

View file

@ -0,0 +1,19 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Runtime/Core/Public/Modules/ModuleInterface.h"
#include "ComponentVisualizer.h"
class FVRExpansionEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
void RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr<FComponentVisualizer> Visualizer);
/** Array of component class names we have registered, so we know what to unregister afterwards */
TArray<FName> RegisteredComponentClassNames;
};

View file

@ -0,0 +1,33 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "IDetailCustomization.h"
#include "Input/Reply.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWindow.h"
class IDetailLayoutBuilder;
class FVRGlobalSettingsDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
FReply OnCorrectInvalidAnimationAssets();
FReply OnFixShadowShader();
void OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder);
FVRGlobalSettingsDetails()
{
}
};

View file

@ -0,0 +1,62 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
using System.IO;
namespace UnrealBuildTool.Rules
{
public class VRExpansionEditor : ModuleRules
{
public VRExpansionEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
// ... add other public dependencies that you statically link with here ...
"Engine",
"Core",
"CoreUObject",
"VRExpansionPlugin",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"BlueprintGraph",
"AnimGraph",
"AnimGraphRuntime",
"SlateCore",
"Slate",
"InputCore",
"Engine",
"UnrealEd",
"EditorStyle",
"AssetRegistry"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
}