A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
HandSocketComponentDetails.cpp
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
5#include "PropertyEditing.h"
6#include "Widgets/Text/STextBlock.h"
7#include "Widgets/Input/SButton.h"
8#include "PropertyHandle.h"
9#include "DetailLayoutBuilder.h"
10#include "DetailWidgetRow.h"
11#include "DetailCategoryBuilder.h"
12#include "IDetailsView.h"
13
14#include "Developer/AssetTools/Public/IAssetTools.h"
15#include "Developer/AssetTools/Public/AssetToolsModule.h"
16#include "Editor/ContentBrowser/Public/IContentBrowserSingleton.h"
17#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
18#include "AnimationUtils.h"
19#include "AssetRegistryModule.h"
20#include "Misc/MessageDialog.h"
21#include "Widgets/Layout/SBorder.h"
22#include "Widgets/Layout/SSeparator.h"
23#include "Widgets/Layout/SUniformGridPanel.h"
24#include "Widgets/Input/SEditableTextBox.h"
25#include "Editor.h"
26#include "EditorStyleSet.h"
27#include "Styling/CoreStyle.h"
28
29#include "Editor/UnrealEdEngine.h"
30#include "UnrealEdGlobals.h"
31
32#define LOCTEXT_NAMESPACE "HandSocketComponentDetails"
33
35
36static bool PromptUserForAssetPath(FString& AssetPath, FString& AssetName)
37{
38 TSharedRef<SCreateHandAnimationDlg> NewAnimDlg = SNew(SCreateHandAnimationDlg);
39 if (NewAnimDlg->ShowModal() != EAppReturnType::Cancel)
40 {
41 AssetPath = NewAnimDlg->GetFullAssetPath();
42 AssetName = NewAnimDlg->GetAssetName();
43 return true;
44 }
45
46 return false;
47}
48
49TWeakObjectPtr<UAnimSequence> FHandSocketComponentDetails::SaveAnimationAsset(const FString& InAssetPath, const FString& InAssetName)
50{
51
52 TWeakObjectPtr<UAnimSequence> FinalAnimation;
53
54 // Replace when this moves to custom display
55 if (!HandSocketComponent.IsValid())
56 return FinalAnimation;
57
58 /*if (!HandSocketComponent->HandVisualizerComponent)// || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh || !HandSocketComponent->HandVisualizerComponent->SkeletalMesh->Skeleton)
59 {
60 return false;
61 }*/
62
63 if (!HandSocketComponent->HandTargetAnimation && (!HandSocketComponent->VisualizationMesh || !HandSocketComponent->VisualizationMesh->GetSkeleton()))
64 {
65 return FinalAnimation;
66 }
67
68 // create the asset
69 FText InvalidPathReason;
70 bool const bValidPackageName = FPackageName::IsValidLongPackageName(InAssetPath, false, &InvalidPathReason);
71 if (bValidPackageName == false)
72 {
73 UE_LOG(LogAnimation, Log, TEXT("%s is an invalid asset path, prompting user for new asset path. Reason: %s"), *InAssetPath, *InvalidPathReason.ToString());
74 }
75
76 FString ValidatedAssetPath = InAssetPath;
77 FString ValidatedAssetName = InAssetName;
78
79 UObject* Parent = bValidPackageName ? CreatePackage(*ValidatedAssetPath) : nullptr;
80 if (Parent == nullptr)
81 {
82 // bad or no path passed in, do the popup
83 if (PromptUserForAssetPath(ValidatedAssetPath, ValidatedAssetName) == false)
84 {
85 return FinalAnimation;
86 }
87
88 Parent = CreatePackage(*ValidatedAssetPath);
89 }
90
91 UObject* const Object = LoadObject<UObject>(Parent, *ValidatedAssetName, nullptr, LOAD_Quiet, nullptr);
92 // if object with same name exists, warn user
93 if (Object)
94 {
95 EAppReturnType::Type ReturnValue = FMessageDialog::Open(EAppMsgType::YesNo, NSLOCTEXT("UnrealEd", "Error_AssetExist", "Asset with same name exists. Do you wish to overwrite it?"));
96 if (ReturnValue == EAppReturnType::No)
97 {
98 return FinalAnimation; // failed
99 }
100 }
101
102 UAnimSequence* BaseAnimation = HandSocketComponent->HandTargetAnimation;
103 TArray<FTransform> LocalPoses;
104
105 if (!BaseAnimation)
106 {
107 LocalPoses = HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRefLocalPoses();
108 }
109
110 // If not, create new one now.
111 UAnimSequence* const NewSeq = NewObject<UAnimSequence>(Parent, *ValidatedAssetName, RF_Public | RF_Standalone);
112 if (NewSeq)
113 {
114 // set skeleton
115 if (BaseAnimation)
116 {
117 NewSeq->SetSkeleton(BaseAnimation->GetSkeleton());
118 }
119 else
120 {
121 NewSeq->SetSkeleton(HandSocketComponent->VisualizationMesh->GetSkeleton());
122 }
123
124 // Notify the asset registry
125 FAssetRegistryModule::AssetCreated(NewSeq);
126 //StartRecord(Component, NewSeq);
127
128 //return true;
129 UAnimSequence* AnimationObject = NewSeq;
130
131 AnimationObject->RecycleAnimSequence();
132 if (BaseAnimation)
133 {
134 AnimationObject->BoneCompressionSettings = BaseAnimation->BoneCompressionSettings;
135 }
136 else
137 {
138 AnimationObject->BoneCompressionSettings = FAnimationUtils::GetDefaultAnimationBoneCompressionSettings();
139 }
140
141 AnimationObject->SequenceLength = 4.f;
142 AnimationObject->SetRawNumberOfFrame(1);
143
144 if (BaseAnimation)
145 {
146 for (FName TrackName : BaseAnimation->GetAnimationTrackNames())
147 {
148 AnimationObject->AddNewRawTrack(TrackName);
149 }
150 }
151 else
152 {
153 int numBones = HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetNum();
154 for (int i = 0; i < LocalPoses.Num() && i < numBones; ++i)
155 {
156 AnimationObject->AddNewRawTrack(HandSocketComponent->VisualizationMesh->GetRefSkeleton().GetBoneName(i));
157 }
158 }
159
160 if (BaseAnimation)
161 {
162 AnimationObject->RetargetSource = BaseAnimation->RetargetSource;
163 }
164 else
165 {
166 AnimationObject->RetargetSource = HandSocketComponent->VisualizationMesh ? HandSocketComponent->VisualizationMesh->GetSkeleton()->GetRetargetSourceForMesh(HandSocketComponent->VisualizationMesh) : NAME_None;
167 }
168
169
171 if (BaseAnimation)
172 {
173 for (int32 TrackIndex = 0; TrackIndex < BaseAnimation->GetRawAnimationData().Num(); ++TrackIndex)
174 {
175 FRawAnimSequenceTrack& RawTrack = BaseAnimation->GetRawAnimationTrack(TrackIndex);
176
177 bool bHadLoc = false;
178 bool bHadRot = false;
179 bool bHadScale = false;
180 FVector Loc = FVector::ZeroVector;
181 FQuat Rot = FQuat::Identity;
182 FVector Scale = FVector::ZeroVector;
183
184 if (RawTrack.PosKeys.Num())
185 {
186 Loc = RawTrack.PosKeys[0];
187 bHadLoc = true;
188 }
189
190 if (RawTrack.RotKeys.Num())
191 {
192 Rot = RawTrack.RotKeys[0];
193 bHadRot = true;
194 }
195
196 if (RawTrack.ScaleKeys.Num())
197 {
198 Scale = RawTrack.ScaleKeys[0];
199 bHadScale = true;
200 }
201
202 FTransform FinalTrans(Rot, Loc, Scale);
203
204 FName TrackName = (BaseAnimation->GetAnimationTrackNames())[TrackIndex];
205
206 FQuat DeltaQuat = FQuat::Identity;
207 for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
208 {
209 if (HandPair.BoneName == TrackName)
210 {
211 DeltaQuat = HandPair.DeltaPose;
212 bHadRot = true;
213 break;
214 }
215 }
216
217 FinalTrans.ConcatenateRotation(DeltaQuat);
218 FinalTrans.NormalizeRotation();
219
220 FRawAnimSequenceTrack& RawNewTrack = AnimationObject->GetRawAnimationTrack(TrackIndex);
221 if (bHadLoc)
222 RawNewTrack.PosKeys.Add(FinalTrans.GetTranslation());
223 if (bHadRot)
224 RawNewTrack.RotKeys.Add(FinalTrans.GetRotation());
225 if (bHadScale)
226 RawNewTrack.ScaleKeys.Add(FinalTrans.GetScale3D());
227 }
228 }
229 else
230 {
231 USkeletalMesh* SkeletalMesh = HandSocketComponent->VisualizationMesh;
232 USkeleton* AnimSkeleton = SkeletalMesh->GetSkeleton();
233 for (int32 TrackIndex = 0; TrackIndex < AnimationObject->GetRawAnimationData().Num(); ++TrackIndex)
234 {
235 // verify if this bone exists in skeleton
236 int32 BoneTreeIndex = AnimationObject->GetSkeletonIndexFromRawDataTrackIndex(TrackIndex);
237 if (BoneTreeIndex != INDEX_NONE)
238 {
239 int32 BoneIndex = AnimSkeleton->GetMeshBoneIndexFromSkeletonBoneIndex(SkeletalMesh, BoneTreeIndex);
240 //int32 ParentIndex = SkeletalMesh->RefSkeleton.GetParentIndex(BoneIndex);
241 FTransform LocalTransform = LocalPoses[BoneIndex];
242
243
244 FName BoneName = AnimSkeleton->GetReferenceSkeleton().GetBoneName(BoneIndex);
245
246 FQuat DeltaQuat = FQuat::Identity;
247 for (FBPVRHandPoseBonePair& HandPair : HandSocketComponent->CustomPoseDeltas)
248 {
249 if (HandPair.BoneName == BoneName)
250 {
251 DeltaQuat = HandPair.DeltaPose;
252 }
253 }
254
255 LocalTransform.ConcatenateRotation(DeltaQuat);
256 LocalTransform.NormalizeRotation();
257
258 FRawAnimSequenceTrack& RawTrack = AnimationObject->GetRawAnimationTrack(TrackIndex);
259 RawTrack.PosKeys.Add(LocalTransform.GetTranslation());
260 RawTrack.RotKeys.Add(LocalTransform.GetRotation());
261 RawTrack.ScaleKeys.Add(LocalTransform.GetScale3D());
262 }
263 }
264 }
265
270
271 // init notifies
272 AnimationObject->InitializeNotifyTrack();
273 AnimationObject->PostProcessSequence();
274 AnimationObject->MarkPackageDirty();
275
276 //if (bAutoSaveAsset)
277 {
278 UPackage* const Package = AnimationObject->GetOutermost();
279 FString const PackageName = Package->GetName();
280 FString const PackageFileName = FPackageName::LongPackageNameToFilename(PackageName, FPackageName::GetAssetPackageExtension());
281
282 double StartTime = FPlatformTime::Seconds();
283
284 UPackage::SavePackage(Package, NULL, RF_Standalone, *PackageFileName, GError, nullptr, false, true, SAVE_NoError);
285
286 double ElapsedTime = FPlatformTime::Seconds() - StartTime;
287 UE_LOG(LogAnimation, Log, TEXT("Animation Recorder saved %s in %0.2f seconds"), *PackageName, ElapsedTime);
288 }
289
290 FinalAnimation = AnimationObject;
291 return FinalAnimation;
292 }
293
294 return FinalAnimation;
295}
296
297TSharedRef< IDetailCustomization > FHandSocketComponentDetails::MakeInstance()
298{
299 return MakeShareable(new FHandSocketComponentDetails);
300}
301
302void FHandSocketComponentDetails::OnHandRelativeUpdated(IDetailLayoutBuilder* LayoutBuilder)
303{
304
305 if (!HandSocketComponent.IsValid())
306 {
307 return;
308 }
309
310 HandSocketComponent->Modify();
311 if (AActor* Owner = HandSocketComponent->GetOwner())
312 {
313 Owner->Modify();
314 }
315
316 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
317 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
318
319 if (HandVisualizer)
320 {
321 if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
322 {
323 RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
324 }
325 }
326
327 FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
328}
329
330void FHandSocketComponentDetails::OnLeftDominantUpdated(IDetailLayoutBuilder* LayoutBuilder)
331{
332
333 if (!HandSocketComponent.IsValid())
334 {
335 return;
336 }
337
338 // Default to always flipping this
339 //if (HandSocketComponent->bFlipForLeftHand)
340 {
341 FTransform relTrans = HandSocketComponent->GetRelativeTransform();
342 FTransform HandPlacement = HandSocketComponent->GetHandRelativePlacement();
343
344 if (HandSocketComponent->bDecoupleMeshPlacement)
345 {
346 relTrans = FTransform::Identity;
347 }
348
349 FTransform ReturnTrans = (HandPlacement * relTrans);
350
351 HandSocketComponent->MirrorHandTransform(ReturnTrans, relTrans);
352
353 HandSocketComponent->Modify();
354 if (AActor* Owner = HandSocketComponent->GetOwner())
355 {
356 Owner->Modify();
357 }
358 ReturnTrans = ReturnTrans.GetRelativeTransform(relTrans);
359 HandSocketComponent->HandRelativePlacement = ReturnTrans;
360
361 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
362 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
363
364 if (HandVisualizer)
365 {
366 if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
367 {
368 RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
369 //FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
370 }
371 }
372
373 FComponentVisualizer::NotifyPropertyModified(HandSocketComponent.Get(), FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
374 }
375}
376
377void FHandSocketComponentDetails::OnLockedStateUpdated(IDetailLayoutBuilder* LayoutBuilder)
378{
379
380 if (!HandSocketComponent.IsValid())
381 {
382 return;
383 }
384
385 if (HandSocketComponent->bDecoupleMeshPlacement)
386 {
387 //FTransform RelTrans = HandSocketComponent->GetRelativeTransform();
388 //FTransform WorldTrans = HandSocketComponent->GetComponentTransform();
389 //if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
390 {
391
392 HandSocketComponent->Modify();
393 if (AActor* Owner = HandSocketComponent->GetOwner())
394 {
395 Owner->Modify();
396 }
397 HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement * HandSocketComponent->GetRelativeTransform();// HandSocketComponent->GetComponentTransform();
398 HandSocketComponent->bDecoupled = true;
399
400 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
401 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
402
403 if (HandVisualizer)
404 {
405 if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
406 {
407 RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
408 RefHand->bDecoupled = true;
409 //FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
410 }
411 }
412 }
413 }
414 else
415 {
416 //if (USceneComponent* ParentComp = HandSocketComponent->GetAttachParent())
417 {
418 HandSocketComponent->Modify();
419 if (AActor* Owner = HandSocketComponent->GetOwner())
420 {
421 Owner->Modify();
422 }
423 HandSocketComponent->HandRelativePlacement = HandSocketComponent->HandRelativePlacement.GetRelativeTransform(HandSocketComponent->GetRelativeTransform());
424 HandSocketComponent->bDecoupled = false;
425
426 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
427 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
428
429 if (HandVisualizer)
430 {
431 if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
432 {
433 RefHand->HandRelativePlacement = HandSocketComponent->HandRelativePlacement;
434 RefHand->bDecoupled = false;
435 //FComponentVisualizer::NotifyPropertyModified(RefHand, FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
436 }
437 }
438 }
439 }
440
441 TArray<FProperty*> PropertiesToModify;
442 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement)));
443 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupled)));
444 FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
445}
446
447void FHandSocketComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
448{
449 // Hide the SplineCurves property
450 //TSharedPtr<IPropertyHandle> HandPlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
451 //HandPlacementProperty->MarkHiddenByCustomization();
452
453
454 TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
455 DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
456
457 if (ObjectsBeingCustomized.Num() == 1)
458 {
459 UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(ObjectsBeingCustomized[0]);
460 if (CurrentHandSocket != NULL)
461 {
462 if (HandSocketComponent != CurrentHandSocket)
463 {
464 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
465 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
466
467 if (HandVisualizer)
468 {
469 HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
470 HandVisualizer->CurrentlySelectedBone = NAME_None;
471 HandVisualizer->HandPropertyPath = FComponentPropertyPath();
472 //HandVisualizer->OldHandSocketComp = CurrentHandSocket;
473 }
474
475 HandSocketComponent = CurrentHandSocket;
476 }
477 }
478 }
479
480 /*const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailBuilder.GetSelectedObjects();
481 for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex)
482 {
483 const TWeakObjectPtr<UObject>& CurrentObject = SelectedObjects[ObjectIndex];
484 if (CurrentObject.IsValid())
485 {
486 UHandSocketComponent* CurrentHandSocket = Cast<UHandSocketComponent>(CurrentObject.Get());
487 if (CurrentHandSocket != NULL)
488 {
489 if (HandSocketComponent != CurrentHandSocket)
490 {
491 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(CurrentHandSocket->GetClass());
492 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
493
494 if (HandVisualizer)
495 {
496 HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
497 HandVisualizer->CurrentlySelectedBone = NAME_None;
498 HandVisualizer->HandPropertyPath = FComponentPropertyPath();
499 //HandVisualizer->OldHandSocketComp = CurrentHandSocket;
500 }
501
502 HandSocketComponent = CurrentHandSocket;
503 }
504 break;
505 }
506 }
507 }*/
508
509 DetailBuilder.HideCategory(FName("ComponentTick"));
510 DetailBuilder.HideCategory(FName("GameplayTags"));
511 DetailBuilder.HideCategory(FName("VRGripInterface"));
512 DetailBuilder.HideCategory(FName("VRGripInterface|Replication"));
513 DetailBuilder.HideCategory(FName("Tags"));
514 DetailBuilder.HideCategory(FName("AssetUserData"));
515 DetailBuilder.HideCategory(FName("Events"));
516 DetailBuilder.HideCategory(FName("Activation"));
517 DetailBuilder.HideCategory(FName("Cooking"));
518 DetailBuilder.HideCategory(FName("ComponentReplication"));
519
520 TSharedPtr<IPropertyHandle> LockedLocationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bDecoupleMeshPlacement));
521 TSharedPtr<IPropertyHandle> HandRelativePlacementProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandRelativePlacement));
522 TSharedPtr<IPropertyHandle> LeftHandDominateProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bLeftHandDominant));
523
524 FSimpleDelegate OnHandRelativeChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnHandRelativeUpdated, &DetailBuilder);
525 HandRelativePlacementProperty->SetOnPropertyValueChanged(OnHandRelativeChangedDelegate);
526
527 FSimpleDelegate OnLockedStateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLockedStateUpdated, &DetailBuilder);
528 LockedLocationProperty->SetOnPropertyValueChanged(OnLockedStateChangedDelegate);
529
530 FSimpleDelegate OnLeftDominateChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnLeftDominantUpdated, &DetailBuilder);
531 LeftHandDominateProperty->SetOnPropertyValueChanged(OnLeftDominateChangedDelegate);
532
533 TSharedPtr<IPropertyHandle> ShowVisualizationProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bShowVisualizationMesh));
534
535 FSimpleDelegate OnShowVisChangedDelegate = FSimpleDelegate::CreateSP(this, &FHandSocketComponentDetails::OnUpdateShowMesh, &DetailBuilder);
536 ShowVisualizationProperty->SetOnPropertyValueChanged(OnShowVisChangedDelegate);
537
538 DetailBuilder.EditCategory("Hand Animation")
539 .AddCustomRow(NSLOCTEXT("HandSocketDetails", "UpdateHandSocket", "Save Current Pose"))
540 .NameContent()
541 [
542 SNew(STextBlock)
543 .Font(IDetailLayoutBuilder::GetDetailFont())
544 .Text(NSLOCTEXT("HandSocketDetails", "UpdateHandSocket", "Save Current Pose"))
545 ]
546 .ValueContent()
547 .MaxDesiredWidth(125.f)
548 .MinDesiredWidth(125.f)
549 [
550 SNew(SButton)
551 .ContentPadding(2)
552 .VAlign(VAlign_Center)
553 .HAlign(HAlign_Center)
555 [
556 SNew(STextBlock)
557 .Font(IDetailLayoutBuilder::GetDetailFont())
558 .Text(NSLOCTEXT("HandSocketDetails", "UpdateHandSocket", "Save"))
559 ]
560 ];
561}
562
563void FHandSocketComponentDetails::OnUpdateShowMesh(IDetailLayoutBuilder* LayoutBuilder)
564{
565 if (!HandSocketComponent.IsValid())
566 return;
567
568 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
569 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
570
571 if (HandVisualizer)
572 {
573 HandVisualizer->CurrentlySelectedBoneIdx = INDEX_NONE;
574 HandVisualizer->CurrentlySelectedBone = NAME_None;
575 HandVisualizer->HandPropertyPath = FComponentPropertyPath();
576 }
577}
578
580{
581 if (HandSocketComponent.IsValid() && HandSocketComponent->CustomPoseDeltas.Num() > 0)
582 {
583 if (HandSocketComponent->HandTargetAnimation || HandSocketComponent->VisualizationMesh)
584 {
585 // Save Animation Pose here
586 FString AssetPath;
587 FString AssetName;
588 PromptUserForAssetPath(AssetPath, AssetName);
589 TWeakObjectPtr<UAnimSequence> NewAnim = SaveAnimationAsset(AssetPath, AssetName);
590
591 // Finally remove the deltas
592 if (NewAnim.IsValid())
593 {
594 HandSocketComponent->Modify();
595 if (AActor* Owner = HandSocketComponent->GetOwner())
596 {
597 Owner->Modify();
598 }
599
600 HandSocketComponent->HandTargetAnimation = NewAnim.Get();
601 HandSocketComponent->CustomPoseDeltas.Empty();
602 HandSocketComponent->bUseCustomPoseDeltas = false;
603
604 TSharedPtr<FComponentVisualizer> Visualizer = GUnrealEd->FindComponentVisualizer(HandSocketComponent->GetClass());
605 FHandSocketVisualizer* HandVisualizer = (FHandSocketVisualizer*)Visualizer.Get();
606
607 if (HandVisualizer)
608 {
609 if (UHandSocketComponent* RefHand = HandVisualizer->GetCurrentlyEditingComponent())
610 {
611 RefHand->HandTargetAnimation = NewAnim.Get();
612 RefHand->CustomPoseDeltas.Empty();
613 RefHand->bUseCustomPoseDeltas = false;
614 /*TArray<FProperty*> PropertiesToModify;
615 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
616 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
617 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
618 FComponentVisualizer::NotifyPropertiesModified(RefHand, PropertiesToModify);*/
619 }
620 }
621
622 // Modify all of the properties at once
623 TArray<FProperty*> PropertiesToModify;
624 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, HandTargetAnimation)));
625 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, bUseCustomPoseDeltas)));
626 PropertiesToModify.Add(FindFProperty<FProperty>(UHandSocketComponent::StaticClass(), GET_MEMBER_NAME_CHECKED(UHandSocketComponent, CustomPoseDeltas)));
627 FComponentVisualizer::NotifyPropertiesModified(HandSocketComponent.Get(), PropertiesToModify);
628 }
629 }
630 }
631
632 return FReply::Handled();
633}
634
635void SCreateHandAnimationDlg::Construct(const FArguments& InArgs)
636{
637 AssetPath = FText::FromString(FPackageName::GetLongPackagePath(InArgs._DefaultAssetPath.ToString()));
638 AssetName = FText::FromString(FPackageName::GetLongPackageAssetName(InArgs._DefaultAssetPath.ToString()));
639
640 if (AssetPath.IsEmpty())
641 {
643 // still empty?
644 if (AssetPath.IsEmpty())
645 {
646 AssetPath = FText::FromString(TEXT("/Game"));
647 }
648 }
649 else
650 {
652 }
653
654 if (AssetName.IsEmpty())
655 {
656 // find default name for them
657 FAssetToolsModule& AssetToolsModule = FModuleManager::Get().LoadModuleChecked<FAssetToolsModule>("AssetTools");
658 FString OutPackageName, OutAssetName;
659 FString PackageName = AssetPath.ToString() + TEXT("/NewAnimation");
660
661 AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), OutPackageName, OutAssetName);
662 AssetName = FText::FromString(OutAssetName);
663 }
664
665 FPathPickerConfig PathPickerConfig;
666 PathPickerConfig.DefaultPath = AssetPath.ToString();
667 PathPickerConfig.OnPathSelected = FOnPathSelected::CreateSP(this, &SCreateHandAnimationDlg::OnPathChange);
668 PathPickerConfig.bAddDefaultPath = true;
669
670 FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
671
672 SWindow::Construct(SWindow::FArguments()
673 .Title(LOCTEXT("SCreateHandAnimationDlg_Title", "Create New Animation Object"))
674 .SupportsMinimize(false)
675 .SupportsMaximize(false)
676 //.SizingRule( ESizingRule::Autosized )
677 .ClientSize(FVector2D(450, 450))
678 [
679 SNew(SVerticalBox)
680
681 + SVerticalBox::Slot() // Add user input block
682 .Padding(2)
683 [
684 SNew(SBorder)
685 .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder"))
686 [
687 SNew(SVerticalBox)
688
689 + SVerticalBox::Slot()
690 .AutoHeight()
691 [
692 SNew(STextBlock)
693 .Text(LOCTEXT("SelectPath", "Select Path to create animation"))
694 .Font(FCoreStyle::GetDefaultFontStyle("Regular", 14))
695 ]
696
697 + SVerticalBox::Slot()
698 .FillHeight(1)
699 .Padding(3)
700 [
701 ContentBrowserModule.Get().CreatePathPicker(PathPickerConfig)
702 ]
703
704 + SVerticalBox::Slot()
705 .AutoHeight()
706 [
707 SNew(SSeparator)
708 ]
709
710 + SVerticalBox::Slot()
711 .AutoHeight()
712 .Padding(3)
713 [
714 SNew(SHorizontalBox)
715
716 + SHorizontalBox::Slot()
717 .AutoWidth()
718 .Padding(0, 0, 10, 0)
719 .VAlign(VAlign_Center)
720 [
721 SNew(STextBlock)
722 .Text(LOCTEXT("AnimationName", "Animation Name"))
723 ]
724
725 + SHorizontalBox::Slot()
726 [
727 SNew(SEditableTextBox)
728 .Text(AssetName)
729 .OnTextCommitted(this, &SCreateHandAnimationDlg::OnNameChange)
730 .MinDesiredWidth(250)
731 ]
732 ]
733 ]
734 ]
735
736 + SVerticalBox::Slot()
737 .AutoHeight()
738 .HAlign(HAlign_Right)
739 .Padding(5)
740 [
741 SNew(SUniformGridPanel)
742 .SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding"))
743 .MinDesiredSlotWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth"))
744 .MinDesiredSlotHeight(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotHeight"))
745 + SUniformGridPanel::Slot(0, 0)
746 [
747 SNew(SButton)
748 .HAlign(HAlign_Center)
749 .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
750 .Text(LOCTEXT("OK", "OK"))
751 .OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Ok)
752 ]
753 + SUniformGridPanel::Slot(1, 0)
754 [
755 SNew(SButton)
756 .HAlign(HAlign_Center)
757 .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
758 .Text(LOCTEXT("Cancel", "Cancel"))
759 .OnClicked(this, &SCreateHandAnimationDlg::OnButtonClick, EAppReturnType::Cancel)
760 ]
761 ]
762 ]);
763}
764
765void SCreateHandAnimationDlg::OnNameChange(const FText& NewName, ETextCommit::Type CommitInfo)
766{
767 AssetName = NewName;
768}
769
770void SCreateHandAnimationDlg::OnPathChange(const FString& NewPath)
771{
772 AssetPath = FText::FromString(NewPath);
774}
775
776FReply SCreateHandAnimationDlg::OnButtonClick(EAppReturnType::Type ButtonID)
777{
778 UserResponse = ButtonID;
779
780 if (ButtonID != EAppReturnType::Cancel)
781 {
782 if (!ValidatePackage())
783 {
784 // reject the request
785 return FReply::Handled();
786 }
787 }
788
789 RequestDestroyWindow();
790
791 return FReply::Handled();
792}
793
796{
797 FText Reason;
798 FString FullPath = GetFullAssetPath();
799
800 if (!FPackageName::IsValidLongPackageName(FullPath, false, &Reason)
801 || !FName(*AssetName.ToString()).IsValidObjectName(Reason))
802 {
803 FMessageDialog::Open(EAppMsgType::Ok, Reason);
804 return false;
805 }
806
807 return true;
808}
809
811{
812 GEditor->EditorAddModalWindow(SharedThis(this));
813 return UserResponse;
814}
815
817{
818 return AssetPath.ToString();
819}
820
822{
823 return AssetName.ToString();
824}
825
827{
828 return AssetPath.ToString() + "/" + AssetName.ToString();
829}
830
831#undef LOCTEXT_NAMESPACE
static bool PromptUserForAssetPath(FString &AssetPath, FString &AssetName)
void OnLockedStateUpdated(IDetailLayoutBuilder *LayoutBuilder)
void OnHandRelativeUpdated(IDetailLayoutBuilder *LayoutBuilder)
static TSharedRef< IDetailCustomization > MakeInstance()
TWeakObjectPtr< UHandSocketComponent > HandSocketComponent
void OnLeftDominantUpdated(IDetailLayoutBuilder *LayoutBuilder)
void OnUpdateShowMesh(IDetailLayoutBuilder *LayoutBuilder)
TWeakObjectPtr< UAnimSequence > SaveAnimationAsset(const FString &InAssetPath, const FString &InAssetName)
virtual void CustomizeDetails(IDetailLayoutBuilder &DetailBuilder) override
FComponentPropertyPath HandPropertyPath
UPROPERTY()
UHandSocketComponent * GetCurrentlyEditingComponent() const
void Construct(const FArguments &InArgs)
void OnPathChange(const FString &NewPath)
void OnNameChange(const FText &NewName, ETextCommit::Type CommitInfo)
FReply OnButtonClick(EAppReturnType::Type ButtonID)
UCLASS(Blueprintable, ClassGroup = (VRExpansionPlugin), hideCategories = ("Component Tick",...
USTRUCT(BlueprintType, Category = "VRExpansionLibrary")