Files
UnrealEngine/Engine/Source/Editor/PhysicsAssetEditor/Private/PhysicsAssetEditor.cpp
2025-05-18 13:04:45 +08:00

4830 lines
186 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "PhysicsAssetEditor.h"
#include "Components/StaticMeshComponent.h"
#include "DetailLayoutBuilder.h"
#include "Framework/MultiBox/MultiBox.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Framework/Notifications/NotificationManager.h"
#include "EngineGlobals.h"
#include "Engine/SkeletalMesh.h"
#include "Animation/AnimationAsset.h"
#include "Animation/AnimSequence.h"
#include "Engine/StaticMesh.h"
#include "Editor.h"
#include "Misc/MessageDialog.h"
#include "Modules/ModuleManager.h"
#include "Framework/Application/SlateApplication.h"
#include "Widgets/Layout/SSpacer.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Text/SRichTextBlock.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Input/SComboButton.h"
#include "Styling/AppStyle.h"
#include "Preferences/PhysicsAssetEditorOptions.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "PhysicsAssetEditorModule.h"
#include "ScopedTransaction.h"
#include "PhysicsAssetEditorActions.h"
#include "PhysicsAssetEditorSelection.h"
#include "PhysicsAssetRenderUtils.h"
#include "PhysicsAssetEditorSkeletalMeshComponent.h"
#include "PhysicsAssetEditorToolMenuContext.h"
#include "Templates/TypeHash.h"
#include "ToolMenus.h"
#include "PropertyEditorModule.h"
#include "IDetailsView.h"
#include "IContentBrowserSingleton.h"
#include "ContentBrowserModule.h"
#include "WorkflowOrientedApp/SContentReference.h"
#include "MeshUtilities.h"
#include "MeshUtilitiesCommon.h"
#include "EngineAnalytics.h"
#include "AnalyticsEventAttribute.h"
#include "Interfaces/IAnalyticsProvider.h"
#include "Widgets/Docking/SDockTab.h"
#include "PhysicsEngine/ConvexElem.h"
#include "PhysicsEngine/BoxElem.h"
#include "PhysicsEngine/SphereElem.h"
#include "PhysicsEngine/SphylElem.h"
#include "PhysicsEngine/TaperedCapsuleElem.h"
#include "PhysicsEngine/BodySetup.h"
#include "PhysicsEngine/PhysicsConstraintTemplate.h"
#include "PhysicsEngine/ConstraintUtils.h"
#include "PhysicsEngine/PhysicsAsset.h"
#include "PhysicsEngine/SkeletalBodySetup.h"
#include "Engine/Selection.h"
#include "PersonaModule.h"
#include "PersonaToolMenuContext.h"
#include "PhysicsAssetEditorAnimInstance.h"
#include "PhysicsAssetEditorAnimInstanceProxy.h"
#include "PhysicsAssetEditorMode.h"
#include "PersonaModule.h"
#include "IAssetFamily.h"
#include "ISkeletonEditorModule.h"
#include "IPersonaToolkit.h"
#include "IPersonaPreviewScene.h"
#include "PhysicsAssetEditorSkeletonTreeBuilder.h"
#include "BoneProxy.h"
#include "PhysicsAssetGraph/SPhysicsAssetGraph.h"
#include "PhysicsAssetEditorEditMode.h"
#include "AssetEditorModeManager.h"
#include "PhysicsAssetEditorPhysicsHandleComponent.h"
#include "ISkeletonTreeItem.h"
#include "Algo/Transform.h"
#include "SkeletonTreeSelection.h"
#include "SkeletonTreePhysicsBodyItem.h"
#include "SkeletonTreePhysicsShapeItem.h"
#include "SkeletonTreePhysicsConstraintItem.h"
#include "Misc/ScopedSlowTask.h"
#include "PhysicsAssetGenerationSettings.h"
#include "Framework/Commands/GenericCommands.h"
#include "UICommandList_Pinnable.h"
#include "IPinnedCommandList.h"
#include "Widgets/Input/SSpinBox.h"
#include "Widgets/Input/SNumericEntryBox.h"
#include "AnimationEditorPreviewActor.h"
#include "Preferences/PersonaOptions.h"
#include "PhysicsEngine/MLLevelSetModelAndBonesBinningInfo.h"
const FName PhysicsAssetEditorModes::PhysicsAssetEditorMode("PhysicsAssetEditorMode");
const FName PhysicsAssetEditorAppIdentifier = FName(TEXT("PhysicsAssetEditorApp"));
DEFINE_LOG_CATEGORY(LogPhysicsAssetEditor);
#define LOCTEXT_NAMESPACE "PhysicsAssetEditor"
//PRAGMA_DISABLE_OPTIMIZATION
namespace PhysicsAssetEditor
{
const float DefaultPrimSize = 15.0f;
const float DuplicateXOffset = 10.0f;
/** contain everything to identify a shape uniquely - used for synchronizing selection mostly */
struct FShapeData
{
int32 Index;
int32 PrimitiveIndex;
EAggCollisionShape::Type PrimitiveType;
FShapeData(int32 Index, int32 PrimitiveIndex, EAggCollisionShape::Type PrimitiveType)
: Index(Index)
, PrimitiveIndex(PrimitiveIndex)
, PrimitiveType(PrimitiveType)
{
}
bool operator==(const FShapeData& rhs) const
{
return Index == rhs.Index && PrimitiveIndex == rhs.PrimitiveIndex && PrimitiveType == rhs.PrimitiveType;
}
friend uint32 GetTypeHash(const FShapeData& ShapeData)
{
return HashCombine(
HashCombine(
::GetTypeHash(ShapeData.Index),
::GetTypeHash(ShapeData.PrimitiveIndex)),
::GetTypeHash(ShapeData.PrimitiveType));
}
};
static TSharedPtr<FPhysicsAssetEditor> GetPhysicsAssetEditorFromToolContext(const FToolMenuContext& InMenuContext)
{
if (UPhysicsAssetEditorToolMenuContext* Context = InMenuContext.FindContext<UPhysicsAssetEditorToolMenuContext>())
{
return Context->PhysicsAssetEditor.Pin();
}
return TSharedPtr<FPhysicsAssetEditor>();
}
}
void FPhysicsAssetEditor::RegisterTabSpawners(const TSharedRef<class FTabManager>& InTabManager)
{
WorkspaceMenuCategory = InTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_PhysicsAssetEditor", "PhysicsAssetEditor"));
FAssetEditorToolkit::RegisterTabSpawners(InTabManager);
}
FPhysicsAssetEditor::~FPhysicsAssetEditor()
{
if( SharedData->bRunningSimulation )
{
// Disable simulation when shutting down
ImpToggleSimulation();
}
GEditor->UnregisterForUndo(this);
GEditor->GetEditorSubsystem<UImportSubsystem>()->OnAssetReimport.Remove(OnAssetReimportDelegateHandle);
if (PersonaToolkit.IsValid())
{
constexpr bool bSetPreviewMeshInAsset = false;
PersonaToolkit->SetPreviewMesh(nullptr, bSetPreviewMeshInAsset);
}
}
static void FillAddPrimitiveMenu(FMenuBuilder& InSubMenuBuilder)
{
const FPhysicsAssetEditorCommands& PhysicsAssetEditorCommands = FPhysicsAssetEditorCommands::Get();
InSubMenuBuilder.BeginSection("PrimitiveTypeHeader", LOCTEXT("PrimitiveTypeHeader", "Primitive Type"));
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.AddBox );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.AddSphere );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.AddSphyl );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.AddTaperedCapsule );
InSubMenuBuilder.EndSection();
}
static void FillCreateBodiesConstraintsMenu(FMenuBuilder& InSubMenuBuilder)
{
const FPhysicsAssetEditorCommands& PhysicsAssetEditorCommands = FPhysicsAssetEditorCommands::Get();
// Primitive Type Section
{
InSubMenuBuilder.BeginSection("PrimitiveTypeHeader", LOCTEXT("PrimitiveTypeHeader", "Primitive Type"));
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.CreateBodyWithBox );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.CreateBodyWithSphere );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.CreateBodyWithSphyl );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.CreateBodyWithTaperedCapsule );
InSubMenuBuilder.EndSection();
}
// Advanced Section
{
InSubMenuBuilder.BeginSection("AdvancedHeader", LOCTEXT("AdvancedHeader", "Advanced"));
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.CreateBodyShouldCreateConstraints);
InSubMenuBuilder.EndSection();
}
}
void FPhysicsAssetEditor::InitPhysicsAssetEditor(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UPhysicsAsset* ObjectToEdit)
{
SelectedSimulation = false;
SharedData = MakeShareable(new FPhysicsAssetEditorSharedData());
SharedData->SelectionChangedEvent.AddRaw(this, &FPhysicsAssetEditor::HandleViewportSelectionChanged);
SharedData->HierarchyChangedEvent.AddRaw(this, &FPhysicsAssetEditor::RefreshHierachyTree);
SharedData->PreviewChangedEvent.AddRaw(this, &FPhysicsAssetEditor::RefreshPreviewViewport);
SharedData->PhysicsAsset = ObjectToEdit;
SharedData->CachePreviewMesh();
FPersonaToolkitArgs PersonaToolkitArgs;
PersonaToolkitArgs.OnPreviewSceneCreated = FOnPreviewSceneCreated::FDelegate::CreateSP(this, &FPhysicsAssetEditor::HandlePreviewSceneCreated);
PersonaToolkitArgs.OnPreviewSceneSettingsCustomized = FOnPreviewSceneSettingsCustomized::FDelegate::CreateSP(this, &FPhysicsAssetEditor::HandleOnPreviewSceneSettingsCustomized);
PersonaToolkitArgs.bPreviewMeshCanUseDifferentSkeleton = true;
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
PersonaToolkit = PersonaModule.CreatePersonaToolkit(SharedData->PhysicsAsset, PersonaToolkitArgs);
PersonaModule.RecordAssetOpened(FAssetData(ObjectToEdit));
SharedData->InitializeOverlappingBodyPairs();
FSkeletonTreeArgs SkeletonTreeArgs;
SkeletonTreeArgs.OnSelectionChanged = FOnSkeletonTreeSelectionChanged::CreateSP(this, &FPhysicsAssetEditor::HandleSelectionChanged);
SkeletonTreeArgs.PreviewScene = PersonaToolkit->GetPreviewScene();
SkeletonTreeArgs.bShowBlendProfiles = false;
SkeletonTreeArgs.bShowDebugVisualizationOptions = true;
SkeletonTreeArgs.bAllowMeshOperations = false;
SkeletonTreeArgs.bAllowSkeletonOperations = false;
SkeletonTreeArgs.bHideBonesByDefault = true;
SkeletonTreeArgs.OnGetFilterText = FOnGetFilterText::CreateSP(this, &FPhysicsAssetEditor::HandleGetFilterLabel);
SkeletonTreeArgs.Extenders = MakeShared<FExtender>();
SkeletonTreeArgs.Extenders->AddMenuExtension("FilterOptions", EExtensionHook::After, GetToolkitCommands(), FMenuExtensionDelegate::CreateSP(this, &FPhysicsAssetEditor::HandleExtendFilterMenu));
SkeletonTreeArgs.Extenders->AddMenuExtension("SkeletonTreeContextMenu", EExtensionHook::After, GetToolkitCommands(), FMenuExtensionDelegate::CreateSP(this, &FPhysicsAssetEditor::HandleExtendContextMenu));
SkeletonTreeArgs.Extenders->AddMenuExtension("CreateNew", EExtensionHook::After, GetToolkitCommands(), FMenuExtensionDelegate::CreateStatic( &FillAddPrimitiveMenu));
SkeletonTreeArgs.Builder = SkeletonTreeBuilder = MakeShared<FPhysicsAssetEditorSkeletonTreeBuilder>(ObjectToEdit);
SkeletonTreeArgs.ContextName = GetToolkitFName();
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::GetModuleChecked<ISkeletonEditorModule>("SkeletonEditor");
GetMutableDefault<UPersonaOptions>()->bFlattenSkeletonHierarchyWhenFiltering = false;
GetMutableDefault<UPersonaOptions>()->bHideParentsWhenFiltering = true;
SkeletonTree = SkeletonEditorModule.CreateSkeletonTree(PersonaToolkit->GetSkeleton(), SkeletonTreeArgs);
bSelecting = false;
GEditor->RegisterForUndo(this);
// If any assets we care about get reimported, we need to rebuild some stuff
OnAssetReimportDelegateHandle = GEditor->GetEditorSubsystem<UImportSubsystem>()->OnAssetReimport.AddSP(this, &FPhysicsAssetEditor::OnAssetReimport);
// Register our commands. This will only register them if not previously registered
FPhysicsAssetEditorCommands::Register();
BindCommands();
const bool bCreateDefaultStandaloneMenu = true;
const bool bCreateDefaultToolbar = true;
FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, PhysicsAssetEditorAppIdentifier, FTabManager::FLayout::NullLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit);
AddApplicationMode(
PhysicsAssetEditorModes::PhysicsAssetEditorMode,
MakeShareable(new FPhysicsAssetEditorMode(SharedThis(this), SkeletonTree.ToSharedRef(), PersonaToolkit->GetPreviewScene())));
SetCurrentMode(PhysicsAssetEditorModes::PhysicsAssetEditorMode);
// Force disable simulation as InitArticulated can be called during viewport creation
SharedData->EnableSimulation(false);
GetEditorModeManager().SetDefaultMode(FPhysicsAssetEditorEditMode::ModeName);
GetEditorModeManager().ActivateMode(FPersonaEditModes::SkeletonSelection);
GetEditorModeManager().ActivateMode(FPhysicsAssetEditorEditMode::ModeName);
static_cast<FPhysicsAssetEditorEditMode*>(GetEditorModeManager().GetActiveMode(FPhysicsAssetEditorEditMode::ModeName))->SetSharedData(SharedThis(this), *SharedData.Get());
IPhysicsAssetEditorModule* PhysicsAssetEditorModule = &FModuleManager::LoadModuleChecked<IPhysicsAssetEditorModule>( "PhysicsAssetEditor" );
ExtendMenu();
ExtendToolbar();
ExtendViewportMenus();
RegenerateMenusAndToolbars();
}
TSharedPtr<FPhysicsAssetEditorSharedData> FPhysicsAssetEditor::GetSharedData() const
{
return SharedData;
}
void FPhysicsAssetEditor::HandleViewportSelectionChanged(const TArray<FPhysicsAssetEditorSelectedElement>& InSelectedElements)
{
if (!bSelecting)
{
TGuardValue<bool> RecursionGuard(bSelecting, true);
if (SkeletonTree.IsValid())
{
SkeletonTree->DeselectAll();
}
if(InSelectedElements.IsEmpty())
{
if (PhysAssetProperties.IsValid())
{
PhysAssetProperties->SetObject(nullptr);
}
if (PhysicsAssetGraph.IsValid())
{
PhysicsAssetGraph.Pin()->SelectObjects(TArray<USkeletalBodySetup*>(), TArray<UPhysicsConstraintTemplate*>());
}
}
else
{
// let's store all the selection in sets so that when we go through the list of items in the list
// ( which can be long ) we only do O(1) lookup for each of them
TSet<UObject*> Objects;
TSet<USkeletalBodySetup*> Bodies;
TSet<UPhysicsConstraintTemplate*> Constraints;
TSet<PhysicsAssetEditor::FShapeData> Shapes;
for (const FPhysicsAssetEditorSelectedElement& SelectedElement : InSelectedElements)
{
if (SelectedElement.HasType(FPhysicsAssetEditorSelectedElement::Body | FPhysicsAssetEditorSelectedElement::Primitive))
{
USkeletalBodySetup* const SelectedBodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedElement.GetIndex()];
Bodies.Add(SelectedBodySetup);
Shapes.Add(PhysicsAssetEditor::FShapeData(SelectedElement.GetIndex(), SelectedElement.GetPrimitiveIndex(), SelectedElement.GetPrimitiveType()));
Objects.Add(SelectedBodySetup);
}
else if (SelectedElement.HasType(FPhysicsAssetEditorSelectedElement::Constraint))
{
UPhysicsConstraintTemplate* const SelectedConstraint = SharedData->PhysicsAsset->ConstraintSetup[SelectedElement.GetIndex()];
Constraints.Add(SelectedConstraint);
Shapes.Add(PhysicsAssetEditor::FShapeData(SelectedElement.GetIndex(), SelectedElement.GetPrimitiveIndex(), SelectedElement.GetPrimitiveType()));
Objects.Add(SelectedConstraint);
}
else if (SelectedElement.HasType(FPhysicsAssetEditorSelectedElement::CenterOfMass))
{
USkeletalBodySetup* const SelectedBodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedElement.GetIndex()];
Objects.Add(SelectedBodySetup); // Add the owning physics body here so we display its details panel in the UI.
}
}
if (PhysAssetProperties.IsValid())
{
PhysAssetProperties->SetObjects(Objects.Array());
}
if (SkeletonTree.IsValid())
{
SkeletonTree->SelectItemsBy([this, &Objects, &Constraints, &Bodies, &Shapes](const TSharedRef<ISkeletonTreeItem>& InItem, bool& bInOutExpand)
{
if (InItem->IsOfType<FSkeletonTreePhysicsBodyItem>())
{
const USkeletalBodySetup* BodySetup = Cast<USkeletalBodySetup>(InItem->GetObject());
if (Bodies.Contains(BodySetup))
{
bInOutExpand = true;
return true;
}
}
else if (InItem->IsOfType<FSkeletonTreePhysicsShapeItem>())
{
TSharedRef<FSkeletonTreePhysicsShapeItem> ShapeItem = StaticCastSharedRef<FSkeletonTreePhysicsShapeItem>(InItem);
PhysicsAssetEditor::FShapeData ShapeData(ShapeItem->GetBodySetupIndex(), ShapeItem->GetShapeIndex(), ShapeItem->GetShapeType());
if (Shapes.Contains(ShapeData))
{
bInOutExpand = true;
return true;
}
}
else if (InItem->IsOfType<FSkeletonTreePhysicsConstraintItem>())
{
const UPhysicsConstraintTemplate* Constraint = Cast<UPhysicsConstraintTemplate>(InItem->GetObject());
if (Constraints.Contains(Constraint))
{
bInOutExpand = true;
return true;
}
}
return false;
},
ESelectInfo::OnMouseClick);
}
if (PhysicsAssetGraph.IsValid())
{
PhysicsAssetGraph.Pin()->SelectObjects(Bodies.Array(), Constraints.Array());
}
}
}
}
void FPhysicsAssetEditor::RefreshHierachyTree()
{
if(SkeletonTree.IsValid())
{
SkeletonTree->Refresh();
}
}
void FPhysicsAssetEditor::RefreshPreviewViewport()
{
if (PersonaToolkit.IsValid())
{
PersonaToolkit->GetPreviewScene()->InvalidateViews();
}
}
FName FPhysicsAssetEditor::GetToolkitFName() const
{
return FName("PhysicsAssetEditor");
}
FText FPhysicsAssetEditor::GetBaseToolkitName() const
{
return LOCTEXT("AppLabel", "Physics Asset Editor");
}
FString FPhysicsAssetEditor::GetWorldCentricTabPrefix() const
{
return LOCTEXT( "WorldCentricTabPrefix", "Physics Asset Editor ").ToString();
}
FLinearColor FPhysicsAssetEditor::GetWorldCentricTabColorScale() const
{
return FLinearColor(0.3f, 0.2f, 0.5f, 0.5f);
}
void FPhysicsAssetEditor::InitToolMenuContext(FToolMenuContext& MenuContext)
{
FAssetEditorToolkit::InitToolMenuContext(MenuContext);
UPhysicsAssetEditorToolMenuContext* PhysicsAssetEditorContext = NewObject<UPhysicsAssetEditorToolMenuContext>();
PhysicsAssetEditorContext->PhysicsAssetEditor = SharedThis(this);
MenuContext.AddObject(PhysicsAssetEditorContext);
UPersonaToolMenuContext* PersonaContext = NewObject<UPersonaToolMenuContext>();
PersonaContext->SetToolkit(GetPersonaToolkit());
MenuContext.AddObject(PersonaContext);
MenuContext.AppendCommandList(ViewportCommandList);
}
void FPhysicsAssetEditor::OnClose()
{
// Clear render settings from editor viewport. These settings must be applied to the rendering in all editors
// when an asset is open in the Physics Asset Editor but should not persist after the editor has been closed.
if (FPhysicsAssetRenderSettings* const RenderSettings = UPhysicsAssetRenderUtilities::GetSettings(SharedData->PhysicsAsset))
{
RenderSettings->ResetEditorViewportOptions();
}
if (UPhysicsAssetRenderUtilities* PhysicsAssetRenderUtilities = GetMutableDefault<UPhysicsAssetRenderUtilities>())
{
PhysicsAssetRenderUtilities->SaveConfig();
}
IPhysicsAssetEditor::OnClose();
}
void FPhysicsAssetEditor::AddReferencedObjects(FReferenceCollector& Collector)
{
SharedData->AddReferencedObjects(Collector);
}
void FPhysicsAssetEditor::PostUndo(bool bSuccess)
{
OnPostUndo.Broadcast();
SharedData->PostUndo();
RefreshHierachyTree();
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
}
void FPhysicsAssetEditor::PostRedo( bool bSuccess )
{
OnPostUndo.Broadcast();
for (int32 BodyIdx=0; BodyIdx < SharedData->PhysicsAsset->SkeletalBodySetups.Num(); ++BodyIdx)
{
UBodySetup* Body = SharedData->PhysicsAsset->SkeletalBodySetups[BodyIdx];
bool bRecreate = false;
for (int32 ElemIdx=0; ElemIdx < Body->AggGeom.ConvexElems.Num(); ++ElemIdx)
{
FKConvexElem& Element = Body->AggGeom.ConvexElems[ElemIdx];
if (Element.GetChaosConvexMesh() == NULL)
{
bRecreate = true;
break;
}
}
if (bRecreate)
{
Body->InvalidatePhysicsData();
Body->CreatePhysicsMeshes();
}
}
PostUndo(bSuccess);
}
void FPhysicsAssetEditor::OnAssetReimport(UObject* Object)
{
RecreatePhysicsState();
RefreshHierachyTree();
RefreshPreviewViewport();
if (SharedData->EditorSkelComp && SharedData->EditorSkelComp->GetSkeletalMeshAsset())
{
IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities");
// Update various infos based on the mesh
MeshUtilities.CalcBoneVertInfos(SharedData->EditorSkelComp->GetSkeletalMeshAsset(), SharedData->DominantWeightBoneInfos, true);
MeshUtilities.CalcBoneVertInfos(SharedData->EditorSkelComp->GetSkeletalMeshAsset(), SharedData->AnyWeightBoneInfos, false);
}
}
void FPhysicsAssetEditor::OnFinishedChangingProperties(const FPropertyChangedEvent& PropertyChangedEvent)
{
// if simulating ignore update request
if (SharedData->bRunningSimulation)
{
return;
}
FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
// Update bounds bodies and setup when bConsiderForBounds was changed
if (PropertyName == GET_MEMBER_NAME_CHECKED(UBodySetup, bConsiderForBounds))
{
SharedData->PhysicsAsset->UpdateBoundsBodiesArray();
SharedData->PhysicsAsset->UpdateBodySetupIndexMap();
}
// if we updated the array of shapes we should make sure we update the selection
if (PropertyName == GET_MEMBER_NAME_CHECKED(UBodySetup, AggGeom))
{
// reselect all the bodies that were selected when the array changed, because selection keeps a primitive type that may have changed since
TArray<FPhysicsAssetEditorSharedData::FSelection> ReselectedPrimitives;
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedPrimitive : SharedData->SelectedPrimitives())
{
ReselectedPrimitives.AddUnique(MakeSelectionAnyPrimitiveInBody(SharedData->PhysicsAsset, SelectedPrimitive.Index));
}
{ // bulk update
FScopedBulkSelection BulkSelection(SharedData);
SharedData->SetSelectedPrimitives(ReselectedPrimitives);
}
}
if (UPhysicsAssetRenderUtilities* PhysicsAssetRenderUtilities = GetMutableDefault<UPhysicsAssetRenderUtilities>())
{
PhysicsAssetRenderUtilities->SaveConfig();
}
RecreatePhysicsState();
RefreshPreviewViewport();
}
FText FPhysicsAssetEditor::GetRepeatLastSimulationToolTip() const
{
if(SelectedSimulation)
{
return FPhysicsAssetEditorCommands::Get().SelectedSimulation->GetDescription();
}
else
{
if(SharedData->bNoGravitySimulation)
{
return FPhysicsAssetEditorCommands::Get().SimulationNoGravity->GetDescription();
}
else
{
return FPhysicsAssetEditorCommands::Get().SimulationAll->GetDescription();
}
}
}
FSlateIcon FPhysicsAssetEditor::GetRepeatLastSimulationIcon() const
{
if(SelectedSimulation)
{
return FPhysicsAssetEditorCommands::Get().SelectedSimulation->GetIcon();
}
else
{
if(SharedData->bNoGravitySimulation)
{
return FPhysicsAssetEditorCommands::Get().SimulationNoGravity->GetIcon();
}
else
{
return FPhysicsAssetEditorCommands::Get().SimulationAll->GetIcon();
}
}
}
void FPhysicsAssetEditor::BuildMenuWidgetBone(FMenuBuilder& InMenuBuilder, const TArray<TSharedPtr<ISkeletonTreeItem>>& SelectedBones)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
TSharedPtr< const FUICommandInfo > MenuEntryCommand = Commands.CreateOrRegenerateBodies;
// Determine whether any or all selected bones have child bodies.
uint32 WithBodyBoneCount = 0;
uint32 SelectedBoneCount = 0;
for (const TSharedPtr<ISkeletonTreeItem>& SkeletonTreeItem : SelectedBones)
{
++SelectedBoneCount;
for (const TSharedPtr<ISkeletonTreeItem>& ChildTreeItem : SkeletonTreeItem->GetChildren())
{
if (ChildTreeItem->IsOfType<FSkeletonTreePhysicsBodyItem>())
{
++WithBodyBoneCount;
break;
}
}
}
static const FText AddPrimitiveMenuText = LOCTEXT("AddPrimitiveMenu", "Add Primitive");
static const FText CreateBodiesConstraintsMenuText = LOCTEXT("CreateBodiesConstraintsMenu", "Create Bodies / Constraints");
// Build sub menu.
InMenuBuilder.PushCommandList(GetToolkitCommands());
InMenuBuilder.BeginSection("BodyActions", LOCTEXT("BodyHeader", "Body"));
// Find the most appropriate menu entry for the selected bones.
if (WithBodyBoneCount == 0)
{
// None of the selected bones have bodies - show the sub menu to create new bodies using a specified primitive with or without constraints.
InMenuBuilder.AddSubMenu(CreateBodiesConstraintsMenuText, LOCTEXT("CreateBodiesConstraintsMenu_ToolTip", "Create new bodies and potentially constraints for the selected bones."),
FNewMenuDelegate::CreateStatic(&FillCreateBodiesConstraintsMenu));
}
else if(WithBodyBoneCount == SelectedBoneCount)
{
// All of the selected bones have bodies - show the options to regenerate bodies and add primitives.
InMenuBuilder.AddMenuEntry(Commands.RegenerateBodies);
InMenuBuilder.AddSubMenu(AddPrimitiveMenuText, LOCTEXT("AddPrimitiveMenu_ToolTip", "Add Primitives to this body"),
FNewMenuDelegate::CreateStatic(&FillAddPrimitiveMenu));
}
else
{
// Some selected bones have bodies, some do not - gray out all the options and use tool tips to explain why.
const FText InconsistentSelectionForNoBodyCommandsToolTip = LOCTEXT("InconsistentBoneSelectionForNoBodyCommandsToolTip", "This option is only available when none of the selected bones have an associated body.");
const FText InconsistentSelectionForWithBodyCommandsToolTip = LOCTEXT("InconsistentBoneSelectionForWithBodyCommandsToolTip", "This option is only available when all of the selected bones have an associated body.");
const TSharedRef< SWidget > CreateBodiesConstraintsWidget =
SNew(STextBlock)
.Text(CreateBodiesConstraintsMenuText)
.TextStyle(FAppStyle::Get(), "NormalText.Subdued")
.ToolTipText(InconsistentSelectionForNoBodyCommandsToolTip);
const TSharedRef< SWidget > AddPrimitiveWidget =
SNew(STextBlock)
.Text(AddPrimitiveMenuText)
.TextStyle(FAppStyle::Get(), "NormalText.Subdued")
.ToolTipText(InconsistentSelectionForWithBodyCommandsToolTip);
const TSharedRef< SWidget > RegenerateBodiesWidget =
SNew(STextBlock)
.Text(Commands.RegenerateBodies->GetLabel())
.TextStyle(FAppStyle::Get(), "NormalText.Subdued")
.ToolTipText(InconsistentSelectionForWithBodyCommandsToolTip);
// Add non-functional menu entries that appear grayed-out.
InMenuBuilder.AddMenuEntry(FUIAction(), RegenerateBodiesWidget);
InMenuBuilder.AddMenuEntry(FUIAction(), AddPrimitiveWidget);
InMenuBuilder.AddMenuEntry(FUIAction(), CreateBodiesConstraintsWidget);
}
InMenuBuilder.EndSection();
AddAdvancedMenuWidget(InMenuBuilder);
InMenuBuilder.PopCommandList();
}
void FPhysicsAssetEditor::ExtendToolbar()
{
struct Local
{
static TSharedRef< SWidget > FillSimulateOptions(TSharedRef<FUICommandList> InCommandList)
{
const bool bShouldCloseWindowAfterMenuSelection = true;
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList );
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
//selected simulation
MenuBuilder.BeginSection("Simulation", LOCTEXT("SimulationHeader", "Simulation"));
{
MenuBuilder.AddMenuEntry(Commands.SimulationAll);
MenuBuilder.AddMenuEntry(Commands.SelectedSimulation);
}
MenuBuilder.EndSection();
MenuBuilder.BeginSection("SimulationOptions", LOCTEXT("SimulationOptionsHeader", "Simulation Options"));
{
MenuBuilder.AddMenuEntry(Commands.SimulationNoGravity);
MenuBuilder.AddMenuEntry(Commands.SimulationFloorCollision);
}
MenuBuilder.EndSection();
return MenuBuilder.MakeWidget();
}
};
FName ParentName;
static const FName MenuName = GetToolMenuToolbarName(ParentName);
UToolMenu* ToolMenu = UToolMenus::Get()->ExtendMenu(MenuName);
const FToolMenuInsert SectionInsertLocation("Asset", EToolMenuInsertType::After);
ToolMenu->AddDynamicSection("Persona", FNewToolMenuDelegate::CreateLambda([](UToolMenu* InToolMenu)
{
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
FPersonaModule::FCommonToolbarExtensionArgs Args;
Args.bReferencePose = true;
PersonaModule.AddCommonToolbarExtensions(InToolMenu, Args);
}), SectionInsertLocation);
ToolMenu->AddDynamicSection("BodyTools", FNewToolMenuDelegate::CreateLambda([](UToolMenu* InToolMenu)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
TSharedPtr<FPhysicsAssetEditor> PhysicsAssetEditor = PhysicsAssetEditor::GetPhysicsAssetEditorFromToolContext(InToolMenu->Context);
if (PhysicsAssetEditor)
{
FToolMenuSection& Section = InToolMenu->AddSection("BodyTools", FText());
Section.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.EnableCollision));
Section.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.DisableCollision));
Section.AddEntry(FToolMenuEntry::InitComboButton("ApplyPhysicalMaterial",
FUIAction(FExecuteAction(), FCanExecuteAction::CreateSP(PhysicsAssetEditor.Get(), &FPhysicsAssetEditor::IsNotSimulation)),
FOnGetContent::CreateLambda([WeakPhysicsAssetEditor = PhysicsAssetEditor.ToWeakPtr()]()
{
return WeakPhysicsAssetEditor.Pin()->BuildPhysicalMaterialAssetPicker(true);
}),
Commands.ApplyPhysicalMaterial->GetLabel(),
Commands.ApplyPhysicalMaterial->GetDescription(),
Commands.ApplyPhysicalMaterial->GetIcon()));
}
}), SectionInsertLocation);
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
FToolMenuSection& Section = ToolMenu->AddSection("ConstraintTools", FText(), SectionInsertLocation);
Section.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.ConvertToBallAndSocket));
Section.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.ConvertToHinge));
Section.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.ConvertToPrismatic));
Section.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.ConvertToSkeletal));
}
ToolMenu->AddDynamicSection("Simulation", FNewToolMenuDelegate::CreateLambda([](UToolMenu* InToolMenu)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
TSharedPtr<FPhysicsAssetEditor> PhysicsAssetEditor = PhysicsAssetEditor::GetPhysicsAssetEditorFromToolContext(InToolMenu->Context);
if (PhysicsAssetEditor)
{
FToolMenuSection& Section = InToolMenu->AddSection("Simulation", FText());
// Simulate
Section.AddEntry(FToolMenuEntry::InitToolBarButton(
Commands.RepeatLastSimulation,
LOCTEXT("RepeatLastSimulation", "Simulate"),
TAttribute< FText >::Create(TAttribute< FText >::FGetter::CreateSP(PhysicsAssetEditor.Get(), &FPhysicsAssetEditor::GetRepeatLastSimulationToolTip)),
TAttribute< FSlateIcon >::Create(TAttribute< FSlateIcon >::FGetter::CreateSP(PhysicsAssetEditor.Get(), &FPhysicsAssetEditor::GetRepeatLastSimulationIcon))));
Section.AddEntry(FToolMenuEntry::InitComboButton("SimulationMode",
FUIAction(FExecuteAction(), FCanExecuteAction::CreateSP(PhysicsAssetEditor.Get(), &FPhysicsAssetEditor::IsNotSimulation)),
FOnGetContent::CreateLambda([WeakPhysicsAssetEditor = PhysicsAssetEditor.ToWeakPtr()]()
{
return Local::FillSimulateOptions(WeakPhysicsAssetEditor.Pin()->GetToolkitCommands());
}),
LOCTEXT("SimulateCombo_Label", "Simulate Options"),
LOCTEXT("SimulateComboToolTip", "Options for Simulation"),
FSlateIcon(),
true));
}
}), SectionInsertLocation);
// If the ToolbarExtender is valid, remove it before rebuilding it
if ( ToolbarExtender.IsValid() )
{
RemoveToolbarExtender( ToolbarExtender );
ToolbarExtender.Reset();
}
ToolbarExtender = MakeShareable(new FExtender);
AddToolbarExtender(ToolbarExtender);
IPhysicsAssetEditorModule* PhysicsAssetEditorModule = &FModuleManager::LoadModuleChecked<IPhysicsAssetEditorModule>( "PhysicsAssetEditor" );
AddToolbarExtender(PhysicsAssetEditorModule->GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
ToolbarExtender->AddToolBarExtension(
"Asset",
EExtensionHook::After,
GetToolkitCommands(),
FToolBarExtensionDelegate::CreateLambda([this](FToolBarBuilder& ParentToolbarBuilder)
{
FPersonaModule& PersonaModule = FModuleManager::LoadModuleChecked<FPersonaModule>("Persona");
TSharedRef<class IAssetFamily> AssetFamily = PersonaModule.CreatePersonaAssetFamily(SharedData->PhysicsAsset);
AddToolbarWidget(PersonaModule.CreateAssetFamilyShortcutWidget(SharedThis(this), AssetFamily));
}
));
}
void FPhysicsAssetEditor::ExtendMenu()
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
MenuExtender = MakeShareable(new FExtender);
AddMenuExtender(MenuExtender);
IPhysicsAssetEditorModule* PhysicsAssetEditorModule = &FModuleManager::LoadModuleChecked<IPhysicsAssetEditorModule>( "PhysicsAssetEditor" );
AddMenuExtender(PhysicsAssetEditorModule->GetMenuExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
FToolMenuOwnerScoped OwnerScoped(this);
static const FName EditMenuName = UToolMenus::JoinMenuPaths(GetToolMenuName(), TEXT("Edit"));
if (UToolMenu* EditMenu = UToolMenus::Get()->ExtendMenu(EditMenuName))
{
{
FToolMenuSection& Section = EditMenu->AddSection("Selection", LOCTEXT("PhatEditSelection", "Selection"));
Section.AddMenuEntry(Commands.ShowSelected);
Section.AddMenuEntry(Commands.HideSelected);
Section.AddMenuEntry(Commands.ToggleShowOnlySelected);
Section.AddMenuEntry(Commands.ToggleShowOnlyColliding);
Section.AddMenuEntry(Commands.ToggleShowOnlyConstrained);
Section.AddMenuEntry(Commands.ShowAll);
Section.AddMenuEntry(Commands.HideAll);
Section.AddMenuEntry(Commands.DeselectAll);
Section.AddMenuEntry(Commands.ToggleShowSelected);
}
{
FToolMenuSection& Section = EditMenu->AddSection("Bodies & Constraints", LOCTEXT("PhatEditSelectionBodies", "Bodies & Constraints"));
Section.AddMenuEntry(Commands.SelectAllBodies);
Section.AddMenuEntry(Commands.SelectSimulatedBodies);
Section.AddMenuEntry(Commands.SelectKinematicBodies);
Section.AddMenuEntry(Commands.SelectAllConstraints);
Section.AddMenuEntry(Commands.ToggleSelectionType);
Section.AddMenuEntry(Commands.ToggleSelectionTypeWithUserConstraints);
Section.AddMenuEntry(Commands.GenerateSkinnedTriangleMesh);
}
{
FToolMenuSection& Section = EditMenu->AddSection("Shapes", LOCTEXT("PhatEditSelectionShapes", "Shapes"));
Section.AddMenuEntry(Commands.SelectShapesQueryOnly);
Section.AddMenuEntry(Commands.SelectShapesQueryAndPhysics);
Section.AddMenuEntry(Commands.SelectShapesPhysicsOnly);
Section.AddMenuEntry(Commands.SelectShapesQueryAndProbe);
Section.AddMenuEntry(Commands.SelectShapesProbeOnly);
}
if(bEnableMLLevelSet)
{
FToolMenuSection& Section = EditMenu->AddSection("Import", LOCTEXT("PhatEditImport", "Import"));
Section.AddMenuEntry(Commands.ImportMLLevelSet);
}
}
}
void FPhysicsAssetEditor::ExtendViewportMenus()
{
auto ExtendMenuWithPhysicsRenderingSection = [MenuOwner = this](FName InMenuName) -> void
{
FToolMenuOwnerScoped OwnerScoped(MenuOwner);
UToolMenu* ExtendableCharacterMenu = UToolMenus::Get()->ExtendMenu(InMenuName);
ExtendableCharacterMenu->AddDynamicSection(
"PhysicsCharacterMenu",
FNewToolMenuDelegate::CreateLambda(
[](UToolMenu* CharacterMenu)
{
TSharedPtr<FPhysicsAssetEditor> PhysicsAssetEditor =
PhysicsAssetEditor::GetPhysicsAssetEditorFromToolContext(CharacterMenu->Context);
if (PhysicsAssetEditor)
{
FToolMenuSection& Section = CharacterMenu->AddSection(
"PhysicsAssetShowCommands",
LOCTEXT("PhysicsShowCommands", "Physics Rendering"),
FToolMenuInsert("AnimViewportSceneElements", EToolMenuInsertType::Before)
);
Section.AddSubMenu(
TEXT("MassPropertiesSubMenu"),
LOCTEXT("MassPropertiesSubMenu", "Mass Properties"),
FText::GetEmpty(),
FNewToolMenuDelegate::CreateLambda(
[WeakPhysicsAssetEditor = PhysicsAssetEditor.ToWeakPtr()](UToolMenu* InSubMenu)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorCenterOfMassRenderSettings",
LOCTEXT("CenterOfMassRenderSettingsHeader", "Center of Mass Drawing")
);
Section.AddMenuEntry(Commands.DrawBodyMass);
Section.AddMenuEntry(Commands.HideCenterOfMassForKinematicBodies);
Section.AddEntry(FToolMenuEntry::InitWidget(
TEXT("CoMMarkerScale"),
WeakPhysicsAssetEditor.Pin()->MakeCoMMarkerScaleWidget(),
LOCTEXT("CoMMarkerScaleLabel", "Marker Scale")
));
}
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorCenterOfMassRenderingMode",
LOCTEXT("CenterOfMassRenderingModeHeader", "Center of Mass Drawing (Edit)")
);
Section.AddMenuEntry(Commands.CenterOfMassRenderingMode_All);
Section.AddMenuEntry(Commands.CenterOfMassRenderingMode_Selected);
Section.AddMenuEntry(Commands.CenterOfMassRenderingMode_None);
}
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorCenterOfMassRenderingModeSim",
LOCTEXT("CenterOfMassRenderingModeSimHeader", "Center of Mass Drawing (Simulation)")
);
Section.AddMenuEntry(Commands.CenterOfMassRenderingMode_Simulation_All);
Section.AddMenuEntry(Commands.CenterOfMassRenderingMode_Simulation_Selected);
Section.AddMenuEntry(Commands.CenterOfMassRenderingMode_Simulation_None);
}
}
)
);
Section.AddSubMenu(
TEXT("MeshRenderModeSubMenu"),
LOCTEXT("MeshRenderModeSubMenu", "Mesh"),
FText::GetEmpty(),
FNewToolMenuDelegate::CreateLambda(
[](UToolMenu* InSubMenu)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorRenderingMode",
LOCTEXT("MeshRenderModeHeader", "Mesh Drawing (Edit)")
);
Section.AddMenuEntry(Commands.MeshRenderingMode_Solid);
Section.AddMenuEntry(Commands.MeshRenderingMode_Wireframe);
Section.AddMenuEntry(Commands.MeshRenderingMode_None);
}
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorRenderingModeSim",
LOCTEXT("MeshRenderModeSimHeader", "Mesh Drawing (Simulation)")
);
Section.AddMenuEntry(Commands.MeshRenderingMode_Simulation_Solid);
Section.AddMenuEntry(Commands.MeshRenderingMode_Simulation_Wireframe);
Section.AddMenuEntry(Commands.MeshRenderingMode_Simulation_None);
}
}
)
);
Section.AddSubMenu(
TEXT("CollisionRenderModeSubMenu"),
LOCTEXT("CollisionRenderModeSubMenu", "Bodies"),
FText::GetEmpty(),
FNewToolMenuDelegate::CreateLambda(
[WeakPhysicsAssetEditor = PhysicsAssetEditor.ToWeakPtr()](UToolMenu* InSubMenu)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorCollisionRenderSettings",
LOCTEXT("CollisionRenderSettingsHeader", "Body Drawing")
);
Section.AddMenuEntry(Commands.RenderOnlySelectedSolid);
Section.AddMenuEntry(Commands.HideSimulatedBodies);
Section.AddMenuEntry(Commands.HideKinematicBodies);
Section.AddEntry(FToolMenuEntry::InitWidget(
TEXT("CollisionOpacity"),
WeakPhysicsAssetEditor.Pin()->MakeCollisionOpacityWidget(),
LOCTEXT("CollisionOpacityLabel", "Collision Opacity")
));
}
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorCollisionMode",
LOCTEXT("CollisionRenderModeHeader", "Body Drawing (Edit)")
);
Section.AddMenuEntry(Commands.CollisionRenderingMode_Solid);
Section.AddMenuEntry(Commands.CollisionRenderingMode_Wireframe);
Section.AddMenuEntry(Commands.CollisionRenderingMode_SolidWireframe);
Section.AddMenuEntry(Commands.CollisionRenderingMode_None);
Section.AddMenuEntry(Commands.HighlightOverlappingBodies);
}
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorCollisionModeSim",
LOCTEXT("CollisionRenderModeSimHeader", "Body Drawing (Simulation)")
);
Section.AddMenuEntry(Commands.CollisionRenderingMode_Simulation_Solid);
Section.AddMenuEntry(Commands.CollisionRenderingMode_Simulation_Wireframe);
Section.AddMenuEntry(Commands.CollisionRenderingMode_Simulation_SolidWireframe);
Section.AddMenuEntry(Commands.CollisionRenderingMode_Simulation_None);
}
}
)
);
Section.AddSubMenu(
TEXT("ConstraintConstraintModeSubMenu"),
LOCTEXT("ConstraintConstraintModeSubMenu", "Constraints"),
FText::GetEmpty(),
FNewToolMenuDelegate::CreateLambda(
[WeakPhysicsAssetEditor = PhysicsAssetEditor.ToWeakPtr()](UToolMenu* InSubMenu)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorConstraints", LOCTEXT("ConstraintHeader", "Constraints")
);
Section.AddMenuEntry(Commands.DrawConstraintsAsPoints);
Section.AddMenuEntry(Commands.DrawViolatedLimits);
Section.AddMenuEntry(Commands.RenderOnlySelectedConstraints);
Section.AddEntry(FToolMenuEntry::InitWidget(
TEXT("ConstraintScale"),
WeakPhysicsAssetEditor.Pin()->MakeConstraintScaleWidget(),
LOCTEXT("ConstraintScaleLabel", "Constraint Scale")
));
}
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorConstraintMode",
LOCTEXT("ConstraintRenderModeHeader", "Constraint Drawing (Edit)")
);
Section.AddMenuEntry(Commands.ConstraintRenderingMode_None);
Section.AddMenuEntry(Commands.ConstraintRenderingMode_AllPositions);
Section.AddMenuEntry(Commands.ConstraintRenderingMode_AllLimits);
}
{
FToolMenuSection& Section = InSubMenu->AddSection(
"PhysicsAssetEditorConstraintModeSim",
LOCTEXT("ConstraintRenderModeSimHeader", "Constraint Drawing (Simulation)")
);
Section.AddMenuEntry(Commands.ConstraintRenderingMode_Simulation_None);
Section.AddMenuEntry(Commands.ConstraintRenderingMode_Simulation_AllPositions);
Section.AddMenuEntry(Commands.ConstraintRenderingMode_Simulation_AllLimits);
}
}
)
);
}
}
)
);
};
// Extend the old viewport toolbar "Character" menu.
ExtendMenuWithPhysicsRenderingSection("Persona.AnimViewportCharacterMenu");
// Extend the new viewport toolbar "Show" menu.
ExtendMenuWithPhysicsRenderingSection("AnimationEditor.ViewportToolbar.Show");
static const FName PhysicsMenuName("Persona.AnimViewportPhysicsMenu");
UToolMenu* ExtendablePhysicsMenu = UToolMenus::Get()->ExtendMenu(PhysicsMenuName);
ExtendablePhysicsMenu->AddDynamicSection("AnimViewportPhysicsMenu", FNewToolMenuDelegate::CreateLambda([](UToolMenu* PhysicsMenu)
{
TSharedPtr<FPhysicsAssetEditor> PhysicsAssetEditor = PhysicsAssetEditor::GetPhysicsAssetEditorFromToolContext(PhysicsMenu->Context);
if (PhysicsAssetEditor)
{
FToolMenuSection& Section = PhysicsMenu->AddSection("AnimViewportPhysicsMenu", LOCTEXT("ViewMenu_AnimViewportPhysicsMenu", "Physics Menu"));
FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
FDetailsViewArgs DetailsViewArgs;
DetailsViewArgs.bAllowSearch = false;
DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea;
TSharedPtr<IDetailsView> DetailsView = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
DetailsView->SetObject(PhysicsAssetEditor->GetSharedData()->EditorOptions);
DetailsView->OnFinishedChangingProperties().AddLambda([WeakPhysicsAssetEditor = PhysicsAssetEditor.ToWeakPtr()](const FPropertyChangedEvent& InEvent) { WeakPhysicsAssetEditor.Pin()->GetSharedData()->EditorOptions->SaveConfig(); });
Section.AddEntry(FToolMenuEntry::InitWidget("PhysicsEditorOptions", DetailsView.ToSharedRef(), FText()));
}
}));
}
void FPhysicsAssetEditor::BindCommands()
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
ToolkitCommands->MapAction(
Commands.RegenerateBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ResetBoneCollision),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CreateBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ResetBoneCollision),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CreateBodyWithBox,
FExecuteAction::CreateSPLambda(this, [this]() { this->CreateBodiesAndConstraintsForSelectedBones(EAggCollisionShape::Box, this->ShouldCreateConstraintsWhenCreatingBodies()); }),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CreateBodyWithSphere,
FExecuteAction::CreateSPLambda(this, [this]() { this->CreateBodiesAndConstraintsForSelectedBones(EAggCollisionShape::Sphere, this->ShouldCreateConstraintsWhenCreatingBodies()); }),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CreateBodyWithSphyl,
FExecuteAction::CreateSPLambda(this, [this]() { this->CreateBodiesAndConstraintsForSelectedBones(EAggCollisionShape::Sphyl, this->ShouldCreateConstraintsWhenCreatingBodies()); }),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CreateBodyWithTaperedCapsule,
FExecuteAction::CreateSPLambda(this, [this]() { this->CreateBodiesAndConstraintsForSelectedBones(EAggCollisionShape::TaperedCapsule, this->ShouldCreateConstraintsWhenCreatingBodies()); }),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CreateBodyShouldCreateConstraints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleCreateConstraintsWhenCreatingBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::ShouldCreateConstraintsWhenCreatingBodies));
ToolkitCommands->MapAction(
Commands.CreateOrRegenerateBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ResetBoneCollision),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CopyProperties,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCopyProperties),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanCopyProperties),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCopyProperties));
ToolkitCommands->MapAction(
Commands.PasteProperties,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnPasteProperties),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanPasteProperties));
ToolkitCommands->MapAction(
Commands.CopyBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCopyBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanCopyBodies),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCopyBodies));
ToolkitCommands->MapAction(
Commands.PasteBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnPasteBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanPasteBodies));
ToolkitCommands->MapAction(
Commands.CopyShapes,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCopyShapes),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanCopyShapes));
ToolkitCommands->MapAction(
Commands.PasteShapes,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnPasteShapes),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanPasteShapes));
ToolkitCommands->MapAction(
Commands.CopyBodyName,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCopyBodyName),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanCopyBodyName));
ToolkitCommands->MapAction(
Commands.RepeatLastSimulation,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnRepeatLastSimulation),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsToggleSimulation));
ToolkitCommands->MapAction(
Commands.SimulationNoGravity,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSimulationNoGravity),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsNoGravitySimulationEnabled));
ToolkitCommands->MapAction(
Commands.SimulationFloorCollision,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSimulationFloorCollision),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsSimulationFloorCollisionEnabled));
ToolkitCommands->MapAction(
Commands.SelectedSimulation,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSimulation, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsSelectedSimulation));
ToolkitCommands->MapAction(
Commands.SimulationAll,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSimulation, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsFullSimulation));
ToolkitCommands->MapAction(
Commands.DisableCollision,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetCollision, false),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetCollision, false));
ToolkitCommands->MapAction(
Commands.DisableCollisionAll,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetCollisionAll, false),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetCollisionAll, false));
ToolkitCommands->MapAction(
Commands.EnableCollision,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetCollision, true),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetCollision, true));
ToolkitCommands->MapAction(
Commands.EnableCollisionAll,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetCollisionAll, true),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetCollisionAll, true));
ToolkitCommands->MapAction(
Commands.PrimitiveQueryAndPhysics,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetPrimitiveCollision, ECollisionEnabled::QueryAndPhysics),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetPrimitiveCollision, ECollisionEnabled::QueryAndPhysics),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsPrimitiveCollisionChecked, ECollisionEnabled::QueryAndPhysics));
ToolkitCommands->MapAction(
Commands.PrimitiveQueryAndProbe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetPrimitiveCollision, ECollisionEnabled::QueryAndProbe),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetPrimitiveCollision, ECollisionEnabled::QueryAndProbe),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsPrimitiveCollisionChecked, ECollisionEnabled::QueryAndProbe));
ToolkitCommands->MapAction(
Commands.PrimitiveQueryOnly,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetPrimitiveCollision, ECollisionEnabled::QueryOnly),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetPrimitiveCollision, ECollisionEnabled::QueryOnly),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsPrimitiveCollisionChecked, ECollisionEnabled::QueryOnly));
ToolkitCommands->MapAction(
Commands.PrimitivePhysicsOnly,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetPrimitiveCollision, ECollisionEnabled::PhysicsOnly),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetPrimitiveCollision, ECollisionEnabled::PhysicsOnly),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsPrimitiveCollisionChecked, ECollisionEnabled::PhysicsOnly));
ToolkitCommands->MapAction(
Commands.PrimitiveProbeOnly,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetPrimitiveCollision, ECollisionEnabled::ProbeOnly),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetPrimitiveCollision, ECollisionEnabled::ProbeOnly),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsPrimitiveCollisionChecked, ECollisionEnabled::ProbeOnly));
ToolkitCommands->MapAction(
Commands.PrimitiveNoCollision,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetPrimitiveCollision, ECollisionEnabled::NoCollision),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetPrimitiveCollision, ECollisionEnabled::NoCollision),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsPrimitiveCollisionChecked, ECollisionEnabled::NoCollision));
ToolkitCommands->MapAction(
Commands.PrimitiveContributeToMass,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetPrimitiveContributeToMass),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanSetPrimitiveContributeToMass),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::GetPrimitiveContributeToMass));
ToolkitCommands->MapAction(
Commands.WeldToBody,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnWeldToBody),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanWeldToBody));
ToolkitCommands->MapAction(
Commands.AddSphere,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnAddSphere),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanAddPrimitive, EAggCollisionShape::Sphere));
ToolkitCommands->MapAction(
Commands.AddSphyl,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnAddSphyl),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanAddPrimitive, EAggCollisionShape::Sphyl));
ToolkitCommands->MapAction(
Commands.AddBox,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnAddBox),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanAddPrimitive, EAggCollisionShape::Box));
ToolkitCommands->MapAction(
Commands.AddTaperedCapsule,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnAddTaperedCapsule),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanAddPrimitive, EAggCollisionShape::TaperedCapsule));
ToolkitCommands->MapAction(
Commands.DeletePrimitive,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnDeletePrimitive),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedBodyAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.DuplicatePrimitive,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnDuplicatePrimitive),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanDuplicatePrimitive));
ToolkitCommands->MapAction(
Commands.ConstrainChildBodiesToParentBody,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConstrainChildBodiesToParentBody),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasMoreThanOneSelectedBodyAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.ResetConstraint,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnResetConstraint),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.SnapConstraint,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSnapConstraint, EConstraintTransformComponentFlags::All),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.SnapConstraintChildPosition,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSnapConstraint, EConstraintTransformComponentFlags::ChildPosition),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.SnapConstraintChildOrientation,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSnapConstraint, EConstraintTransformComponentFlags::ChildRotation),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.SnapConstraintParentPosition,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSnapConstraint, EConstraintTransformComponentFlags::ParentPosition),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.SnapConstraintParentOrientation,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSnapConstraint, EConstraintTransformComponentFlags::ParentRotation),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.ConvertToBallAndSocket,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConvertToBallAndSocket),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanEditConstraintProperties));
ToolkitCommands->MapAction(
Commands.ConvertToHinge,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConvertToHinge),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanEditConstraintProperties));
ToolkitCommands->MapAction(
Commands.ConvertToPrismatic,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConvertToPrismatic),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanEditConstraintProperties));
ToolkitCommands->MapAction(
Commands.ConvertToSkeletal,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConvertToSkeletal),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::CanEditConstraintProperties));
ToolkitCommands->MapAction(
Commands.DeleteConstraint,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnDeleteConstraint),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.MakeBodyKinematic,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetBodyPhysicsType, EPhysicsType::PhysType_Kinematic ),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsBodyPhysicsType, EPhysicsType::PhysType_Kinematic ) );
ToolkitCommands->MapAction(
Commands.MakeBodySimulated,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetBodyPhysicsType, EPhysicsType::PhysType_Simulated ),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsBodyPhysicsType, EPhysicsType::PhysType_Simulated ) );
ToolkitCommands->MapAction(
Commands.MakeBodyDefault,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSetBodyPhysicsType, EPhysicsType::PhysType_Default ),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsBodyPhysicsType, EPhysicsType::PhysType_Default ) );
ToolkitCommands->MapAction(
Commands.KinematicAllBodiesBelow,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::SetBodiesBelowSelectedPhysicsType, EPhysicsType::PhysType_Kinematic, true),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SimulatedAllBodiesBelow,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::SetBodiesBelowSelectedPhysicsType, EPhysicsType::PhysType_Simulated, true),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.MakeAllBodiesBelowDefault,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::SetBodiesBelowSelectedPhysicsType, EPhysicsType::PhysType_Default, true),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.DeleteBody,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnDeleteBody),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.DeleteAllBodiesBelow,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnDeleteAllBodiesBelow),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.DeleteSelected,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnDeleteSelection),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CycleConstraintOrientation,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCycleConstraintOrientation),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.CycleConstraintActive,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCycleConstraintActive),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ToggleSwing1,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSwing1),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsSwing1Locked));
ToolkitCommands->MapAction(
Commands.ToggleSwing2,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSwing2),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsSwing2Locked));
ToolkitCommands->MapAction(
Commands.ToggleTwist,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleTwist),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsTwistLocked));
ToolkitCommands->MapAction(
Commands.SelectAllBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectAllBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectSimulatedBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectSimulatedBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectKinematicBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectKinematicBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectShapesQueryOnly,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectShapes, ECollisionEnabled::QueryOnly),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectShapesQueryAndPhysics,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectShapes, ECollisionEnabled::QueryAndPhysics),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectShapesPhysicsOnly,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectShapes, ECollisionEnabled::PhysicsOnly),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectShapesQueryAndProbe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectShapes, ECollisionEnabled::QueryAndProbe),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectShapesProbeOnly,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectShapes, ECollisionEnabled::ProbeOnly),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ImportMLLevelSet,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ImportMLLevelSetFromDataTable),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.GenerateSkinnedTriangleMesh,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::GenerateSkinnedTriangleMesh),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.SelectAllConstraints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnSelectAllConstraints),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ToggleSelectionType,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSelectionType, true),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ToggleSelectionTypeWithUserConstraints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleSelectionType, false),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ToggleShowSelected,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleShowSelected),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ShowSelected,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnShowSelected),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.HideSelected,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnHideSelected),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ToggleShowOnlyColliding,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleShowOnlyColliding),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasOneSelectedBodyAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.ToggleShowOnlyConstrained,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleShowOnlyConstrained),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HasSelectedBodyOrConstraintAndIsNotSimulation));
ToolkitCommands->MapAction(
Commands.ToggleShowOnlySelected,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnToggleShowOnlySelected),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.ShowAll,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnShowAll),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.HideAll,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnHideAll),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.DeselectAll,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnDeselectAll),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation));
ToolkitCommands->MapAction(
Commands.Mirror,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::Mirror),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation)
);
ViewportCommandList = MakeShared<FUICommandList_Pinnable>();
ViewportCommandList->BeginGroup(TEXT("MeshRenderingMode"));
ViewportCommandList->MapAction(
Commands.MeshRenderingMode_Solid,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Solid, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Solid, false));
ViewportCommandList->MapAction(
Commands.MeshRenderingMode_Wireframe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Wireframe, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Wireframe, false));
ViewportCommandList->MapAction(
Commands.MeshRenderingMode_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::None, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::None, false));
ViewportCommandList->EndGroup();
ViewportCommandList->BeginGroup(TEXT("CenterOfMassRenderingMode"));
ViewportCommandList->MapAction(
Commands.CenterOfMassRenderingMode_All,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::All, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::All, false));
ViewportCommandList->MapAction(
Commands.CenterOfMassRenderingMode_Selected,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::Selected, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::Selected, false));
ViewportCommandList->MapAction(
Commands.CenterOfMassRenderingMode_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::None, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::None, false));
ViewportCommandList->EndGroup();
ViewportCommandList->BeginGroup(TEXT("CollisionRenderingMode"));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_Solid,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Solid, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Solid, false));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_Wireframe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Wireframe, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Wireframe, false));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_SolidWireframe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::SolidWireframe, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::SolidWireframe, false));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::None, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::None, false));
ViewportCommandList->EndGroup();
ViewportCommandList->BeginGroup(TEXT("ConstraintRenderingMode"));
ViewportCommandList->MapAction(
Commands.ConstraintRenderingMode_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::None, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::None, false));
ViewportCommandList->MapAction(
Commands.ConstraintRenderingMode_AllPositions,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllPositions, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllPositions, false));
ViewportCommandList->MapAction(
Commands.ConstraintRenderingMode_AllLimits,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllLimits, false),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllLimits, false));
ViewportCommandList->EndGroup();
ViewportCommandList->BeginGroup(TEXT("MeshRenderingMode_Simulation"));
ViewportCommandList->MapAction(
Commands.MeshRenderingMode_Simulation_Solid,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Solid, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Solid, true));
ViewportCommandList->MapAction(
Commands.MeshRenderingMode_Simulation_Wireframe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Wireframe, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::Wireframe, true));
ViewportCommandList->MapAction(
Commands.MeshRenderingMode_Simulation_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::None, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsMeshRenderingMode, EPhysicsAssetEditorMeshViewMode::None, true));
ViewportCommandList->EndGroup();
ViewportCommandList->BeginGroup(TEXT("CenterOfMassRenderingMode_Simulation"));
ViewportCommandList->MapAction(
Commands.CenterOfMassRenderingMode_Simulation_All,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::All, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::All, true));
ViewportCommandList->MapAction(
Commands.CenterOfMassRenderingMode_Simulation_Selected,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::Selected, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::Selected, true));
ViewportCommandList->MapAction(
Commands.CenterOfMassRenderingMode_Simulation_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::None, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCenterOfMassRenderingMode, EPhysicsAssetEditorCenterOfMassViewMode::None, true));
ViewportCommandList->EndGroup();
ViewportCommandList->BeginGroup(TEXT("CollisionRenderingMode_Simulation"));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_Simulation_Solid,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Solid, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Solid, true));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_Simulation_Wireframe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Wireframe, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::Wireframe, true));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_Simulation_SolidWireframe,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::SolidWireframe, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::SolidWireframe, true));
ViewportCommandList->MapAction(
Commands.CollisionRenderingMode_Simulation_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::None, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsCollisionRenderingMode, EPhysicsAssetEditorCollisionViewMode::None, true));
ViewportCommandList->EndGroup();
ViewportCommandList->BeginGroup(TEXT("ConstraintRenderingMode_Simulation"));
ViewportCommandList->MapAction(
Commands.ConstraintRenderingMode_Simulation_None,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::None, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::None, true));
ViewportCommandList->MapAction(
Commands.ConstraintRenderingMode_Simulation_AllPositions,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllPositions, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllPositions, true));
ViewportCommandList->MapAction(
Commands.ConstraintRenderingMode_Simulation_AllLimits,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::OnConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllLimits, true),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsConstraintRenderingMode, EPhysicsAssetEditorConstraintViewMode::AllLimits, true));
ViewportCommandList->EndGroup();
ViewportCommandList->MapAction(
Commands.RenderOnlySelectedSolid,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleRenderOnlySelectedSolid),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsRenderingOnlySelectedSolid));
ViewportCommandList->MapAction(
Commands.HideSimulatedBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleHideSimulatedBodies),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsHidingSimulatedBodies));
ViewportCommandList->MapAction(
Commands.HideKinematicBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleHideKinematicBodies),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsHidingKinematicBodies));
ViewportCommandList->MapAction(
Commands.HighlightOverlappingBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleHighlightOverlappingBodies),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsHighlightOverlappingBodies));
ViewportCommandList->MapAction(
Commands.DrawBodyMass,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleHideBodyMass),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsDrawingBodyMass));
ViewportCommandList->MapAction(
Commands.HideCenterOfMassForKinematicBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleHideCenterOfMassForKinematicBodies),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsHidingCenterOfMassForKinematicBodies));
ViewportCommandList->MapAction(
Commands.DrawConstraintsAsPoints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleDrawConstraintsAsPoints),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsDrawingConstraintsAsPoints));
ViewportCommandList->MapAction(
Commands.DrawViolatedLimits,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleDrawViolatedLimits),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsDrawingViolatedLimits));
ViewportCommandList->MapAction(
Commands.RenderOnlySelectedConstraints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::ToggleRenderOnlySelectedConstraints),
FCanExecuteAction(),
FIsActionChecked::CreateSP(this, &FPhysicsAssetEditor::IsRenderingOnlySelectedConstraints));
SkeletonTreeCommandList = MakeShared<FUICommandList_Pinnable>();
SkeletonTreeCommandList->MapAction(
Commands.ShowBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowBodies),
FCanExecuteAction(),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowBodiesChecked)
);
SkeletonTreeCommandList->MapAction(
Commands.ShowSimulatedBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowSimulatedBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsShowBodiesChecked),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowSimulatedBodiesChecked)
);
SkeletonTreeCommandList->MapAction(
Commands.ShowKinematicBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowKinematicBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsShowBodiesChecked),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowKinematicBodiesChecked)
);
SkeletonTreeCommandList->MapAction(
Commands.ShowConstraints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowConstraints),
FCanExecuteAction(),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowConstraintsChecked)
);
SkeletonTreeCommandList->MapAction(
Commands.ShowCrossConstraints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowCrossConstraints),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsShowConstraintsChecked),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowCrossConstraintsChecked)
);
SkeletonTreeCommandList->MapAction(
Commands.ShowParentChildConstraints,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowParentChildConstraints),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsShowConstraintsChecked),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowParentChildConstraintsChecked)
);
SkeletonTreeCommandList->MapAction(
Commands.ShowConstraintsOnParentBodies,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowConstraintsOnParentBodies),
FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsShowConstraintsChecked),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowConstraintsOnParentBodiesChecked)
);
SkeletonTreeCommandList->MapAction(
Commands.ShowPrimitives,
FExecuteAction::CreateSP(this, &FPhysicsAssetEditor::HandleToggleShowPrimitives),
FCanExecuteAction(),
FGetActionCheckState::CreateSP(this, &FPhysicsAssetEditor::GetShowPrimitivesChecked)
);
SkeletonTree->GetPinnedCommandList()->BindCommandList(SkeletonTreeCommandList.ToSharedRef());
}
void FPhysicsAssetEditor::Mirror()
{
SharedData->Mirror();
RecreatePhysicsState();
RefreshHierachyTree();
RefreshPreviewViewport();
}
void FPhysicsAssetEditor::AddAdvancedMenuWidget(FMenuBuilder& InMenuBuilder)
{
InMenuBuilder.BeginSection("Advanced", LOCTEXT("AdvancedHeading", "Advanced"));
InMenuBuilder.AddSubMenu(
LOCTEXT("AddCollisionfromStaticMesh", "Copy Collision From StaticMesh"),
LOCTEXT("AddCollisionfromStaticMesh_Tooltip", "Copy convex collision from a specified static mesh"),
FNewMenuDelegate::CreateLambda([this](FMenuBuilder& InSubMenuBuilder)
{
InSubMenuBuilder.AddWidget(BuildStaticMeshAssetPicker(), FText(), true);
})
);
InMenuBuilder.EndSection();
}
void FPhysicsAssetEditor::BuildMenuWidgetBody(FMenuBuilder& InMenuBuilder)
{
InMenuBuilder.PushCommandList(GetToolkitCommands());
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
struct FLocal
{
static void FillPhysicsTypeMenu(FMenuBuilder& InSubMenuBuilder)
{
const FPhysicsAssetEditorCommands& PhysicsAssetEditorCommands = FPhysicsAssetEditorCommands::Get();
const bool bExposeSimulationControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuSimulationControls;
InSubMenuBuilder.BeginSection("BodyPhysicsTypeActions", LOCTEXT("BodyPhysicsTypeHeader", "Body Physics Type"));
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.MakeBodyKinematic);
if (bExposeSimulationControls)
{
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.MakeBodySimulated);
}
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.MakeBodyDefault);
InSubMenuBuilder.EndSection();
InSubMenuBuilder.BeginSection("BodiesBelowPhysicsTypeActions", LOCTEXT("BodiesBelowPhysicsTypeHeader", "Bodies Below Physics Type"));
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.KinematicAllBodiesBelow);
if (bExposeSimulationControls)
{
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.SimulatedAllBodiesBelow);
}
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.MakeAllBodiesBelowDefault);
InSubMenuBuilder.EndSection();
}
static void FillCollisionMenu(FMenuBuilder& InSubMenuBuilder)
{
const FPhysicsAssetEditorCommands& PhysicsAssetEditorCommands = FPhysicsAssetEditorCommands::Get();
const bool bExposeSimulationControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuSimulationControls;
InSubMenuBuilder.BeginSection("CollisionHeader", LOCTEXT("CollisionHeader", "Collision"));
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.WeldToBody );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.EnableCollision );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.EnableCollisionAll );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.DisableCollision );
InSubMenuBuilder.AddMenuEntry( PhysicsAssetEditorCommands.DisableCollisionAll );
InSubMenuBuilder.EndSection();
InSubMenuBuilder.BeginSection("CollisionFilteringHeader", LOCTEXT("CollisionFilteringHeader", "Collision Filtering"));
if (bExposeSimulationControls)
{
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.PrimitiveQueryAndPhysics);
}
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.PrimitiveQueryOnly);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.PrimitiveQueryAndProbe);
if (bExposeSimulationControls)
{
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.PrimitivePhysicsOnly);
}
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.PrimitiveProbeOnly);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.PrimitiveNoCollision);
InSubMenuBuilder.EndSection();
if (bExposeSimulationControls)
{
InSubMenuBuilder.BeginSection("MassHeader", LOCTEXT("MassHeader", "Mass"));
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.PrimitiveContributeToMass);
InSubMenuBuilder.EndSection();
}
}
};
const bool bExposeSimulationControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuSimulationControls;
const bool bExposeConstraintControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuConstraintControls;
InMenuBuilder.BeginSection( "BodyActions", LOCTEXT( "BodyHeader", "Body" ) );
InMenuBuilder.AddMenuEntry( Commands.RegenerateBodies );
InMenuBuilder.AddSubMenu( LOCTEXT("AddPrimitiveMenu", "Add Primitive"), LOCTEXT("AddPrimitiveMenu_ToolTip", "Add Primitives to this body"),
FNewMenuDelegate::CreateStatic( &FillAddPrimitiveMenu ) );
InMenuBuilder.AddSubMenu( LOCTEXT("CollisionMenu", "Collision"), LOCTEXT("CollisionMenu_ToolTip", "Adjust body/body collision"),
FNewMenuDelegate::CreateStatic( &FLocal::FillCollisionMenu ) );
if (bExposeConstraintControls)
{
InMenuBuilder.AddMenuEntry(Commands.ConstrainChildBodiesToParentBody);
InMenuBuilder.AddSubMenu(LOCTEXT("ConstraintMenu", "Constraints"), LOCTEXT("ConstraintMenu_ToolTip", "Constraint Operations"),
FNewMenuDelegate::CreateSP(this, &FPhysicsAssetEditor::BuildMenuWidgetNewConstraint));
}
InMenuBuilder.AddSubMenu( LOCTEXT("BodyPhysicsTypeMenu", "Physics Type"), LOCTEXT("BodyPhysicsTypeMenu_ToolTip", "Physics Type"),
FNewMenuDelegate::CreateStatic( &FLocal::FillPhysicsTypeMenu ) );
InMenuBuilder.AddSubMenu(
Commands.ApplyPhysicalMaterial->GetLabel(),
LOCTEXT("ApplyPhysicalMaterialSelected", "Apply a physical material to the selected bodies"),
FNewMenuDelegate::CreateLambda([this](FMenuBuilder& InSubMenuBuilder)
{
InSubMenuBuilder.AddWidget(BuildPhysicalMaterialAssetPicker(false), FText(), true);
}),
FUIAction(FExecuteAction(), FCanExecuteAction::CreateSP(this, &FPhysicsAssetEditor::IsNotSimulation)),
NAME_None,
EUserInterfaceActionType::Button
);
InMenuBuilder.AddMenuEntry(Commands.CopyBodies);
InMenuBuilder.AddMenuEntry(Commands.PasteBodies);
InMenuBuilder.AddMenuEntry(Commands.CopyShapes);
InMenuBuilder.AddMenuEntry(Commands.PasteShapes);
InMenuBuilder.AddMenuEntry(Commands.CopyProperties);
InMenuBuilder.AddMenuEntry(Commands.PasteProperties);
InMenuBuilder.AddMenuEntry(Commands.CopyBodyName);
InMenuBuilder.AddMenuEntry( Commands.DeleteBody );
InMenuBuilder.AddMenuEntry( Commands.DeleteAllBodiesBelow );
InMenuBuilder.AddMenuEntry( Commands.Mirror );
InMenuBuilder.EndSection();
InMenuBuilder.BeginSection( "PhysicalAnimationProfile", LOCTEXT( "PhysicalAnimationProfileHeader", "Physical Animation Profile" ) );
InMenuBuilder.AddMenuEntry( Commands.AddBodyToPhysicalAnimationProfile );
InMenuBuilder.AddMenuEntry( Commands.RemoveBodyFromPhysicalAnimationProfile );
InMenuBuilder.EndSection();
AddAdvancedMenuWidget(InMenuBuilder);
}
InMenuBuilder.PopCommandList();
}
void FPhysicsAssetEditor::BuildMenuWidgetPrimitives(FMenuBuilder& InMenuBuilder)
{
InMenuBuilder.PushCommandList(GetToolkitCommands());
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
InMenuBuilder.BeginSection("PrimitiveActions", LOCTEXT("PrimitivesHeader", "Primitives"));
InMenuBuilder.AddMenuEntry(FGenericCommands::Get().Rename);
InMenuBuilder.AddMenuEntry(Commands.DuplicatePrimitive);
InMenuBuilder.AddMenuEntry(Commands.DeletePrimitive);
InMenuBuilder.EndSection();
}
InMenuBuilder.PopCommandList();
}
void FPhysicsAssetEditor::BuildMenuWidgetConstraint(FMenuBuilder& InMenuBuilder)
{
if (!GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuConstraintControls)
{
return;
}
InMenuBuilder.PushCommandList(GetToolkitCommands());
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
struct FLocal
{
static void FillAxesAndLimitsMenu(FMenuBuilder& InSubMenuBuilder)
{
const FPhysicsAssetEditorCommands& PhysicsAssetEditorCommands = FPhysicsAssetEditorCommands::Get();
InSubMenuBuilder.BeginSection("AxesAndLimitsHeader", LOCTEXT("AxesAndLimitsHeader", "Axes and Limits"));
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.CycleConstraintOrientation);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.CycleConstraintActive);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.ToggleSwing1);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.ToggleSwing2);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.ToggleTwist);
InSubMenuBuilder.EndSection();
}
static void FillConvertMenu(FMenuBuilder& InSubMenuBuilder)
{
const FPhysicsAssetEditorCommands& PhysicsAssetEditorCommands = FPhysicsAssetEditorCommands::Get();
InSubMenuBuilder.BeginSection("ConvertHeader", LOCTEXT("ConvertHeader", "Convert"));
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.ConvertToBallAndSocket);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.ConvertToHinge);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.ConvertToPrismatic);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.ConvertToSkeletal);
InSubMenuBuilder.EndSection();
}
static void FillSnapMenu(FMenuBuilder& InSubMenuBuilder)
{
const FPhysicsAssetEditorCommands& PhysicsAssetEditorCommands = FPhysicsAssetEditorCommands::Get();
InSubMenuBuilder.BeginSection("SnapHeader", LOCTEXT("SnapHeader", "Snap"));
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.SnapConstraint);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.SnapConstraintChildPosition);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.SnapConstraintChildOrientation);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.SnapConstraintParentPosition);
InSubMenuBuilder.AddMenuEntry(PhysicsAssetEditorCommands.SnapConstraintParentOrientation);
InSubMenuBuilder.EndSection();
}
};
InMenuBuilder.BeginSection("EditTypeActions", LOCTEXT("ConstraintEditTypeHeader", "Edit"));
InMenuBuilder.AddSubMenu(LOCTEXT("SnapMenu", "Snap"), LOCTEXT("SnapMenu_ToolTip", "Set constraint transforms to defaults"),
FNewMenuDelegate::CreateStatic(&FLocal::FillSnapMenu));
InMenuBuilder.AddMenuEntry(Commands.ResetConstraint);
InMenuBuilder.AddSubMenu( LOCTEXT("AxesAndLimitsMenu", "Axes and Limits"), LOCTEXT("AxesAndLimitsMenu_ToolTip", "Edit axes and limits of this constraint"),
FNewMenuDelegate::CreateStatic( &FLocal::FillAxesAndLimitsMenu ) );
InMenuBuilder.AddSubMenu( LOCTEXT("ConvertMenu", "Convert"), LOCTEXT("ConvertMenu_ToolTip", "Convert constraint to various presets"),
FNewMenuDelegate::CreateStatic( &FLocal::FillConvertMenu ) );
InMenuBuilder.AddMenuEntry(Commands.CopyBodies);
InMenuBuilder.AddMenuEntry(Commands.PasteBodies);
InMenuBuilder.AddMenuEntry(Commands.CopyShapes);
InMenuBuilder.AddMenuEntry(Commands.PasteShapes);
InMenuBuilder.AddMenuEntry(Commands.CopyProperties);
InMenuBuilder.AddMenuEntry(Commands.PasteProperties);
InMenuBuilder.AddMenuEntry(Commands.DeleteConstraint);
InMenuBuilder.EndSection();
InMenuBuilder.BeginSection("ConstraintProfile", LOCTEXT( "ConstraintProfileHeader", "Constraint Profile"));
InMenuBuilder.AddMenuEntry(Commands.AddConstraintToCurrentConstraintProfile);
InMenuBuilder.AddMenuEntry(Commands.RemoveConstraintFromCurrentConstraintProfile);
InMenuBuilder.EndSection();
}
InMenuBuilder.PopCommandList();
}
void FPhysicsAssetEditor::BuildMenuWidgetSelection(FMenuBuilder& InMenuBuilder)
{
InMenuBuilder.PushCommandList(GetToolkitCommands());
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
const bool bExposeSimulationControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuSimulationControls;
const bool bExposeConstraintControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuConstraintControls;
InMenuBuilder.BeginSection("Selection", LOCTEXT("Selection", "Selection"));
InMenuBuilder.AddMenuEntry(Commands.SelectAllBodies);
if (bExposeSimulationControls)
{
InMenuBuilder.AddMenuEntry(Commands.SelectSimulatedBodies);
}
InMenuBuilder.AddMenuEntry(Commands.SelectKinematicBodies);
if (bExposeConstraintControls)
{
InMenuBuilder.AddMenuEntry(Commands.SelectAllConstraints);
}
InMenuBuilder.AddMenuEntry( Commands.ToggleSelectionType );
InMenuBuilder.AddMenuEntry( Commands.ToggleSelectionTypeWithUserConstraints);
InMenuBuilder.AddMenuEntry( Commands.ToggleShowSelected );
InMenuBuilder.AddMenuEntry( Commands.ShowSelected );
InMenuBuilder.AddMenuEntry( Commands.HideSelected );
InMenuBuilder.AddMenuEntry( Commands.ToggleShowOnlySelected );
InMenuBuilder.AddMenuEntry( Commands.ToggleShowOnlyColliding );
if (bExposeConstraintControls)
{
InMenuBuilder.AddMenuEntry(Commands.ToggleShowOnlyConstrained);
}
InMenuBuilder.AddMenuEntry( Commands.ShowAll );
InMenuBuilder.AddMenuEntry( Commands.HideAll );
InMenuBuilder.AddMenuEntry( Commands.SelectShapesQueryOnly);
if (bExposeSimulationControls)
{
InMenuBuilder.AddMenuEntry(Commands.SelectShapesQueryAndPhysics);
InMenuBuilder.AddMenuEntry(Commands.SelectShapesPhysicsOnly);
}
InMenuBuilder.AddMenuEntry( Commands.SelectShapesQueryAndProbe);
InMenuBuilder.AddMenuEntry( Commands.SelectShapesProbeOnly);
InMenuBuilder.EndSection();
InMenuBuilder.BeginSection("CreateAdvancedPrimitives", LOCTEXT("CreateAdvancedPrimitives", "Create Advanced Primitives"));
InMenuBuilder.AddMenuEntry( Commands.ImportMLLevelSet);
InMenuBuilder.AddMenuEntry( Commands.GenerateSkinnedTriangleMesh );
InMenuBuilder.EndSection();
}
InMenuBuilder.PopCommandList();
}
void FPhysicsAssetEditor::BuildMenuWidgetNewConstraint(FMenuBuilder& InMenuBuilder)
{
BuildMenuWidgetNewConstraintForBody(InMenuBuilder, INDEX_NONE);
}
TSharedRef<ISkeletonTree> FPhysicsAssetEditor::BuildMenuWidgetNewConstraintForBody(FMenuBuilder& InMenuBuilder, int32 InSourceBodyIndex, SGraphEditor::FActionMenuClosed InOnActionMenuClosed)
{
FSkeletonTreeBuilderArgs SkeletonTreeBuilderArgs(false, false, false, false);
TSharedRef<FPhysicsAssetEditorSkeletonTreeBuilder> Builder = MakeShared<FPhysicsAssetEditorSkeletonTreeBuilder>(SharedData->PhysicsAsset, SkeletonTreeBuilderArgs);
Builder->bShowBodies = true;
Builder->bShowSimulatedBodies = true;
Builder->bShowKinematicBodies = true;
Builder->bShowConstraints = false;
Builder->bShowCrossConstraints = true;
Builder->bShowParentChildConstraints = true;
Builder->bShowPrimitives = false;
FSkeletonTreeArgs SkeletonTreeArgs;
SkeletonTreeArgs.Mode = ESkeletonTreeMode::Picker;
SkeletonTreeArgs.bAllowMeshOperations = false;
SkeletonTreeArgs.bAllowSkeletonOperations = false;
SkeletonTreeArgs.bShowBlendProfiles = false;
SkeletonTreeArgs.bShowFilterMenu = false;
SkeletonTreeArgs.bShowDebugVisualizationOptions = true;
SkeletonTreeArgs.bHideBonesByDefault = true;
SkeletonTreeArgs.Builder = Builder;
SkeletonTreeArgs.PreviewScene = GetPersonaToolkit()->GetPreviewScene();
SkeletonTreeArgs.OnSelectionChanged = FOnSkeletonTreeSelectionChanged::CreateLambda([this, InSourceBodyIndex, InOnActionMenuClosed](const TArrayView<TSharedPtr<ISkeletonTreeItem>>& InSelectedItems, ESelectInfo::Type SelectInfo)
{
if(InSelectedItems.Num() > 0)
{
TSharedPtr<ISkeletonTreeItem> SelectedItem = InSelectedItems[0];
check(SelectedItem->IsOfType<FSkeletonTreePhysicsBodyItem>());
TSharedPtr<FSkeletonTreePhysicsBodyItem> SelectedBody = StaticCastSharedPtr<FSkeletonTreePhysicsBodyItem>(SelectedItem);
if(InSourceBodyIndex != INDEX_NONE)
{
HandleCreateNewConstraint(InSourceBodyIndex, SelectedBody->GetBodySetupIndex());
}
else
{
const FPhysicsAssetEditorSharedData::SelectionUniqueRange SelectionRange = SharedData->UniqueSelectionReferencingBodies();
if (!SelectionRange.IsEmpty())
{
// make a copy to avoid changing SelectedBodies while iterating SelectedBodies
TArray<int32> SourceBodyIndices;
for (const FPhysicsAssetEditorSharedData::FSelection& SourceBody : SelectionRange)
{
SourceBodyIndices.Add(SourceBody.Index);
}
// create constraints
for (const int32 SourceBodyIndex : SourceBodyIndices)
{
HandleCreateNewConstraint(SourceBodyIndex, SelectedBody->GetBodySetupIndex());
}
}
}
}
FSlateApplication::Get().DismissAllMenus();
InOnActionMenuClosed.ExecuteIfBound();
});
ISkeletonEditorModule& SkeletonEditorModule = FModuleManager::GetModuleChecked<ISkeletonEditorModule>("SkeletonEditor");
TSharedRef<ISkeletonTree> SkeletonPicker = SkeletonEditorModule.CreateSkeletonTree(SkeletonTree->GetEditableSkeleton(), SkeletonTreeArgs);
InMenuBuilder.BeginSection(TEXT("CreateNewConstraint"), LOCTEXT("CreateNewConstraint", "Create New Constraint With..."));
{
InMenuBuilder.AddWidget(
SNew(SBox)
.IsEnabled(this, &FPhysicsAssetEditor::IsNotSimulation)
.WidthOverride(300.0f)
.HeightOverride(400.0f)
[
SkeletonPicker
],
FText(), true, false);
}
InMenuBuilder.EndSection();
return SkeletonPicker;
}
void FPhysicsAssetEditor::BuildMenuWidgetBone(FMenuBuilder& InMenuBuilder)
{
InMenuBuilder.PushCommandList(GetToolkitCommands());
InMenuBuilder.BeginSection("BodyActions", LOCTEXT("BodyHeader", "Body"));
{
InMenuBuilder.AddMenuEntry(FPhysicsAssetEditorCommands::Get().CreateBodies);
}
InMenuBuilder.EndSection();
AddAdvancedMenuWidget(InMenuBuilder);
InMenuBuilder.PopCommandList();
}
bool FPhysicsAssetEditor::ShouldFilterAssetBasedOnSkeleton( const FAssetData& AssetData )
{
// @TODO This is a duplicate of FPersona::ShouldFilterAssetBasedOnSkeleton(), but should go away once PhysicsAssetEditor is integrated with Persona
const FString SkeletonName = AssetData.GetTagValueRef<FString>("Skeleton");
if ( !SkeletonName.IsEmpty() )
{
USkeletalMesh* EditorSkelMesh = SharedData->PhysicsAsset->GetPreviewMesh();
if(EditorSkelMesh != nullptr)
{
USkeleton* Skeleton = EditorSkelMesh->GetSkeleton();
if ( Skeleton && (*SkeletonName) == FObjectPropertyBase::GetExportPath(Skeleton) )
{
return false;
}
}
}
return true;
}
void FPhysicsAssetEditor::SnapConstraintToBone(const FPhysicsAssetEditorSharedData::FSelection* Constraint)
{
SharedData->SnapConstraintToBone(Constraint->Index);
}
void FPhysicsAssetEditor::CreateOrConvertConstraint(EPhysicsAssetEditorConstraintType ConstraintType)
{
//we have to manually call PostEditChange to ensure profiles are updated correctly
FProperty* DefaultInstanceProperty = FindFProperty<FProperty>(UPhysicsConstraintTemplate::StaticClass(), GET_MEMBER_NAME_CHECKED(UPhysicsConstraintTemplate, DefaultInstance));
const FScopedTransaction Transaction( LOCTEXT( "CreateConvertConstraint", "Create Or Convert Constraint" ) );
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedConstraint : SharedData->SelectedConstraints())
{
UPhysicsConstraintTemplate* const ConstraintSetup = SharedData->PhysicsAsset->ConstraintSetup[SelectedConstraint.Index];
ConstraintSetup->PreEditChange(DefaultInstanceProperty);
if(ConstraintType == EPCT_BSJoint)
{
ConstraintUtils::ConfigureAsBallAndSocket(ConstraintSetup->DefaultInstance);
}
else if(ConstraintType == EPCT_Hinge)
{
ConstraintUtils::ConfigureAsHinge(ConstraintSetup->DefaultInstance);
}
else if(ConstraintType == EPCT_Prismatic)
{
ConstraintUtils::ConfigureAsPrismatic(ConstraintSetup->DefaultInstance);
}
else if(ConstraintType == EPCT_SkelJoint)
{
ConstraintUtils::ConfigureAsSkelJoint(ConstraintSetup->DefaultInstance);
}
FPropertyChangedEvent PropertyChangedEvent(DefaultInstanceProperty);
ConstraintSetup->PostEditChangeProperty(PropertyChangedEvent);
}
RecreatePhysicsState();
RefreshHierachyTree();
RefreshPreviewViewport();
}
void FPhysicsAssetEditor::AddNewPrimitive(EAggCollisionShape::Type InPrimitiveType, bool bCopySelected)
{
TArray<FPhysicsAssetEditorSharedData::FSelection> NewSelection = SharedData->UniqueSelectionReferencingBodies().ToArray();
check(!bCopySelected || NewSelection.Num() == 1); //we only support this for one selection
int32 NewPrimIndex = 0;
{
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands();
const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "AddNewPrimitive", "Add New Primitive") );
// Make new bodies for any bones we have selected that don't already have them.
TArray<TSharedPtr<ISkeletonTreeItem>> Items = SkeletonTree->GetSelectedItems();
FSkeletonTreeSelection Selection(Items);
TArray<TSharedPtr<ISkeletonTreeItem>> BoneItems = Selection.GetSelectedItemsByTypeId("FSkeletonTreeBoneItem");
for(TSharedPtr<ISkeletonTreeItem> BoneItem : BoneItems)
{
UBoneProxy* BoneProxy = CastChecked<UBoneProxy>(BoneItem->GetObject());
int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(BoneProxy->BoneName);
if (BoneIndex != INDEX_NONE)
{
const FPhysAssetCreateParams& NewBodyData = GetDefault<UPhysicsAssetGenerationSettings>()->CreateParams;
int32 NewBodyIndex = FPhysicsAssetUtils::CreateNewBody(SharedData->PhysicsAsset, BoneProxy->BoneName, NewBodyData);
NewSelection.AddUnique(MakePrimitiveSelection(NewBodyIndex, EAggCollisionShape::Unknown, 0));
}
}
for(int32 i=0; i<NewSelection.Num(); ++i)
{
const int32 BodyIndex = NewSelection[i].Index;
UBodySetup* BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[BodyIndex];
EAggCollisionShape::Type PrimitiveType;
if (bCopySelected)
{
PrimitiveType = SharedData->GetSelectedBodyOrPrimitive()->GetPrimitiveType();
}
else
{
PrimitiveType = InPrimitiveType;
}
BodySetup->Modify();
if (PrimitiveType == EAggCollisionShape::Sphere)
{
NewPrimIndex = BodySetup->AggGeom.SphereElems.Add(FKSphereElem());
NewSelection[i].PrimitiveType = EAggCollisionShape::Sphere;
NewSelection[i].PrimitiveIndex = NewPrimIndex;
FKSphereElem* SphereElem = &BodySetup->AggGeom.SphereElems[NewPrimIndex];
if (!bCopySelected)
{
SphereElem->Center = FVector::ZeroVector;
SphereElem->Radius = PhysicsAssetEditor::DefaultPrimSize;
}
else
{
SphereElem->Center = BodySetup->AggGeom.SphereElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Center;
SphereElem->Center.X += PhysicsAssetEditor::DuplicateXOffset;
SphereElem->Radius = BodySetup->AggGeom.SphereElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Radius;
}
SharedData->AutoNamePrimitive(BodyIndex, PrimitiveType);
}
else if (PrimitiveType == EAggCollisionShape::Box)
{
NewPrimIndex = BodySetup->AggGeom.BoxElems.Add(FKBoxElem());
NewSelection[i].PrimitiveType = EAggCollisionShape::Box;
NewSelection[i].PrimitiveIndex = NewPrimIndex;
FKBoxElem* BoxElem = &BodySetup->AggGeom.BoxElems[NewPrimIndex];
if (!bCopySelected)
{
BoxElem->SetTransform( FTransform::Identity );
BoxElem->X = 0.5f * PhysicsAssetEditor::DefaultPrimSize;
BoxElem->Y = 0.5f * PhysicsAssetEditor::DefaultPrimSize;
BoxElem->Z = 0.5f * PhysicsAssetEditor::DefaultPrimSize;
}
else
{
BoxElem->SetTransform( BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].GetTransform() );
BoxElem->Center.X += PhysicsAssetEditor::DuplicateXOffset;
BoxElem->X = BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].X;
BoxElem->Y = BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Y;
BoxElem->Z = BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Z;
}
SharedData->AutoNamePrimitive(BodyIndex, PrimitiveType);
}
else if (PrimitiveType == EAggCollisionShape::Sphyl)
{
NewPrimIndex = BodySetup->AggGeom.SphylElems.Add(FKSphylElem());
NewSelection[i].PrimitiveType = EAggCollisionShape::Sphyl;
NewSelection[i].PrimitiveIndex = NewPrimIndex;
FKSphylElem* SphylElem = &BodySetup->AggGeom.SphylElems[NewPrimIndex];
if (!bCopySelected)
{
SphylElem->SetTransform( FTransform::Identity );
SphylElem->Length = PhysicsAssetEditor::DefaultPrimSize;
SphylElem->Radius = PhysicsAssetEditor::DefaultPrimSize;
}
else
{
SphylElem->SetTransform( BodySetup->AggGeom.SphylElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].GetTransform() );
SphylElem->Center.X += PhysicsAssetEditor::DuplicateXOffset;
SphylElem->Length = BodySetup->AggGeom.SphylElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Length;
SphylElem->Radius = BodySetup->AggGeom.SphylElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Radius;
}
SharedData->AutoNamePrimitive(BodyIndex, PrimitiveType);
}
else if (PrimitiveType == EAggCollisionShape::Convex)
{
check(bCopySelected); //only support copying for Convex primitive, as there is no default vertex data
NewPrimIndex = BodySetup->AggGeom.ConvexElems.Add(FKConvexElem());
NewSelection[i].PrimitiveType = EAggCollisionShape::Convex;
NewSelection[i].PrimitiveIndex = NewPrimIndex;
FKConvexElem* ConvexElem = &BodySetup->AggGeom.ConvexElems[NewPrimIndex];
ConvexElem->SetTransform(BodySetup->AggGeom.ConvexElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].GetTransform());
// Copy all of the vertices of the convex element
for (FVector v : BodySetup->AggGeom.ConvexElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].VertexData)
{
v.X += PhysicsAssetEditor::DuplicateXOffset;
ConvexElem->VertexData.Add(v);
}
ConvexElem->UpdateElemBox();
SharedData->AutoNamePrimitive(BodyIndex, PrimitiveType);
BodySetup->InvalidatePhysicsData();
BodySetup->CreatePhysicsMeshes();
}
else if (PrimitiveType == EAggCollisionShape::TaperedCapsule)
{
NewPrimIndex = BodySetup->AggGeom.TaperedCapsuleElems.Add(FKTaperedCapsuleElem());
NewSelection[i].PrimitiveType = EAggCollisionShape::TaperedCapsule;
NewSelection[i].PrimitiveIndex = NewPrimIndex;
FKTaperedCapsuleElem* TaperedCapsuleElem = &BodySetup->AggGeom.TaperedCapsuleElems[NewPrimIndex];
if (!bCopySelected)
{
TaperedCapsuleElem->SetTransform( FTransform::Identity );
TaperedCapsuleElem->Length = PhysicsAssetEditor::DefaultPrimSize;
TaperedCapsuleElem->Radius0 = PhysicsAssetEditor::DefaultPrimSize;
TaperedCapsuleElem->Radius1 = PhysicsAssetEditor::DefaultPrimSize;
}
else
{
TaperedCapsuleElem->SetTransform( BodySetup->AggGeom.TaperedCapsuleElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].GetTransform() );
TaperedCapsuleElem->Center.X += PhysicsAssetEditor::DuplicateXOffset;
TaperedCapsuleElem->Length = BodySetup->AggGeom.TaperedCapsuleElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Length;
TaperedCapsuleElem->Radius0 = BodySetup->AggGeom.TaperedCapsuleElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Radius0;
TaperedCapsuleElem->Radius1 = BodySetup->AggGeom.TaperedCapsuleElems[SharedData->GetSelectedBodyOrPrimitive()->PrimitiveIndex].Radius1;
}
SharedData->AutoNamePrimitive(BodyIndex, PrimitiveType);
}
else
{
check(0); //unrecognized primitive type
}
SharedData->UpdateOverlappingBodyPairs(BodyIndex);
}
} // ScopedTransaction
//clear selection
SharedData->SetSelectedPrimitives(NewSelection);
RecreatePhysicsState();
RefreshHierachyTree();
RefreshPreviewViewport();
}
void FPhysicsAssetEditor::SetBodiesBelowSelectedPhysicsType( EPhysicsType InPhysicsType, bool bMarkAsDirty)
{
TArray<int32> Indices;
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SharedData->UniqueSelectionReferencingBodies())
{
Indices.Add(SelectedBody.Index);
}
SetBodiesBelowPhysicsType(InPhysicsType, Indices, bMarkAsDirty);
}
void FPhysicsAssetEditor::SetBodiesBelowPhysicsType( EPhysicsType InPhysicsType, const TArray<int32> & Indices, bool bMarkAsDirty)
{
USkeletalMesh* EditorSkelMesh = SharedData->PhysicsAsset->GetPreviewMesh();
if(EditorSkelMesh != nullptr)
{
TArray<int32> BelowBodies;
for(int32 i=0; i<Indices.Num(); ++i)
{
// Get the index of this body
UBodySetup* BaseSetup = SharedData->PhysicsAsset->SkeletalBodySetups[Indices[i]];
SharedData->PhysicsAsset->GetBodyIndicesBelow(BelowBodies, BaseSetup->BoneName, EditorSkelMesh);
// Now reset our skeletal mesh, as we don't re-init the physics state when simulating
bool bSimulate = InPhysicsType == PhysType_Simulated || (InPhysicsType == EPhysicsType::PhysType_Default && SharedData->EditorSkelComp->BodyInstance.bSimulatePhysics);
SharedData->EditorSkelComp->SetAllBodiesBelowSimulatePhysics(BaseSetup->BoneName, bSimulate, true);
}
// Make sure that the body setups are also correctly setup (the above loop just does the instances)
for (int32 i = 0; i < BelowBodies.Num(); ++i)
{
int32 BodyIndex = BelowBodies[i];
UBodySetup* BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[BodyIndex];
if (bMarkAsDirty)
{
BodySetup->Modify();
}
BodySetup->PhysicsType = InPhysicsType;
}
}
RecreatePhysicsState();
RefreshHierachyTree();
}
bool FPhysicsAssetEditor::IsNotSimulation() const
{
return !SharedData->bRunningSimulation;
}
bool FPhysicsAssetEditor::HasSelectedBodyAndIsNotSimulation() const
{
return IsNotSimulation() && (SharedData->GetSelectedBodyOrPrimitive());
}
bool FPhysicsAssetEditor::HasOneSelectedBodyAndIsNotSimulation() const
{
return IsNotSimulation() && (SharedData->UniqueSelectionReferencingBodies().Num() == 1);
}
bool FPhysicsAssetEditor::HasMoreThanOneSelectedBodyAndIsNotSimulation() const
{
return IsNotSimulation() && (SharedData->UniqueSelectionReferencingBodies().Num() > 1);
}
bool FPhysicsAssetEditor::HasSelectedBodyOrConstraintAndIsNotSimulation() const
{
return IsNotSimulation() && (!SharedData->UniqueSelectionReferencingBodies().IsEmpty() || !SharedData->SelectedConstraints().IsEmpty());
}
bool FPhysicsAssetEditor::CanEditConstraintProperties() const
{
if(IsNotSimulation() && SharedData->PhysicsAsset && SharedData->GetSelectedConstraint())
{
//If we are currently editing a constraint profile, make sure all selected constraints belong to the profile
if(SharedData->PhysicsAsset->CurrentConstraintProfileName != NAME_None)
{
for (const FPhysicsAssetEditorSharedData::FSelection& Selection : SharedData->SelectedConstraints())
{
UPhysicsConstraintTemplate* CS = SharedData->PhysicsAsset->ConstraintSetup[Selection.Index];
if(!CS || !CS->ContainsConstraintProfile(SharedData->PhysicsAsset->CurrentConstraintProfileName))
{
//missing at least one constraint from profile so don't allow editing
return false;
}
}
}
//no constraint profile so editing is fine
return true;
}
return false;
}
bool FPhysicsAssetEditor::HasSelectedConstraintAndIsNotSimulation() const
{
return IsNotSimulation() && (SharedData->GetSelectedConstraint());
}
void FPhysicsAssetEditor::OnCopyBodyName()
{
if(SharedData->UniqueSelectionReferencingBodies().Num() == 1)
{
SharedData->CopyBodyName();
}
RefreshPreviewViewport();
}
bool FPhysicsAssetEditor::CanCopyBodyName() const
{
return IsSelectedEditMode() && SharedData->UniqueSelectionReferencingBodies().Num() == 1 && SharedData->SelectedConstraints().IsEmpty();
}
bool FPhysicsAssetEditor::IsSelectedEditMode() const
{
return HasSelectedBodyAndIsNotSimulation() || HasSelectedConstraintAndIsNotSimulation();
}
void FPhysicsAssetEditor::OnChangeDefaultMesh(USkeletalMesh* OldPreviewMesh, USkeletalMesh* NewPreviewMesh)
{
if(NewPreviewMesh != nullptr)
{
IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities");
// Update various infos based on the mesh
MeshUtilities.CalcBoneVertInfos(NewPreviewMesh, SharedData->DominantWeightBoneInfos, true);
MeshUtilities.CalcBoneVertInfos(NewPreviewMesh, SharedData->AnyWeightBoneInfos, false);
RefreshHierachyTree();
SharedData->EditorSkelComp->SetDisablePostProcessBlueprint(true);
}
}
void FPhysicsAssetEditor::CreateBodiesAndConstraintsForSelectedBones(const EAggCollisionShape::Type InPrimitiveType, const bool bInShouldCreateConstraints)
{
FPhysAssetCreateParams NewBodyData = GetDefault<UPhysicsAssetGenerationSettings>()->CreateParams;
NewBodyData.GeomType = ConvertAggCollisionShapeTypeToPhysicsAssetGeomType(InPrimitiveType);
NewBodyData.bCreateConstraints = bInShouldCreateConstraints;
CreateBodiesAndConstraintsForSelectedBones(NewBodyData);
}
void FPhysicsAssetEditor::ResetBoneCollision()
{
CreateBodiesAndConstraintsForSelectedBones(GetDefault<UPhysicsAssetGenerationSettings>()->CreateParams);
}
void FPhysicsAssetEditor::CreateBodiesAndConstraintsForSelectedBones(const FPhysAssetCreateParams& NewBodyData)
{
USkeletalMesh* EditorSkelMesh = SharedData->PhysicsAsset->GetPreviewMesh();
if(EditorSkelMesh == nullptr)
{
return;
}
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands();
if(!SharedData->UniqueSelectionReferencingBodies().IsEmpty())
{
TArray<int32> SelectedBodyIndices;
const FScopedTransaction Transaction( LOCTEXT("ResetBoneCollision", "Reset Bone Collision") );
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SharedData->UniqueSelectionReferencingBodies())
{
int32 SelectedBodyIndex = SelectedBody.Index;
if (SharedData->PhysicsAsset->SkeletalBodySetups.IsValidIndex(SelectedBodyIndex) == false)
{
continue;
}
SelectedBodyIndices.Add(SelectedBodyIndex);
}
TArray<int32> BodyIndices;
FPhysicsAssetUtils::CreateCollisionsFromBones(SharedData->PhysicsAsset, EditorSkelMesh, SelectedBodyIndices, NewBodyData,
NewBodyData.VertWeight == EVW_DominantWeight ? SharedData->DominantWeightBoneInfos : SharedData->AnyWeightBoneInfos, BodyIndices);
for(const int32 BodyIndex : BodyIndices)
{
SharedData->AutoNameAllPrimitives(BodyIndex, NewBodyData.GeomType);
}
SharedData->SetSelectedBodies(BodyIndices);
}
else
{
TArray<TSharedPtr<ISkeletonTreeItem>> Items = SkeletonTree->GetSelectedItems();
FSkeletonTreeSelection Selection(Items);
TArray<TSharedPtr<ISkeletonTreeItem>> BoneItems = Selection.GetSelectedItemsByTypeId("FSkeletonTreeBoneItem");
// If we have bones selected, make new bodies for them
if(BoneItems.Num() > 0)
{
const FScopedTransaction Transaction( LOCTEXT("AddNewPrimitive", "Add New Bodies") );
FScopedSlowTask SlowTask((float)BoneItems.Num());
SlowTask.MakeDialog();
for(TSharedPtr<ISkeletonTreeItem> BoneItem : BoneItems)
{
SlowTask.EnterProgressFrame(1.0f, FText::Format(LOCTEXT("ResetCollsionStepInfo", "Generating collision for {0}"), FText::FromName(BoneItem->GetRowItemName())));
UBoneProxy* BoneProxy = CastChecked<UBoneProxy>(BoneItem->GetObject());
const int32 BoneIndex = EditorSkelMesh->GetRefSkeleton().FindBoneIndex(BoneProxy->BoneName);
if (BoneIndex != INDEX_NONE)
{
SharedData->MakeNewBody(NewBodyData, BoneIndex);
}
}
}
else
{
const FScopedTransaction Transaction( LOCTEXT("ResetAllBoneCollision", "Reset All Collision") );
SharedData->PhysicsAsset->Modify();
// Deselect everything.
SharedData->ClearSelectedBody();
SharedData->ClearSelectedConstraints();
// Empty current asset data.
SharedData->PhysicsAsset->SkeletalBodySetups.Empty();
SharedData->PhysicsAsset->BodySetupIndexMap.Empty();
SharedData->PhysicsAsset->ConstraintSetup.Empty();
FText ErrorMessage;
if (FPhysicsAssetUtils::CreateFromSkeletalMesh(SharedData->PhysicsAsset, EditorSkelMesh, NewBodyData, ErrorMessage, /*bSetToMesh=*/false) == false)
{
//name the resulting primitives
for (int32 BodyIndex = 0; BodyIndex < SharedData->PhysicsAsset->SkeletalBodySetups.Num(); BodyIndex++)
{
SharedData->AutoNameAllPrimitives(BodyIndex, NewBodyData.GeomType);
}
FMessageDialog::Open(EAppMsgType::Ok, ErrorMessage);
}
}
}
RecreatePhysicsState();
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
RefreshPreviewViewport();
RefreshHierachyTree();
}
void FPhysicsAssetEditor::ShowNotificationMessage(const FText& Message, const SNotificationItem::ECompletionState CompletionState)
{
FNotificationInfo Info(Message);
Info.ExpireDuration = 5.0f;
Info.bUseLargeFont = false;
Info.bUseThrobber = false;
Info.bUseSuccessFailIcons = false;
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
if (Notification.IsValid())
{
Notification->SetCompletionState(CompletionState);
}
}
void FPhysicsAssetEditor::OnCopyBodies()
{
int32 NumCopiedBodies;
int32 NumCopiedConstraints;
int32 NumCopiedDisabledCollisionPairs;
SharedData->CopySelectedBodiesAndConstraintsToClipboard(NumCopiedBodies, NumCopiedConstraints, NumCopiedDisabledCollisionPairs);
const FText MessageFormat = LOCTEXT("CopiedBodiesAndConstraintsToClipboard", "{0} {0}|plural(one=body,other=bodies), {1} {1}|plural(one=constraints,other=constraints) and {2} disabled collision {2}|plural(one=pair,other=pairs) copied to clipboard");
ShowNotificationMessage(FText::Format(MessageFormat, NumCopiedBodies, NumCopiedConstraints, NumCopiedDisabledCollisionPairs), SNotificationItem::CS_Success);
}
bool FPhysicsAssetEditor::IsCopyBodies() const
{
// todo : implement by checking the clipboard ?
return true;
}
bool FPhysicsAssetEditor::CanCopyBodies() const
{
if (IsSelectedEditMode())
{
return !SharedData->UniqueSelectionReferencingBodies().IsEmpty() || !SharedData->SelectedConstraints().IsEmpty();
}
return false;
}
void FPhysicsAssetEditor::OnPasteBodies()
{
int32 NumPastedBodies;
int32 NumPastedConstraints;
int32 NumCopiedDisabledCollisionPairs;
SharedData->PasteBodiesAndConstraintsFromClipboard(NumPastedBodies, NumPastedConstraints, NumCopiedDisabledCollisionPairs);
const FText MessageFormat = LOCTEXT("PastedBodiesAndConstraintsToClipboard", "{0} {0}|plural(one=body,other=bodies), {1} {1}|plural(one=constraints,other=constraints) and {2} disabled collision {2}|plural(one=pair,other=pairs) pasted from clipboard");
ShowNotificationMessage(FText::Format(MessageFormat, NumPastedBodies, NumPastedConstraints, NumCopiedDisabledCollisionPairs), SNotificationItem::CS_Success);
}
bool FPhysicsAssetEditor::CanPasteBodies() const
{
return SharedData->CanPasteBodiesAndConstraintsFromClipboard();
}
void FPhysicsAssetEditor::OnCopyShapes()
{
int32 NumCopiedShapes;
int32 NumBodiesCopiedFrom;
SharedData->CopySelectedShapesToClipboard(NumCopiedShapes, NumBodiesCopiedFrom);
const FText MessageFormat = LOCTEXT("CopiedShapesToClipboard", "{0} shapes copied to clipboard from {1} selected bodies");
ShowNotificationMessage(FText::Format(MessageFormat, NumCopiedShapes, NumBodiesCopiedFrom), SNotificationItem::CS_Success);
}
bool FPhysicsAssetEditor::CanCopyShapes() const
{
if (IsSelectedEditMode())
{
return !SharedData->UniqueSelectionReferencingBodies().IsEmpty();
}
return false;
}
void FPhysicsAssetEditor::OnPasteShapes()
{
int32 NumPastedShapes;
int32 NumBodiesPastedInto;
SharedData->PasteShapesFromClipboard(NumPastedShapes, NumBodiesPastedInto);
const FText MessageFormat = LOCTEXT("PastedShapesFromClipboard", "{0} shapes pasted from clipboard into {1} selected bodies");
ShowNotificationMessage(FText::Format(MessageFormat, NumPastedShapes, NumBodiesPastedInto), SNotificationItem::CS_Success);
}
bool FPhysicsAssetEditor::CanPasteShapes() const
{
return SharedData->CanPasteShapesFromClipboard();
}
void FPhysicsAssetEditor::OnCopyProperties()
{
if(SharedData->UniqueSelectionReferencingBodies().Num() == 1)
{
SharedData->CopyBodyProperties();
}
else if(SharedData->SelectedConstraints().Num() == 1)
{
SharedData->CopyConstraintProperties();
}
RefreshPreviewViewport();
}
void FPhysicsAssetEditor::OnPasteProperties()
{
if(!SharedData->UniqueSelectionReferencingBodies().IsEmpty())
{
SharedData->PasteBodyProperties();
}
else if (!SharedData->SelectedConstraints().IsEmpty())
{
SharedData->PasteConstraintProperties();
}
RecreatePhysicsState();
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
RefreshPreviewViewport();
RefreshHierachyTree();
}
bool FPhysicsAssetEditor::CanCopyProperties() const
{
if(IsSelectedEditMode())
{
if(SharedData->UniqueSelectionReferencingBodies().Num() == 1 && SharedData->SelectedConstraints().IsEmpty())
{
return true;
}
else if (SharedData->SelectedConstraints().Num() == 1 && SharedData->UniqueSelectionReferencingBodies().IsEmpty())
{
return true;
}
}
return false;
}
bool FPhysicsAssetEditor::CanPasteProperties() const
{
return IsSelectedEditMode() && IsCopyProperties() && (!SharedData->UniqueSelectionReferencingBodies().IsEmpty() || !SharedData->SelectedConstraints().IsEmpty());
}
bool FPhysicsAssetEditor::IsCopyProperties() const
{
return FPhysicsAssetEditorSharedData::ClipboardHasCompatibleData();
}
//We need to save and restore physics states based on the mode we use to simulate
void FPhysicsAssetEditor::FixPhysicsState()
{
UPhysicsAsset * PhysicsAsset = SharedData->PhysicsAsset;
TArray<TObjectPtr<USkeletalBodySetup>>& BodySetup = PhysicsAsset->SkeletalBodySetups;
if(!SharedData->bRunningSimulation)
{
PhysicsTypeState.Reset();
for(int32 i=0; i<SharedData->PhysicsAsset->SkeletalBodySetups.Num(); ++i)
{
PhysicsTypeState.Add(BodySetup[i]->PhysicsType);
}
}else
{
for(int32 i=0; i<PhysicsTypeState.Num(); ++i)
{
BodySetup[i]->PhysicsType = PhysicsTypeState[i];
}
}
}
void FPhysicsAssetEditor::ImpToggleSimulation()
{
static const int32 PrevMaxFPS = GEngine->GetMaxFPS();
if(!SharedData->bRunningSimulation)
{
GEngine->SetMaxFPS(SharedData->EditorOptions->MaxFPS);
}
else
{
GEngine->SetMaxFPS(PrevMaxFPS);
}
SharedData->ToggleSimulation();
// add to analytics record
OnAddPhatRecord(TEXT("ToggleSimulate"), true, true);
}
void FPhysicsAssetEditor::OnRepeatLastSimulation()
{
OnToggleSimulation(SelectedSimulation);
}
void FPhysicsAssetEditor::OnToggleSimulation(bool bInSelected)
{
SelectedSimulation = bInSelected;
// this stores current physics types before simulate
// and recovers to the previous physics types
// so after this one, we can modify physics types fine
FixPhysicsState();
if (IsSelectedSimulation())
{
SetupSelectedSimulation();
}
ImpToggleSimulation();
}
void FPhysicsAssetEditor::OnToggleSimulationNoGravity()
{
SharedData->bNoGravitySimulation = !SharedData->bNoGravitySimulation;
}
bool FPhysicsAssetEditor::IsNoGravitySimulationEnabled() const
{
return SharedData->bNoGravitySimulation;
}
void FPhysicsAssetEditor::OnToggleSimulationFloorCollision()
{
if (SharedData && SharedData->EditorOptions)
{
SharedData->EditorOptions->bSimulationFloorCollisionEnabled = !SharedData->EditorOptions->bSimulationFloorCollisionEnabled;
// Update collision for floor
if (PersonaToolkit)
{
TSharedRef<IPersonaPreviewScene> PersonaPreviewScene = PersonaToolkit->GetPreviewScene();
if (UStaticMeshComponent* FloorMeshComponent = const_cast<UStaticMeshComponent*>(PersonaPreviewScene->GetFloorMeshComponent()))
{
if (SharedData->EditorOptions->bSimulationFloorCollisionEnabled)
{
FloorMeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}
else
{
FloorMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
}
}
}
}
bool FPhysicsAssetEditor::IsSimulationFloorCollisionEnabled() const
{
return SharedData && SharedData->EditorOptions && SharedData->EditorOptions->bSimulationFloorCollisionEnabled;
}
bool FPhysicsAssetEditor::IsFullSimulation() const
{
return !SelectedSimulation;
}
bool FPhysicsAssetEditor::IsSelectedSimulation() const
{
return SelectedSimulation;
}
void FPhysicsAssetEditor::SetupSelectedSimulation()
{
//Before starting we modify the PhysicsType so that selected are unfixed and the rest are fixed
if(SharedData->bRunningSimulation == false)
{
UPhysicsAsset * PhysicsAsset = SharedData->PhysicsAsset;
TArray<TObjectPtr<USkeletalBodySetup>>& BodySetup = PhysicsAsset->SkeletalBodySetups;
//first we fix all the bodies
for(int32 i=0; i<SharedData->PhysicsAsset->SkeletalBodySetups.Num(); ++i)
{
BodySetup[i]->PhysicsType = PhysType_Kinematic;
}
//Bodies already have a function that does this
SetBodiesBelowSelectedPhysicsType(PhysType_Simulated, false);
//constraints need some more work
TArray<int32> BodyIndices;
TArray<TObjectPtr<UPhysicsConstraintTemplate>> & ConstraintSetup = PhysicsAsset->ConstraintSetup;
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedConstraint : SharedData->SelectedConstraints())
{
const int32 ConstraintIndex = SelectedConstraint.Index;
const FName ConstraintBone1 = ConstraintSetup[ConstraintIndex]->DefaultInstance.ConstraintBone1; //we only unfix the child bodies
for(int32 j=0; j<BodySetup.Num(); ++j)
{
if(BodySetup[j]->BoneName == ConstraintBone1)
{
BodyIndices.Add(j);
}
}
}
SetBodiesBelowPhysicsType(PhysType_Simulated, BodyIndices, false);
}
}
bool FPhysicsAssetEditor::IsToggleSimulation() const
{
return SharedData->bRunningSimulation;
}
void FPhysicsAssetEditor::OnMeshRenderingMode(EPhysicsAssetEditorMeshViewMode Mode, bool bSimulation)
{
if (bSimulation)
{
SharedData->EditorOptions->SimulationMeshViewMode = Mode;
}
else
{
SharedData->EditorOptions->MeshViewMode = Mode;
}
SharedData->EditorOptions->SaveConfig();
// Changing the mesh rendering mode requires the skeletal mesh component to change its render state, which is an operation
// which is deferred until after render. Hence we need to trigger another viewport refresh on the following frame.
RefreshPreviewViewport();
}
bool FPhysicsAssetEditor::IsMeshRenderingMode(EPhysicsAssetEditorMeshViewMode Mode, bool bSimulation) const
{
return Mode == SharedData->GetCurrentMeshViewMode(bSimulation);
}
void FPhysicsAssetEditor::OnCenterOfMassRenderingMode(EPhysicsAssetEditorCenterOfMassViewMode Mode, bool bSimulation)
{
if (bSimulation)
{
SharedData->EditorOptions->SimulationCenterOfMassViewMode = Mode;
}
else
{
SharedData->EditorOptions->CenterOfMassViewMode = Mode;
}
SharedData->EditorOptions->SaveConfig();
RefreshPreviewViewport();
}
bool FPhysicsAssetEditor::IsCenterOfMassRenderingMode(EPhysicsAssetEditorCenterOfMassViewMode Mode, bool bSimulation) const
{
return Mode == SharedData->GetCurrentCenterOfMassViewMode(bSimulation);
}
void FPhysicsAssetEditor::OnCollisionRenderingMode(EPhysicsAssetEditorCollisionViewMode Mode, bool bSimulation)
{
if (bSimulation)
{
SharedData->EditorOptions->SimulationCollisionViewMode = Mode;
}
else
{
SharedData->EditorOptions->CollisionViewMode = Mode;
}
SharedData->EditorOptions->SaveConfig();
RefreshPreviewViewport();
}
bool FPhysicsAssetEditor::IsCollisionRenderingMode(EPhysicsAssetEditorCollisionViewMode Mode, bool bSimulation) const
{
return Mode == SharedData->GetCurrentCollisionViewMode(bSimulation);
}
void FPhysicsAssetEditor::OnConstraintRenderingMode(EPhysicsAssetEditorConstraintViewMode Mode, bool bSimulation)
{
if (bSimulation)
{
SharedData->EditorOptions->SimulationConstraintViewMode = Mode;
}
else
{
SharedData->EditorOptions->ConstraintViewMode = Mode;
}
SharedData->EditorOptions->SaveConfig();
RefreshPreviewViewport();
}
void FPhysicsAssetEditor::ToggleDrawConstraintsAsPoints()
{
SharedData->EditorOptions->bShowConstraintsAsPoints = !SharedData->EditorOptions->bShowConstraintsAsPoints;
SharedData->EditorOptions->SaveConfig();
}
bool FPhysicsAssetEditor::IsDrawingConstraintsAsPoints() const
{
return SharedData->EditorOptions->bShowConstraintsAsPoints;
}
void FPhysicsAssetEditor::ToggleDrawViolatedLimits()
{
SharedData->EditorOptions->bDrawViolatedLimits = !SharedData->EditorOptions->bDrawViolatedLimits;
SharedData->EditorOptions->SaveConfig();
}
bool FPhysicsAssetEditor::IsDrawingViolatedLimits() const
{
return SharedData->EditorOptions->bDrawViolatedLimits;
}
void FPhysicsAssetEditor::ToggleHideCenterOfMassForKinematicBodies()
{
SharedData->EditorOptions->bHideCenterOfMassForKinematicBodies = !SharedData->EditorOptions->bHideCenterOfMassForKinematicBodies;
SharedData->EditorOptions->SaveConfig();
}
bool FPhysicsAssetEditor::IsHidingCenterOfMassForKinematicBodies() const
{
return SharedData->EditorOptions->bHideCenterOfMassForKinematicBodies;
}
void FPhysicsAssetEditor::ToggleRenderOnlySelectedConstraints()
{
SharedData->EditorOptions->bRenderOnlySelectedConstraints = !SharedData->EditorOptions->bRenderOnlySelectedConstraints;
SharedData->EditorOptions->SaveConfig();
}
bool FPhysicsAssetEditor::IsRenderingOnlySelectedConstraints() const
{
return SharedData->EditorOptions->bRenderOnlySelectedConstraints;
}
void FPhysicsAssetEditor::ToggleRenderOnlySelectedSolid()
{
SharedData->EditorOptions->bSolidRenderingForSelectedOnly = !SharedData->EditorOptions->bSolidRenderingForSelectedOnly;
SharedData->EditorOptions->SaveConfig();
}
void FPhysicsAssetEditor::ToggleHideSimulatedBodies()
{
SharedData->EditorOptions->bHideSimulatedBodies = !SharedData->EditorOptions->bHideSimulatedBodies;
SharedData->EditorOptions->SaveConfig();
}
void FPhysicsAssetEditor::ToggleHideKinematicBodies()
{
SharedData->EditorOptions->bHideKinematicBodies = !SharedData->EditorOptions->bHideKinematicBodies;
SharedData->EditorOptions->SaveConfig();
}
void FPhysicsAssetEditor::ToggleHighlightOverlappingBodies()
{
SharedData->ToggleHighlightOverlapingBodies();
}
void FPhysicsAssetEditor::ToggleHideBodyMass()
{
SharedData->EditorOptions->bHideBodyMass = !SharedData->EditorOptions->bHideBodyMass;
SharedData->EditorOptions->SaveConfig();
}
bool FPhysicsAssetEditor::IsRenderingOnlySelectedSolid() const
{
return SharedData->EditorOptions->bSolidRenderingForSelectedOnly;
}
bool FPhysicsAssetEditor::IsHidingSimulatedBodies() const
{
return SharedData->EditorOptions->bHideSimulatedBodies;
}
bool FPhysicsAssetEditor::IsHidingKinematicBodies() const
{
return SharedData->EditorOptions->bHideKinematicBodies;
}
bool FPhysicsAssetEditor::IsHighlightOverlappingBodies() const
{
return SharedData->IsHighlightingOverlapingBodies();
}
bool FPhysicsAssetEditor::IsHidingBodyMass() const
{
return SharedData->EditorOptions->bHideBodyMass;
}
bool FPhysicsAssetEditor::IsDrawingBodyMass() const
{
return !IsHidingBodyMass();
}
bool FPhysicsAssetEditor::IsConstraintRenderingMode(EPhysicsAssetEditorConstraintViewMode Mode, bool bSimulation) const
{
return Mode == SharedData->GetCurrentConstraintViewMode(bSimulation);
}
void FPhysicsAssetEditor::OnToggleMassProperties()
{
SharedData->ToggleShowCom();
RefreshPreviewViewport();
}
bool FPhysicsAssetEditor::IsToggleMassProperties() const
{
return SharedData->GetShowCom();
}
void FPhysicsAssetEditor::OnSetCollision(bool bEnable)
{
FScopedTransaction Transaction(LOCTEXT("SetCollision", "Set Collision"));
SharedData->SetCollisionBetweenSelected(bEnable);
}
bool FPhysicsAssetEditor::CanSetCollision(bool bEnable) const
{
return SharedData->CanSetCollisionBetweenSelected(bEnable);
}
void FPhysicsAssetEditor::OnSetCollisionAll(bool bEnable)
{
FScopedTransaction Transaction(LOCTEXT("SetCollision", "Set Collision"));
SharedData->SetCollisionBetweenSelectedAndAll(bEnable);
}
bool FPhysicsAssetEditor::CanSetCollisionAll(bool bEnable) const
{
return SharedData->CanSetCollisionBetweenSelectedAndAll(bEnable);
}
void FPhysicsAssetEditor::OnSetPrimitiveCollision(ECollisionEnabled::Type CollisionEnabled)
{
FScopedTransaction Transaction(LOCTEXT("SetPrimitiveCollision", "Set Primitive Collision"));
SharedData->SetPrimitiveCollision(CollisionEnabled);
}
bool FPhysicsAssetEditor::CanSetPrimitiveCollision(ECollisionEnabled::Type CollisionEnabled) const
{
return SharedData->CanSetPrimitiveCollision(CollisionEnabled);
}
bool FPhysicsAssetEditor::IsPrimitiveCollisionChecked(ECollisionEnabled::Type CollisionEnabled) const
{
return SharedData->GetIsPrimitiveCollisionEnabled(CollisionEnabled);
}
void FPhysicsAssetEditor::OnSetPrimitiveContributeToMass()
{
SharedData->SetPrimitiveContributeToMass(!SharedData->GetPrimitiveContributeToMass());
}
bool FPhysicsAssetEditor::CanSetPrimitiveContributeToMass() const
{
return SharedData->CanSetPrimitiveContributeToMass();
}
bool FPhysicsAssetEditor::GetPrimitiveContributeToMass() const
{
return SharedData->GetPrimitiveContributeToMass();
}
void FPhysicsAssetEditor::OnWeldToBody()
{
SharedData->WeldSelectedBodies();
}
bool FPhysicsAssetEditor::CanWeldToBody()
{
return HasSelectedBodyAndIsNotSimulation() && SharedData->WeldSelectedBodies(false);
}
void FPhysicsAssetEditor::OnAddSphere()
{
AddNewPrimitive(EAggCollisionShape::Sphere);
}
void FPhysicsAssetEditor::OnAddSphyl()
{
AddNewPrimitive(EAggCollisionShape::Sphyl);
}
void FPhysicsAssetEditor::OnAddBox()
{
AddNewPrimitive(EAggCollisionShape::Box);
}
void FPhysicsAssetEditor::OnAddTaperedCapsule()
{
AddNewPrimitive(EAggCollisionShape::TaperedCapsule);
}
bool FPhysicsAssetEditor::CanAddPrimitive(EAggCollisionShape::Type InPrimitiveType) const
{
return IsNotSimulation();
}
void FPhysicsAssetEditor::OnDeletePrimitive()
{
SharedData->DeleteCurrentPrim();
RecreatePhysicsState();
}
void FPhysicsAssetEditor::OnDuplicatePrimitive()
{
AddNewPrimitive(EAggCollisionShape::Unknown, true);
}
bool FPhysicsAssetEditor::CanDuplicatePrimitive() const
{
return HasSelectedBodyAndIsNotSimulation() && SharedData->UniqueSelectionReferencingBodies().Num() == 1;
}
void FPhysicsAssetEditor::OnConstrainChildBodiesToParentBody()
{
const FPhysicsAssetEditorSharedData::FSelection* const LastSelectedBody = SharedData->GetSelectedBodyOrPrimitive();
if (LastSelectedBody && (SharedData->UniqueSelectionReferencingBodies().Num() > 1))
{
const int32 ParentBodyIndex = LastSelectedBody->Index;
TArray<int32> ChildBodyIndices; // needed as the selection may contain multiple time the same body with different primitive index
for (const FPhysicsAssetEditorSharedData::FSelection& Selection : SharedData->UniqueSelectionReferencingBodies())
{
if (Selection.Index != ParentBodyIndex)
{
ChildBodyIndices.AddUnique(Selection.Index);
}
}
SharedData->MakeNewConstraints(ParentBodyIndex, ChildBodyIndices);
}
}
void FPhysicsAssetEditor::OnResetConstraint()
{
SharedData->SetSelectedConstraintRelTM(FTransform::Identity);
RefreshPreviewViewport();
}
void FPhysicsAssetEditor::OnSnapConstraint(const EConstraintTransformComponentFlags ComponentFlags)
{
const FScopedTransaction Transaction( LOCTEXT( "SnapConstraints", "Snap Constraints" ) );
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedConstraint : SharedData->SelectedConstraints())
{
SharedData->SnapConstraintToBone(SelectedConstraint.Index, ComponentFlags);
}
RefreshPreviewViewport();
}
void FPhysicsAssetEditor::OnConvertToBallAndSocket()
{
CreateOrConvertConstraint(EPCT_BSJoint);
}
void FPhysicsAssetEditor::OnConvertToHinge()
{
CreateOrConvertConstraint(EPCT_Hinge);
}
void FPhysicsAssetEditor::OnConvertToPrismatic()
{
CreateOrConvertConstraint(EPCT_Prismatic);
}
void FPhysicsAssetEditor::OnConvertToSkeletal()
{
CreateOrConvertConstraint(EPCT_SkelJoint);
}
void FPhysicsAssetEditor::OnDeleteConstraint()
{
SharedData->DeleteCurrentConstraint();
RecreatePhysicsState();
}
void FPhysicsAssetEditor::OnSetBodyPhysicsType( EPhysicsType InPhysicsType )
{
FPhysicsAssetEditorSharedData::SelectionUniqueRange SelectionRange = SharedData->UniqueSelectionReferencingBodies();
if (!SelectionRange.IsEmpty())
{
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedElement : SelectionRange)
{
UBodySetup* const BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedElement.Index];
BodySetup->Modify();
BodySetup->PhysicsType = InPhysicsType;
}
RecreatePhysicsState();
RefreshPreviewViewport();
}
}
bool FPhysicsAssetEditor::IsBodyPhysicsType( EPhysicsType InPhysicsType )
{
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SharedData->UniqueSelectionReferencingBodies())
{
UBodySetup* const BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBody.Index];
if(BodySetup->PhysicsType == InPhysicsType)
{
return true;
}
}
return false;
}
void FPhysicsAssetEditor::OnDeleteBody()
{
FPhysicsAssetEditorSharedData::SelectionUniqueRange SelectionRange = SharedData->UniqueSelectionReferencingBodies();
if(!SelectionRange.IsEmpty())
{
//first build the bodysetup array because deleting bodies modifies the selected array
TArray<UBodySetup*> BodySetups;
BodySetups.Reserve(SelectionRange.Num());
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SelectionRange)
{
BodySetups.Add( SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBody.Index] );
}
const FScopedTransaction Transaction( LOCTEXT( "DeleteBodies", "Delete Bodies" ) );
for(int32 i=0; i<BodySetups.Num(); ++i)
{
int32 BodyIndex = SharedData->PhysicsAsset->FindBodyIndex(BodySetups[i]->BoneName);
if(BodyIndex != INDEX_NONE)
{
// Use PhysicsAssetEditor function to delete action (so undo works etc)
SharedData->DeleteBody(BodyIndex, false);
}
}
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
}
}
void FPhysicsAssetEditor::OnDeleteAllBodiesBelow()
{
USkeletalMesh* EditorSkelMesh = SharedData->PhysicsAsset->GetPreviewMesh();
if(EditorSkelMesh == nullptr)
{
return;
}
TArray<UBodySetup*> BodySetups;
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SharedData->UniqueSelectionReferencingBodies())
{
UBodySetup* BaseSetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBody.Index];
// Build a list of BodySetups below this one
TArray<int32> BelowBodies;
SharedData->PhysicsAsset->GetBodyIndicesBelow(BelowBodies, BaseSetup->BoneName, EditorSkelMesh);
for (const int32 BodyIndex : BelowBodies)
{
UBodySetup* BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[BodyIndex];
BodySetups.Add(BodySetup);
}
}
if(BodySetups.Num())
{
const FScopedTransaction Transaction( LOCTEXT( "DeleteBodiesBelow", "Delete Bodies Below" ) );
// Now remove each one
for (UBodySetup* BodySetup : BodySetups)
{
// Use PhysicsAssetEditor function to delete action (so undo works etc)
int32 Index = SharedData->PhysicsAsset->FindBodyIndex(BodySetup->BoneName);
if(Index != INDEX_NONE)
{
SharedData->DeleteBody(Index, false);
}
}
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
}
}
void FPhysicsAssetEditor::OnDeleteSelection()
{
SharedData->DeleteCurrentSelection();
RecreatePhysicsState();
}
void FPhysicsAssetEditor::OnCycleConstraintOrientation()
{
if(SharedData->GetSelectedConstraint())
{
SharedData->CycleCurrentConstraintOrientation();
}
}
void FPhysicsAssetEditor::OnCycleConstraintActive()
{
if(SharedData->GetSelectedConstraint())
{
SharedData->CycleCurrentConstraintActive();
}
}
void FPhysicsAssetEditor::OnToggleSwing1()
{
if(SharedData->GetSelectedConstraint())
{
SharedData->ToggleConstraint(FPhysicsAssetEditorSharedData::PCT_Swing1);
}
}
void FPhysicsAssetEditor::OnToggleSwing2()
{
if(SharedData->GetSelectedConstraint())
{
SharedData->ToggleConstraint(FPhysicsAssetEditorSharedData::PCT_Swing2);
}
}
void FPhysicsAssetEditor::OnToggleTwist()
{
if(SharedData->GetSelectedConstraint())
{
SharedData->ToggleConstraint(FPhysicsAssetEditorSharedData::PCT_Twist);
}
}
bool FPhysicsAssetEditor::IsSwing1Locked() const
{
return SharedData->IsAngularConstraintLocked(FPhysicsAssetEditorSharedData::PCT_Swing1);
}
bool FPhysicsAssetEditor::IsSwing2Locked() const
{
return SharedData->IsAngularConstraintLocked(FPhysicsAssetEditorSharedData::PCT_Swing2);
}
bool FPhysicsAssetEditor::IsTwistLocked() const
{
return SharedData->IsAngularConstraintLocked(FPhysicsAssetEditorSharedData::PCT_Twist);
}
TSharedRef<SWidget> FPhysicsAssetEditor::BuildStaticMeshAssetPicker()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
FAssetPickerConfig AssetPickerConfig;
AssetPickerConfig.Filter.ClassPaths.Add(UStaticMesh::StaticClass()->GetClassPathName());
AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateSP(this, &FPhysicsAssetEditor::OnAssetSelectedFromStaticMeshAssetPicker);
AssetPickerConfig.bAllowNullSelection = true;
AssetPickerConfig.InitialAssetViewType = EAssetViewType::List;
AssetPickerConfig.bFocusSearchBoxWhenOpened = true;
AssetPickerConfig.bShowBottomToolbar = false;
AssetPickerConfig.SelectionMode = ESelectionMode::Single;
return SNew(SBox)
.IsEnabled(this, &FPhysicsAssetEditor::IsNotSimulation)
.WidthOverride(300)
.HeightOverride(400)
[
ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig)
];
}
void FPhysicsAssetEditor::OnAssetSelectedFromStaticMeshAssetPicker( const FAssetData& AssetData )
{
FSlateApplication::Get().DismissAllMenus();
const FScopedTransaction Transaction( LOCTEXT("Import Convex", "Import Convex") );
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands();
// get select bones
TArray<TSharedPtr<ISkeletonTreeItem>> Items = SkeletonTree->GetSelectedItems();
FSkeletonTreeSelection Selection(Items);
TArray<TSharedPtr<ISkeletonTreeItem>> BoneItems = Selection.GetSelectedItemsByTypeId("FSkeletonTreeBoneItem");
// gather all the body indices from both the body and bone selection
// make sure to create a body setup if we encounter a bone with no associated body
TSet<int32> BodyIndicesToUpdate;
if (SharedData->GetSelectedBodyOrPrimitive() || BoneItems.Num() > 0)
{
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SharedData->UniqueSelectionReferencingBodies())
{
BodyIndicesToUpdate.Add(SelectedBody.Index);
}
for (TSharedPtr<ISkeletonTreeItem> BoneItem : BoneItems)
{
UBoneProxy* BoneProxy = CastChecked<UBoneProxy>(BoneItem->GetObject());
int32 BodyIndex = SharedData->PhysicsAsset->FindBodyIndex(BoneProxy->BoneName);
if (BodyIndex == INDEX_NONE)
{
// no associated body found, let's create one
const FPhysAssetCreateParams& NewBodyData = GetDefault<UPhysicsAssetGenerationSettings>()->CreateParams;
BodyIndex = FPhysicsAssetUtils::CreateNewBody(SharedData->PhysicsAsset, BoneProxy->BoneName, NewBodyData);
}
BodyIndicesToUpdate.Add(BodyIndex);
}
}
if (BodyIndicesToUpdate.Num() > 0)
{
UStaticMesh* SM = Cast<UStaticMesh>(AssetData.GetAsset());
if (SM && SM->GetBodySetup() && SM->GetBodySetup()->AggGeom.GetElementCount() > 0)
{
SharedData->PhysicsAsset->Modify();
for (int32 BodyIndex: BodyIndicesToUpdate)
{
UBodySetup* BaseSetup = SharedData->PhysicsAsset->SkeletalBodySetups[BodyIndex];
BaseSetup->Modify();
BaseSetup->AddCollisionFrom(SM->GetBodySetup());
BaseSetup->InvalidatePhysicsData();
BaseSetup->CreatePhysicsMeshes();
}
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
RefreshHierachyTree();
}
else
{
UE_LOG(LogPhysics, Warning, TEXT("Failed to import body from static mesh %s. Mesh probably has no collision setup."), *AssetData.AssetName.ToString());
}
}
}
TSharedRef<SWidget> FPhysicsAssetEditor::BuildPhysicalMaterialAssetPicker(bool bForAllBodies)
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
FAssetPickerConfig AssetPickerConfig;
AssetPickerConfig.Filter.ClassPaths.Add(UPhysicalMaterial::StaticClass()->GetClassPathName());
AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateSP(this, &FPhysicsAssetEditor::OnAssetSelectedFromPhysicalMaterialAssetPicker, bForAllBodies);
AssetPickerConfig.bAllowNullSelection = true;
AssetPickerConfig.InitialAssetViewType = EAssetViewType::List;
AssetPickerConfig.bFocusSearchBoxWhenOpened = true;
AssetPickerConfig.bShowBottomToolbar = false;
AssetPickerConfig.SelectionMode = ESelectionMode::Single;
// Find a suitable default if any
UPhysicalMaterial* SelectedPhysicalMaterial = nullptr;
if(bForAllBodies)
{
if(SharedData->PhysicsAsset->SkeletalBodySetups.Num() > 0)
{
SelectedPhysicalMaterial = SharedData->PhysicsAsset->SkeletalBodySetups[0]->PhysMaterial;
for (int32 SelectedBodyIndex = 0; SelectedBodyIndex < SharedData->PhysicsAsset->SkeletalBodySetups.Num(); ++SelectedBodyIndex)
{
USkeletalBodySetup* BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBodyIndex];
if(BodySetup->PhysMaterial != SelectedPhysicalMaterial)
{
SelectedPhysicalMaterial = nullptr;
break;
}
}
}
}
else
{
if(UPhysicsAssetEditorSelection::UniqueIterator SelectedBodyItr = SharedData->UniqueSelectionReferencingBodies().CreateConstIterator())
{
SelectedPhysicalMaterial = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBodyItr->Index]->PhysMaterial;
for ( ; SelectedBodyItr; ++SelectedBodyItr)
{
const FPhysicsAssetEditorSharedData::FSelection& SelectedBody = *SelectedBodyItr;
const USkeletalBodySetup* const BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBody.Index];
if(BodySetup->PhysMaterial != SelectedPhysicalMaterial)
{
SelectedPhysicalMaterial = nullptr;
break;
}
}
}
}
AssetPickerConfig.InitialAssetSelection = FAssetData(SelectedPhysicalMaterial);
return SNew(SBox)
.IsEnabled(this, &FPhysicsAssetEditor::IsNotSimulation)
.WidthOverride(300)
.HeightOverride(400)
[
ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig)
];
}
void FPhysicsAssetEditor::OnAssetSelectedFromPhysicalMaterialAssetPicker( const FAssetData& AssetData, bool bForAllBodies )
{
FSlateApplication::Get().DismissAllMenus();
if (SharedData->GetSelectedBodyOrPrimitive() || bForAllBodies)
{
const FScopedTransaction Transaction(LOCTEXT("SetPhysicalMaterial", "Set Physical Material"));
UPhysicalMaterial* PhysicalMaterial = Cast<UPhysicalMaterial>(AssetData.GetAsset());
if(PhysicalMaterial)
{
if(bForAllBodies)
{
for (int32 SelectedBodyIndex = 0; SelectedBodyIndex < SharedData->PhysicsAsset->SkeletalBodySetups.Num(); ++SelectedBodyIndex)
{
USkeletalBodySetup* BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBodyIndex];
BodySetup->Modify();
BodySetup->PhysMaterial = PhysicalMaterial;
}
}
else
{
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SharedData->UniqueSelectionReferencingBodies())
{
USkeletalBodySetup* BodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBody.Index];
BodySetup->Modify();
BodySetup->PhysMaterial = PhysicalMaterial;
}
}
}
}
}
void FPhysicsAssetEditor::OnSelectAllBodies()
{
UPhysicsAsset * const PhysicsAsset = SharedData->EditorSkelComp->GetPhysicsAsset();
// Block selection broadcast until we have selected all, as this can be an expensive operation
FScopedBulkSelection BulkSelection(SharedData);
//go through every body and add every geom
TArray<int32> NewSelectedBodies;
for (int32 i = 0; i < PhysicsAsset->SkeletalBodySetups.Num(); ++i)
{
NewSelectedBodies.Add(i);
}
//first deselect everything
SharedData->ClearSelectedBody();
SharedData->SetSelectedBodiesAllPrimitive(NewSelectedBodies, true);
}
void FPhysicsAssetEditor::OnSelectKinematicBodies()
{
OnSelectBodies(EPhysicsType::PhysType_Kinematic);
}
void FPhysicsAssetEditor::OnSelectSimulatedBodies()
{
OnSelectBodies(EPhysicsType::PhysType_Simulated);
}
void FPhysicsAssetEditor::OnSelectBodies(EPhysicsType PhysicsType)
{
UPhysicsAsset * const PhysicsAsset = SharedData->EditorSkelComp->GetPhysicsAsset();
// Block selection broadcast until we have selected all, as this can be an expensive operation
FScopedBulkSelection BulkSelection(SharedData);
//go through every body and add every geom
TArray<int32> NewSelectedBodies;
for (int32 i = 0; i < PhysicsAsset->SkeletalBodySetups.Num(); ++i)
{
int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(PhysicsAsset->SkeletalBodySetups[i]->BoneName);
if (PhysicsAsset->SkeletalBodySetups[i]->PhysicsType == PhysicsType)
{
NewSelectedBodies.Add(i);
}
}
//first deselect everything
SharedData->ClearSelectedBody();
SharedData->SetSelectedBodiesAllPrimitive(NewSelectedBodies, true);
}
void FPhysicsAssetEditor::OnSelectShapes(const ECollisionEnabled::Type CollisionEnabled)
{
UPhysicsAsset * const PhysicsAsset = SharedData->EditorSkelComp->GetPhysicsAsset();
TSet<int32> SelectedBodyIndices;
for (const FPhysicsAssetEditorSharedData::FSelection& SelectedBody : SharedData->UniqueSelectionReferencingBodies())
{
SelectedBodyIndices.Add(SelectedBody.Index);
}
SharedData->ClearSelectedBody();
SharedData->SetSelectedBodiesPrimitivesWithCollisionType(SelectedBodyIndices.Array(), CollisionEnabled, true);
}
bool FPhysicsAssetEditor::LoadMLLevelSetFromDataTable(UDataTable* DataTableMLLevelSetModelBoneBinningInfo, TArray<FString>& ErrorMessages)
{
USkeletalMesh* EditorSkelMesh = GetSharedData()->PhysicsAsset->GetPreviewMesh();
bool bCanLoadAll=true;
if (DataTableMLLevelSetModelBoneBinningInfo && EditorSkelMesh != nullptr)
{
for (FName RowName : DataTableMLLevelSetModelBoneBinningInfo->GetRowNames())
{
bool bCanLoadRow = true;
FMLLevelSetModelAndBonesBinningInfo* Row = DataTableMLLevelSetModelBoneBinningInfo->FindRow<FMLLevelSetModelAndBonesBinningInfo>(RowName, TEXT("GENERAL"));
if (Row)
{
const FName ParentBoneName = FName(*(Row->ParentBoneName));
const FString ActiveBoneNamesStringUnited = (*(Row->ActiveBoneNames));
TArray<FString> ActiveBoneNamesStringArray;
ActiveBoneNamesStringUnited.ParseIntoArray(ActiveBoneNamesStringArray, TEXT(","), true);
TArray<FName> ActiveBoneNames;
for (int32 i = 0; i < ActiveBoneNamesStringArray.Num(); i++)
{
ActiveBoneNames.Add(FName(ActiveBoneNamesStringArray[i]));
}
const int NumberOfActiveJoints = ActiveBoneNames.Num();
if (NumberOfActiveJoints == 0)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". There must be at least one Active Joint");
ErrorMessages.Add(ErrorMessage);
}
if (Row->DebugGridResolution.Num() != 3)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". DebugGridResolution must be TArray of 3 int values.");
ErrorMessages.Add(ErrorMessage);
}
if (Row->DebugGridResolution[0] < 5 || Row->DebugGridResolution[1] < 5 || Row->DebugGridResolution[2] < 5)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". DebugGridResolution must be at least 5 in each component.");
ErrorMessages.Add(ErrorMessage);
}
if (Row->SignedDistanceScaling <= 0.0)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". SignedDistanceScaling must be a positive number.");
ErrorMessages.Add(ErrorMessage);
}
if (Row->TrainingGridOrigin.Num() != 3)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". TrainingGridOrigin cannot be imported. It must contain exactly 3 values.");
ErrorMessages.Add(ErrorMessage);
}
if (Row->TrainingGridAxisX.Num() != 3)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". TrainingGridAxisX cannot be imported. It must contain exactly 3 values.");
ErrorMessages.Add(ErrorMessage);
}
if (Row->TrainingGridAxisY.Num() != 3)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". TrainingGridAxisY cannot be imported. It must contain exactly 3 values.");
ErrorMessages.Add(ErrorMessage);
}
if (Row->TrainingGridAxisZ.Num() != 3)
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". TrainingGridAxisZ cannot be imported. It must contain exactly 3 values.");
ErrorMessages.Add(ErrorMessage);
}
//Check the initial joint rotations and translations matches with the number of active joints
if (NumberOfActiveJoints * 3 != Row->ReferenceBoneRotations.Num())
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". ReferenceBoneRotations cannot be imported. It must contain exactly 3 values per active joint.");
ErrorMessages.Add(ErrorMessage);
}
if (NumberOfActiveJoints * 3 != Row->ReferenceBoneTranslations.Num())
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". ReferenceBoneTranslations cannot be imported. It must contain exactly 3 values per active joint.");
ErrorMessages.Add(ErrorMessage);
}
if (NumberOfActiveJoints != Row->NumberOfRotationComponentsPerBone.Num())
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". NumberOfRotationComponentsPerBone cannot be imported. It must contain exactly 1 value per active joint.");
ErrorMessages.Add(ErrorMessage);
}
int32 TotalNumberOfActiveRotationComponents = 0;
for (int32 i = 0; i < Row->NumberOfRotationComponentsPerBone.Num(); i++)
{
TotalNumberOfActiveRotationComponents += Row->NumberOfRotationComponentsPerBone[i];
}
if (TotalNumberOfActiveRotationComponents != Row->RotationComponentIndexes.Num())
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". RotationComponentIndexes does not match with NumberOfRotationComponentsPerBone.");
ErrorMessages.Add(ErrorMessage);
}
if(bCanLoadRow)
{
const double SignedDistanceScaling = Row->SignedDistanceScaling;
FIntVector DebugGridResolution = { Row->DebugGridResolution[0],Row->DebugGridResolution[1], Row->DebugGridResolution[2] };
const FVector3f TrainingGridMin = { Row->TrainingGridOrigin[0], Row->TrainingGridOrigin[1], Row->TrainingGridOrigin[2] };
TArray<FVector3f> TrainingGridAxes;
TrainingGridAxes.SetNum(3);
TrainingGridAxes[0] = FVector3f(Row->TrainingGridAxisX[0], Row->TrainingGridAxisX[1], Row->TrainingGridAxisX[2]);
TrainingGridAxes[1] = FVector3f(Row->TrainingGridAxisY[0], Row->TrainingGridAxisY[1], Row->TrainingGridAxisY[2]);
TrainingGridAxes[2] = FVector3f(Row->TrainingGridAxisZ[0], Row->TrainingGridAxisZ[1], Row->TrainingGridAxisZ[2]);
TArray<FVector3f> ReferenceBoneRotations;
TArray<FVector3f> ReferenceBoneTranslations;
ReferenceBoneRotations.SetNum(NumberOfActiveJoints);
ReferenceBoneTranslations.SetNum(NumberOfActiveJoints);
for (int32 i = 0; i < NumberOfActiveJoints; i++)
{
ReferenceBoneRotations[i] = FVector3f(Row->ReferenceBoneRotations[3 * i], Row->ReferenceBoneRotations[3 * i + 1], Row->ReferenceBoneRotations[3 * i + 2]);
ReferenceBoneTranslations[i] = FVector3f(Row->ReferenceBoneTranslations[3 * i], Row->ReferenceBoneTranslations[3 * i + 1], Row->ReferenceBoneTranslations[3 * i + 2]);
}
TArray<TArray<int32>> ActiveBonesJointRotationComponents;
ActiveBonesJointRotationComponents.SetNum(NumberOfActiveJoints);
int32 ActiveRotationComponentIndexHelper = 0;
for (int32 i = 0; i < NumberOfActiveJoints; i++)
{
ActiveBonesJointRotationComponents[i].SetNum(Row->NumberOfRotationComponentsPerBone[i]);
for (int32 j = 0; j < ActiveBonesJointRotationComponents[i].Num(); j++)
{
ActiveBonesJointRotationComponents[i][j] = Row->RotationComponentIndexes[ActiveRotationComponentIndexHelper + j];
}
ActiveRotationComponentIndexHelper += Row->NumberOfRotationComponentsPerBone[i];
}
UDataTable* MLLevelSetModelInferenceInfo = LoadObject<UDataTable>(nullptr, *(Row->MLModelInferenceInfoDataTablePath));
if (MLLevelSetModelInferenceInfo)
{
FMLLevelSetModelInferenceInfo* RowModelInferenceInfo = MLLevelSetModelInferenceInfo->FindRow<FMLLevelSetModelInferenceInfo>(FName(Row->MLModelInferenceInfoDataTableIndex), TEXT("GENERAL"));
if (RowModelInferenceInfo)
{
const FString NNIModelDir = RowModelInferenceInfo->NNEModelPath;
const FString MLModelWeightsString = RowModelInferenceInfo->MLModelWeights;
const int32 ParentBoneIndex = EditorSkelMesh->GetRefSkeleton().FindBoneIndex(ParentBoneName);
if (ParentBoneIndex == INDEX_NONE)
{
UE_LOG(LogChaos, Error, TEXT("(Parent) Bone Index index with name %s is not found for the MLLevelSet."), *ParentBoneName.ToString());
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". ParentBoneIndex is not found.");
ErrorMessages.Add(ErrorMessage);
}
else
{
int32 ParentBodyIndex = GetSharedData()->PhysicsAsset->FindBodyIndex(ParentBoneName);
// Body associated with parent body does not exist. Create new body.
if(ParentBodyIndex == INDEX_NONE)
{
FPhysAssetCreateParams NewBodyData = GetDefault<UPhysicsAssetGenerationSettings>()->CreateParams;
NewBodyData.GeomType = EPhysAssetFitGeomType::EFG_MLLevelSet;
ParentBodyIndex = FPhysicsAssetUtils::CreateNewBody(SharedData->PhysicsAsset,ParentBoneName, NewBodyData);
}
if (ParentBodyIndex == INDEX_NONE)
{
UE_LOG(LogChaos, Error, TEXT("Controlling Body with index %d is not found or cannot be created for MLLevelSet."), ParentBodyIndex);
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". Controlling Body is not found or cannot be created for MLLevelSet.");
ErrorMessages.Add(ErrorMessage);
}
else
{
const FReferenceSkeleton& RefSkeleton = EditorSkelMesh->GetRefSkeleton();
UE_LOG(LogChaos, Display, TEXT("MLLevelSet is imported and attached to ParentBone =%s with index %d"), *ParentBoneName.ToString(), ParentBoneIndex);
UBodySetup* DestBody = GetSharedData()->PhysicsAsset->SkeletalBodySetups[ParentBodyIndex];
DestBody->Modify();
FKMLLevelSetElem MLLevelsetElem;
FMLLevelSetModelInferenceInfo* RowModelInferenceForIncorrectZoneInfo = MLLevelSetModelInferenceInfo->FindRow<FMLLevelSetModelInferenceInfo>(FName(Row->MLModelInferenceForIncorrectZoneInfoDataTableIndex), TEXT("GENERAL"));
if (RowModelInferenceForIncorrectZoneInfo)
{
TArray<Chaos::FMLLevelSetNNEModelData> NNEModelDataArr;
NNEModelDataArr.Add({ RowModelInferenceInfo->ModelArchitectureActivationNodeSizes, MLModelWeightsString, NNIModelDir, nullptr});
const FString NNIModelIncorrectZoneDir = RowModelInferenceForIncorrectZoneInfo->NNEModelPath;
const FString MLModelIncorrectZoneWeightsString = RowModelInferenceForIncorrectZoneInfo->MLModelWeights;
NNEModelDataArr.Add({ RowModelInferenceForIncorrectZoneInfo->ModelArchitectureActivationNodeSizes, MLModelIncorrectZoneWeightsString, NNIModelIncorrectZoneDir, nullptr });
Chaos::FMLLevelSetImportData MLLevelSetImportData = { ActiveBoneNames, MoveTemp(NNEModelDataArr),
MoveTemp(ReferenceBoneRotations), MoveTemp(ReferenceBoneTranslations),
SignedDistanceScaling, MoveTemp(ActiveBonesJointRotationComponents),TrainingGridMin, TrainingGridAxes, DebugGridResolution };
MLLevelsetElem.BuildMLLevelSet(MoveTemp(MLLevelSetImportData));
}
// No Incorrect Zone Model.
else
{
TArray<Chaos::FMLLevelSetNNEModelData> NNEModelDataArr;
NNEModelDataArr.Add({ RowModelInferenceInfo->ModelArchitectureActivationNodeSizes, MLModelWeightsString, NNIModelDir, nullptr });
Chaos::FMLLevelSetImportData MLLevelSetImportData = { ActiveBoneNames, MoveTemp(NNEModelDataArr),
MoveTemp(ReferenceBoneRotations), MoveTemp(ReferenceBoneTranslations),
SignedDistanceScaling, MoveTemp(ActiveBonesJointRotationComponents),TrainingGridMin, TrainingGridAxes, DebugGridResolution };
MLLevelsetElem.BuildMLLevelSet(MoveTemp(MLLevelSetImportData));
}
DestBody->AggGeom.MLLevelSetElems.Add(MLLevelsetElem);
RecreatePhysicsState();
RefreshHierachyTree();
RefreshPreviewViewport();
}
}
}
else
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". Data Table for MLModelInferenceInfo could not be loaded.");
ErrorMessages.Add(ErrorMessage);
}
}
else
{
bCanLoadRow = false;
FString ErrorMessage = FString("Error in Row ") + RowName.ToString() + FString(". Data Table for MLModelInferenceInfo could not be loaded. See MLModelInferenceInfoDataTablePath.");
ErrorMessages.Add(ErrorMessage);
}
}
}
else
{
bCanLoadRow = false;
FString ErrorMessage = FString("Row ") + RowName.ToString() + FString(" cannot be imported. Wrong Data Table type imported. Data Table has to be FMLLevelSetModelAndBonesBinningInfo");
ErrorMessages.Add(ErrorMessage);
}
bCanLoadAll = bCanLoadAll ? bCanLoadRow : false;
}
}
else
{
ErrorMessages.Add(FString("EditorSkelMesh could not be loaded."));
bCanLoadAll = false;
}
return bCanLoadAll;
}
void FPhysicsAssetEditor::GenerateSkinnedTriangleMesh()
{
USkeletalMesh* const EditorSkelMesh = SharedData->PhysicsAsset->GetPreviewMesh();
if (EditorSkelMesh == nullptr)
{
return;
}
int32 LODIndex = 0;
if (SharedData->EditorSkelComp)
{
LODIndex = SharedData->EditorSkelComp->GetPredictedLODLevel();
}
if (!ensure(EditorSkelMesh->IsValidLODIndex(LODIndex)))
{
return;
}
// Make sure rendering is done - so we are not changing data being used by collision drawing.
FlushRenderingCommands();
const FPhysAssetCreateParams& NewBodyData = GetDefault<UPhysicsAssetGenerationSettings>()->CreateParams;
{
const FScopedTransaction Transaction(LOCTEXT("GenerateSkinnedTriangleMesh", "Generate Skinned Triangle Mesh"));
SharedData->PhysicsAsset->Modify();
FText ErrorMessage;
const int32 BodyIndex = FPhysicsAssetUtils::GenerateSkinnedTriangleMesh(SharedData->PhysicsAsset, EditorSkelMesh, LODIndex, NewBodyData, ErrorMessage);
if (BodyIndex == INDEX_NONE)
{
FMessageDialog::Open(EAppMsgType::Ok, ErrorMessage);
}
else
{
SharedData->AutoNamePrimitive(BodyIndex, EAggCollisionShape::SkinnedTriangleMesh);
SharedData->ModifySelectedBodies(BodyIndex, true);
}
}
RecreatePhysicsState();
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
RefreshPreviewViewport();
RefreshHierachyTree();
}
void FPhysicsAssetEditor::ImportMLLevelSetFromDataTable()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
FOpenAssetDialogConfig OpenAssetDialogConfig;
OpenAssetDialogConfig.DialogTitleOverride = FText::FromString(TEXT("Select DataTable for MLLevelSet Import"));
OpenAssetDialogConfig.AssetClassNames.Add(UDataTable::StaticClass()->GetClassPathName());
OpenAssetDialogConfig.bAllowMultipleSelection = false;
TArray<FAssetData> SelectedDataTable = ContentBrowserModule.Get().CreateModalOpenAssetDialog(OpenAssetDialogConfig);
TArray<FString> ErrorMessages;
if (SelectedDataTable.Num() > 0)
{
FAssetData SelectedAsset = SelectedDataTable[0];
if (SelectedDataTable[0].IsValid())
{
UDataTable* DataTableMLLevelSetModelBoneBinningInfo = LoadObject<UDataTable>(nullptr, *SelectedDataTable[0].GetObjectPathString());
if (DataTableMLLevelSetModelBoneBinningInfo)
{
LoadMLLevelSetFromDataTable(DataTableMLLevelSetModelBoneBinningInfo, ErrorMessages);
}
else
{
ErrorMessages.Add(FString("Data Table could not be loaded."));
}
}
else
{
ErrorMessages.Add(FString("Selected Data Table is not valid."));
}
}
else
{
ErrorMessages.Add(FString("Data Table could not be selected."));
}
if (ErrorMessages.Num() >= 1)
{
FString ErrorMessagesStr = "";
for (FString ErrorMessage : ErrorMessages)
{
ErrorMessagesStr += ErrorMessage + FString("\n");
}
FMessageDialog::Open(EAppMsgType::Ok, FText::Format(LOCTEXT("MLLevelSetDataTableErrorMessage", "{0}"), FText::FromString(ErrorMessagesStr)));
}
}
void FPhysicsAssetEditor::OnSelectAllConstraints()
{
UPhysicsAsset * const PhysicsAsset = SharedData->EditorSkelComp->GetPhysicsAsset();
// Block selection broadcast until we have selected all, as this can be an expensive operation
FScopedBulkSelection BulkSelection(SharedData);
//go through every constraint and add it
TArray<int32> NewSelectedConstraints;
for (int32 i = 0; i < PhysicsAsset->ConstraintSetup.Num(); ++i)
{
int32 BoneIndex1 = SharedData->EditorSkelComp->GetBoneIndex(PhysicsAsset->ConstraintSetup[i]->DefaultInstance.ConstraintBone1);
int32 BoneIndex2 = SharedData->EditorSkelComp->GetBoneIndex(PhysicsAsset->ConstraintSetup[i]->DefaultInstance.ConstraintBone2);
// if bone doesn't exist, do not draw it. It crashes in random points when we try to manipulate.
if (BoneIndex1 != INDEX_NONE && BoneIndex2 != INDEX_NONE)
{
NewSelectedConstraints.Add(i);
}
}
//Deselect everything first
SharedData->ClearSelectedConstraints();
SharedData->ModifySelectedConstraints(NewSelectedConstraints, true);
}
void FPhysicsAssetEditor::OnToggleCreateConstraintsWhenCreatingBodies()
{
SharedData->EditorOptions->bCreateConstraintsWhenCreatingBodiesFromSkeletonTree = !SharedData->EditorOptions->bCreateConstraintsWhenCreatingBodiesFromSkeletonTree;
SharedData->EditorOptions->SaveConfig();
}
void FPhysicsAssetEditor::OnToggleSelectionType(bool bIgnoreUserConstraints)
{
SharedData->ToggleSelectionType(bIgnoreUserConstraints);
}
void FPhysicsAssetEditor::OnToggleShowSelected()
{
SharedData->ToggleShowSelected();
}
void FPhysicsAssetEditor::OnShowSelected()
{
SharedData->ShowSelected();
}
void FPhysicsAssetEditor::OnHideSelected()
{
SharedData->HideSelected();
}
void FPhysicsAssetEditor::OnToggleShowOnlyColliding()
{
SharedData->ToggleShowOnlyColliding();
}
void FPhysicsAssetEditor::OnToggleShowOnlyConstrained()
{
SharedData->ToggleShowOnlyConstrained();
}
void FPhysicsAssetEditor::OnToggleShowOnlySelected()
{
SharedData->ToggleShowOnlySelected();
}
void FPhysicsAssetEditor::OnShowAll()
{
SharedData->ShowAll();
}
void FPhysicsAssetEditor::OnHideAll()
{
SharedData->HideAll();
}
void FPhysicsAssetEditor::OnDeselectAll()
{
SharedData->ClearSelectedBody();
SharedData->ClearSelectedConstraints();
}
bool FPhysicsAssetEditor::ShouldCreateConstraintsWhenCreatingBodies() const
{
return SharedData->EditorOptions->bCreateConstraintsWhenCreatingBodiesFromSkeletonTree;
}
// record if simulating or not, or mode changed or not, or what mode it is in while simulating and what kind of simulation options
void FPhysicsAssetEditor::OnAddPhatRecord(const FString& Action, bool bRecordSimulate, bool bRecordMode)
{
// Don't attempt to report usage stats if analytics isn't available
if( Action.IsEmpty() == false && SharedData.IsValid() && FEngineAnalytics::IsAvailable())
{
TArray<FAnalyticsEventAttribute> Attribs;
if (bRecordSimulate)
{
Attribs.Add(FAnalyticsEventAttribute(TEXT("Simulation"), SharedData->bRunningSimulation? TEXT("ON") : TEXT("OFF")));
if ( SharedData->bRunningSimulation )
{
Attribs.Add(FAnalyticsEventAttribute(TEXT("Selected"), IsSelectedSimulation()? TEXT("ON") : TEXT("OFF")));
Attribs.Add(FAnalyticsEventAttribute(TEXT("Gravity"), SharedData->bNoGravitySimulation ? TEXT("ON") : TEXT("OFF")));
}
}
FString EventString = FString::Printf(TEXT("Editor.Usage.PHAT.%s"), *Action);
FEngineAnalytics::GetProvider().RecordEvent(EventString, Attribs);
}
}
void FPhysicsAssetEditor::Tick(float DeltaTime)
{
GetPersonaToolkit()->GetPreviewScene()->InvalidateViews();
}
TStatId FPhysicsAssetEditor::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FPhysicsAssetEditor, STATGROUP_Tickables);
}
void FPhysicsAssetEditor::HandleDetailsCreated(const TSharedRef<class IDetailsView>& InDetailsView)
{
PhysAssetProperties = InDetailsView;
PhysAssetProperties->SetObject(nullptr);
PhysAssetProperties->OnFinishedChangingProperties().AddSP(this, &FPhysicsAssetEditor::OnFinishedChangingProperties);
PhysAssetProperties->SetEnabled(TAttribute<bool>::Create(TAttribute<bool>::FGetter::CreateLambda([this](){ return !SharedData->bRunningSimulation; })));
}
void FPhysicsAssetEditor::HandlePhysicsAssetGraphCreated(const TSharedRef<SPhysicsAssetGraph>& InPhysicsAssetGraph)
{
PhysicsAssetGraph = InPhysicsAssetGraph;
}
void FPhysicsAssetEditor::HandleGraphObjectsSelected(const TArrayView<UObject*>& InObjects)
{
if (!bSelecting)
{
TGuardValue<bool> RecursionGuard(bSelecting, true);
SkeletonTree->DeselectAll();
TArray<UObject*> Objects;
Algo::TransformIf(InObjects, Objects, [](UObject* InItem) { return InItem != nullptr; }, [](UObject* InItem) { return InItem; });
if (PhysAssetProperties.IsValid())
{
PhysAssetProperties->SetObjects(Objects);
}
// Block selection broadcast until we have selected all, as this can be an expensive operation
FScopedBulkSelection BulkSelection(SharedData);
// clear selection
SharedData->ClearSelected();
TArray<USkeletalBodySetup*> SelectedBodySetups;
TArray<UPhysicsConstraintTemplate*> SelectedConstraintTemplates;
TArray<int32> SelectedBodyIndices;
TArray<int32> SelectedConstraintIndices;
for (UObject* SelectedObject : Objects)
{
if (USkeletalBodySetup* BodySetup = Cast<USkeletalBodySetup>(SelectedObject))
{
SelectedBodySetups.Add(BodySetup);
for (int32 BodySetupIndex = 0; BodySetupIndex < SharedData->PhysicsAsset->SkeletalBodySetups.Num(); ++BodySetupIndex)
{
if (SharedData->PhysicsAsset->SkeletalBodySetups[BodySetupIndex] == BodySetup)
{
SelectedBodyIndices.AddUnique(BodySetupIndex);
}
}
}
else if (UPhysicsConstraintTemplate* Constraint = Cast<UPhysicsConstraintTemplate>(SelectedObject))
{
SelectedConstraintTemplates.Add(Constraint);
for (int32 ConstraintIndex = 0; ConstraintIndex < SharedData->PhysicsAsset->ConstraintSetup.Num(); ++ConstraintIndex)
{
if (SharedData->PhysicsAsset->ConstraintSetup[ConstraintIndex] == Constraint)
{
SelectedConstraintIndices.AddUnique(ConstraintIndex);
}
}
}
}
TArray<FPhysicsAssetEditorSharedData::FSelection> SelectedObjects;
SelectedObjects.Append(MakeBodySelection(SharedData->PhysicsAsset, SelectedBodyIndices));
SelectedObjects.Append(MakeConstraintSelection(SelectedConstraintIndices));
SharedData->ModifySelected(SelectedObjects, true);
SkeletonTree->SelectItemsBy([&SelectedBodySetups, &SelectedConstraintTemplates](const TSharedRef<ISkeletonTreeItem>& InItem, bool& bInOutExpand)
{
if(InItem->IsOfType<FSkeletonTreePhysicsBodyItem>())
{
for (USkeletalBodySetup* SelectedBodySetup : SelectedBodySetups)
{
if (SelectedBodySetup == Cast<USkeletalBodySetup>(InItem->GetObject()))
{
bInOutExpand = true;
return true;
}
}
}
else if(InItem->IsOfType<FSkeletonTreePhysicsConstraintItem>())
{
for (UPhysicsConstraintTemplate* SelectedConstraintTemplate : SelectedConstraintTemplates)
{
if (SelectedConstraintTemplate == Cast<UPhysicsConstraintTemplate>(InItem->GetObject()))
{
bInOutExpand = true;
return true;
}
}
}
bInOutExpand = false;
return false;
});
}
}
void FPhysicsAssetEditor::HandleSelectionChanged(const TArrayView<TSharedPtr<ISkeletonTreeItem>>& InSelectedItems, ESelectInfo::Type InSelectInfo)
{
if (!bSelecting)
{
TGuardValue<bool> RecursionGuard(bSelecting, true);
// Always set the details customization object, regardless of selection type
// We do this because the tree may have been rebuilt and objects invalidated
TArray<UObject*> Objects;
Algo::TransformIf(InSelectedItems, Objects, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return InItem->GetObject() != nullptr; }, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return InItem->GetObject(); });
if (PhysAssetProperties.IsValid())
{
PhysAssetProperties->SetObjects(Objects);
}
// Only a user selection should change other view's selections
if (InSelectInfo != ESelectInfo::Direct)
{
// Block selection broadcast until we have selected all, as this can be an expensive operation
FScopedBulkSelection BulkSelection(SharedData);
bool bBoneSelected = false;
TArray<FPhysicsAssetEditorSharedData::FSelection> SelectedElements;
for (const TSharedPtr<ISkeletonTreeItem>& Item : InSelectedItems)
{
if (Item->IsOfType<FSkeletonTreePhysicsBodyItem>())
{
TSharedPtr<FSkeletonTreePhysicsBodyItem> SkeletonTreePhysicsBodyItem = StaticCastSharedPtr<FSkeletonTreePhysicsBodyItem>(Item);
const FPhysicsAssetEditorSharedData::FSelection BodySelection = MakeBodySelection(SharedData->PhysicsAsset, SkeletonTreePhysicsBodyItem->GetBodySetupIndex());
SelectedElements.Add(BodySelection);
SelectedElements.Add(MakePrimitiveSelection(BodySelection.GetIndex(), BodySelection.GetPrimitiveType(), BodySelection.GetPrimitiveIndex()));
}
else if (Item->IsOfType<FSkeletonTreePhysicsShapeItem>())
{
TSharedPtr<FSkeletonTreePhysicsShapeItem> SkeletonTreePhysicsShapeItem = StaticCastSharedPtr<FSkeletonTreePhysicsShapeItem>(Item);
SelectedElements.Add(MakePrimitiveSelection(SkeletonTreePhysicsShapeItem->GetBodySetupIndex(), SkeletonTreePhysicsShapeItem->GetShapeType(), SkeletonTreePhysicsShapeItem->GetShapeIndex()));
}
else if (Item->IsOfType<FSkeletonTreePhysicsConstraintItem>())
{
TSharedPtr<FSkeletonTreePhysicsConstraintItem> SkeletonTreePhysicsConstraintItem = StaticCastSharedPtr<FSkeletonTreePhysicsConstraintItem>(Item);
SelectedElements.Add(MakeConstraintSelection(SkeletonTreePhysicsConstraintItem->GetConstraintIndex()));
}
else if(Item->IsOfTypeByName(TEXT("FSkeletonTreeBoneItem")))
{
bBoneSelected = true;
}
}
if (SharedData->IsGroupSelectionActive())
{
SharedData->ModifySelected(SelectedElements, true);
}
else
{
SharedData->SetSelected(SelectedElements);
}
if(!bBoneSelected)
{
GetPersonaToolkit()->GetPreviewScene()->ClearSelectedBone();
}
if (PhysicsAssetGraph.IsValid())
{
TSet<USkeletalBodySetup*> Bodies;
TSet<UPhysicsConstraintTemplate*> Constraints;
Algo::TransformIf(InSelectedItems, Bodies, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return InItem->GetObject() && InItem->GetObject()->IsA<USkeletalBodySetup>(); }, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return Cast<USkeletalBodySetup>(InItem->GetObject()); });
Algo::TransformIf(InSelectedItems, Constraints, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return InItem->GetObject() && InItem->GetObject()->IsA<UPhysicsConstraintTemplate>(); }, [](const TSharedPtr<ISkeletonTreeItem>& InItem) { return Cast<UPhysicsConstraintTemplate>(InItem->GetObject()); });
PhysicsAssetGraph.Pin()->SelectObjects(Bodies.Array(), Constraints.Array());
}
}
}
}
void FPhysicsAssetEditor::HandlePreviewSceneCreated(const TSharedRef<IPersonaPreviewScene>& InPersonaPreviewScene)
{
InPersonaPreviewScene->RegisterOnPreviewMeshChanged(FOnPreviewMeshChanged::CreateSP(this, &FPhysicsAssetEditor::OnChangeDefaultMesh));
SharedData->Initialize(InPersonaPreviewScene);
AAnimationEditorPreviewActor* Actor = InPersonaPreviewScene->GetWorld()->SpawnActor<AAnimationEditorPreviewActor>(AAnimationEditorPreviewActor::StaticClass(), FTransform::Identity);
InPersonaPreviewScene->SetActor(Actor);
// Create the preview component
SharedData->EditorSkelComp = NewObject<UPhysicsAssetEditorSkeletalMeshComponent>(Actor);
SharedData->EditorSkelComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
SharedData->EditorSkelComp->SharedData = SharedData.Get();
SharedData->EditorSkelComp->SetSkeletalMesh(SharedData->PhysicsAsset->GetPreviewMesh());
SharedData->EditorSkelComp->SetPhysicsAsset(SharedData->PhysicsAsset, true);
SharedData->EditorSkelComp->SetDisablePostProcessBlueprint(true);
InPersonaPreviewScene->SetPreviewMeshComponent(SharedData->EditorSkelComp);
InPersonaPreviewScene->AddComponent(SharedData->EditorSkelComp, FTransform::Identity);
InPersonaPreviewScene->SetAdditionalMeshesSelectable(false);
// set root component, so we can attach to it.
Actor->SetRootComponent(SharedData->EditorSkelComp);
SharedData->EditorSkelComp->Stop();
SharedData->PhysicalAnimationComponent = NewObject<UPhysicalAnimationComponent>(Actor);
SharedData->PhysicalAnimationComponent->SetSkeletalMeshComponent(SharedData->EditorSkelComp);
InPersonaPreviewScene->AddComponent(SharedData->PhysicalAnimationComponent, FTransform::Identity);
SharedData->ResetTM = SharedData->EditorSkelComp->GetComponentToWorld();
// Register handle component
SharedData->MouseHandle->RegisterComponentWithWorld(InPersonaPreviewScene->GetWorld());
SharedData->EnableSimulation(false);
// we need to make sure we monitor any change to the PhysicsState being recreated, as this can happen from path that is external to this class
// (example: setting a property on a body that is type "simulated" will recreate the state from USkeletalBodySetup::PostEditChangeProperty and let the body simulating (UE-107308)
SharedData->EditorSkelComp->RegisterOnPhysicsCreatedDelegate(FOnSkelMeshPhysicsCreated::CreateLambda([this]()
{
// let's make sure nothing is simulating and that all necessary state are in proper order
SharedData->EnableSimulation(false);
}));
// Make sure the floor mesh has collision (BlockAllDynamic may have been overriden)
static FName CollisionProfileName(TEXT("PhysicsActor"));
UStaticMeshComponent* FloorMeshComponent = const_cast<UStaticMeshComponent*>(InPersonaPreviewScene->GetFloorMeshComponent());
FloorMeshComponent->SetCollisionProfileName(CollisionProfileName);
FloorMeshComponent->RecreatePhysicsState();
}
void FPhysicsAssetEditor::HandleOnPreviewSceneSettingsCustomized(IDetailLayoutBuilder& DetailBuilder) const
{
DetailBuilder.HideCategory("Animation Blueprint");
}
void FPhysicsAssetEditor::HandleExtendContextMenu(FMenuBuilder& InMenuBuilder)
{
TArray<TSharedPtr<ISkeletonTreeItem>> SelectedItems = SkeletonTree->GetSelectedItems();
FSkeletonTreeSelection Selection(SelectedItems);
TArray<TSharedPtr<FSkeletonTreePhysicsBodyItem>> SelectedBodies = Selection.GetSelectedItems<FSkeletonTreePhysicsBodyItem>();
TArray<TSharedPtr<FSkeletonTreePhysicsConstraintItem>> SelectedConstraints = Selection.GetSelectedItems<FSkeletonTreePhysicsConstraintItem>();
TArray<TSharedPtr<FSkeletonTreePhysicsShapeItem>> SelectedShapes = Selection.GetSelectedItems<FSkeletonTreePhysicsShapeItem>();
TArray<TSharedPtr<ISkeletonTreeItem>> SelectedBones = Selection.GetSelectedItemsByTypeId("FSkeletonTreeBoneItem");
if (SelectedBodies.Num() > 0)
{
BuildMenuWidgetBody(InMenuBuilder);
}
else if (SelectedShapes.Num() > 0)
{
BuildMenuWidgetPrimitives(InMenuBuilder);
}
else if(SelectedConstraints.Num() > 0)
{
BuildMenuWidgetConstraint(InMenuBuilder);
}
else if(SelectedBones.Num() > 0)
{
BuildMenuWidgetBone(InMenuBuilder, SelectedBones);
}
BuildMenuWidgetSelection(InMenuBuilder);
}
void FPhysicsAssetEditor::HandleExtendFilterMenu(FMenuBuilder& InMenuBuilder)
{
const FPhysicsAssetEditorCommands& Commands = FPhysicsAssetEditorCommands::Get();
const bool bExposeSimulationControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuSimulationControls;
const bool bExposeConstraintControls = GetDefault<UPhysicsAssetEditorOptions>()->bExposeLegacyMenuConstraintControls;
InMenuBuilder.PushCommandList(SkeletonTreeCommandList.ToSharedRef());
InMenuBuilder.BeginSection(TEXT("PhysicsAssetFilters"), LOCTEXT("PhysicsAssetFiltersHeader", "Physics Asset Filters"));
{
InMenuBuilder.AddMenuEntry(Commands.ShowBodies);
if (bExposeSimulationControls)
{
InMenuBuilder.AddMenuEntry(Commands.ShowSimulatedBodies);
}
InMenuBuilder.AddMenuEntry(Commands.ShowKinematicBodies);
if (bExposeConstraintControls)
{
InMenuBuilder.AddMenuEntry(Commands.ShowConstraints);
InMenuBuilder.AddMenuEntry(Commands.ShowParentChildConstraints);
InMenuBuilder.AddMenuEntry(Commands.ShowCrossConstraints);
}
InMenuBuilder.AddMenuEntry(Commands.ShowPrimitives);
if (bExposeConstraintControls)
{
InMenuBuilder.AddSeparator();
InMenuBuilder.AddMenuEntry(Commands.ShowConstraintsOnParentBodies);
}
}
InMenuBuilder.EndSection();
InMenuBuilder.PopCommandList();
}
void FPhysicsAssetEditor::HandleToggleShowBodies()
{
SkeletonTreeBuilder->bShowBodies = !SkeletonTreeBuilder->bShowBodies;
RefreshFilter();
}
void FPhysicsAssetEditor::HandleToggleShowSimulatedBodies()
{
SkeletonTreeBuilder->bShowSimulatedBodies = !SkeletonTreeBuilder->bShowSimulatedBodies;
RefreshFilter();
}
void FPhysicsAssetEditor::HandleToggleShowKinematicBodies()
{
SkeletonTreeBuilder->bShowKinematicBodies = !SkeletonTreeBuilder->bShowKinematicBodies;
RefreshFilter();
}
void FPhysicsAssetEditor::HandleToggleShowConstraints()
{
SkeletonTreeBuilder->bShowConstraints = !SkeletonTreeBuilder->bShowConstraints;
RefreshFilter();
}
void FPhysicsAssetEditor::HandleToggleShowCrossConstraints()
{
SkeletonTreeBuilder->bShowCrossConstraints = !SkeletonTreeBuilder->bShowCrossConstraints;
RefreshFilter();
}
void FPhysicsAssetEditor::HandleToggleShowParentChildConstraints()
{
SkeletonTreeBuilder->bShowParentChildConstraints = !SkeletonTreeBuilder->bShowParentChildConstraints;
RefreshFilter();
}
void FPhysicsAssetEditor::HandleToggleShowConstraintsOnParentBodies()
{
SkeletonTreeBuilder->bShowConstraintsOnParentBodies = !SkeletonTreeBuilder->bShowConstraintsOnParentBodies;
RefreshFilter();
}
void FPhysicsAssetEditor::HandleToggleShowPrimitives()
{
SkeletonTreeBuilder->bShowPrimitives = !SkeletonTreeBuilder->bShowPrimitives;
RefreshFilter();
}
ECheckBoxState FPhysicsAssetEditor::GetShowBodiesChecked() const
{
return SkeletonTreeBuilder->bShowBodies ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
ECheckBoxState FPhysicsAssetEditor::GetShowSimulatedBodiesChecked() const
{
return SkeletonTreeBuilder->bShowSimulatedBodies ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
ECheckBoxState FPhysicsAssetEditor::GetShowKinematicBodiesChecked() const
{
return SkeletonTreeBuilder->bShowKinematicBodies ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
ECheckBoxState FPhysicsAssetEditor::GetShowCrossConstraintsChecked() const
{
return SkeletonTreeBuilder->bShowCrossConstraints ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
ECheckBoxState FPhysicsAssetEditor::GetShowParentChildConstraintsChecked() const
{
return SkeletonTreeBuilder->bShowParentChildConstraints ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
bool FPhysicsAssetEditor::IsShowBodiesChecked() const
{
return SkeletonTreeBuilder->bShowBodies;
}
bool FPhysicsAssetEditor::IsShowConstraintsChecked() const
{
return SkeletonTreeBuilder->bShowConstraints;
}
ECheckBoxState FPhysicsAssetEditor::GetShowConstraintsOnParentBodiesChecked() const
{
return SkeletonTreeBuilder->bShowConstraintsOnParentBodies ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
ECheckBoxState FPhysicsAssetEditor::GetShowConstraintsChecked() const
{
return SkeletonTreeBuilder->bShowConstraints ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
ECheckBoxState FPhysicsAssetEditor::GetShowPrimitivesChecked() const
{
return SkeletonTreeBuilder->bShowPrimitives ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
}
void FPhysicsAssetEditor::HandleGetFilterLabel(TArray<FText>& InOutItems) const
{
if(SkeletonTreeBuilder->bShowBodies)
{
InOutItems.Add(LOCTEXT("BodiesFilterLabel", "Bodies"));
}
if(SkeletonTreeBuilder->bShowConstraints)
{
InOutItems.Add(LOCTEXT("ConstraintsFilterLabel", "Constraints"));
}
if(SkeletonTreeBuilder->bShowPrimitives)
{
InOutItems.Add(LOCTEXT("PrimitivesFilterLabel", "Primitives"));
}
}
void FPhysicsAssetEditor::RefreshFilter()
{
SkeletonTree->RefreshFilter();
// make sure we resynchronize the list
HandleViewportSelectionChanged(SharedData->SelectedObjects->SelectedElements());
}
void FPhysicsAssetEditor::HandleCreateNewConstraint(int32 BodyIndex0, int32 BodyIndex1)
{
if(BodyIndex0 != BodyIndex1)
{
SharedData->MakeNewConstraint(BodyIndex0, BodyIndex1);
}
}
void FPhysicsAssetEditor::RecreatePhysicsState()
{
// Flush geometry cache inside the asset (don't want to use cached version of old geometry!)
SharedData->PhysicsAsset->InvalidateAllPhysicsMeshes();
SharedData->EditorSkelComp->RecreatePhysicsState();
SharedData->EditorSkelComp->RecreateClothingActors();
// Reset simulation state of body instances so we dont actually simulate outside of 'simulation mode'
SharedData->EnableSimulation(false);
}
template<typename TValueAccessor> TSharedRef<SWidget> FPhysicsAssetEditor::MakeScaleWidget(const float MinValue, const float MaxValue, TValueAccessor ValueAccessorFunction, const FName WidgetInteractionText)
{
return
SNew(SBox)
.HAlign(HAlign_Right)
[
SNew(SBox)
.Padding(FMargin(4.0f, 0.0f, 0.0f, 0.0f))
.WidthOverride(100.0f)
[
SNew(SNumericEntryBox<float>)
.Font(FAppStyle::GetFontStyle(TEXT("MenuItem.Font")))
.AllowSpin(true)
.MinSliderValue(MinValue)
.MaxSliderValue(MaxValue)
.Value_Lambda([ValueAccessorFunction]() { return ValueAccessorFunction(); })
.OnValueChanged_Lambda([ValueAccessorFunction](float InValue) { ValueAccessorFunction() = InValue; })
.OnValueCommitted_Lambda([this, ValueAccessorFunction, WidgetInteractionText](float InValue, ETextCommit::Type InCommitType)
{
ValueAccessorFunction() = InValue;
SharedData->EditorOptions->SaveConfig();
ViewportCommandList->WidgetInteraction(WidgetInteractionText);
})
]
];
}
TSharedRef<SWidget> FPhysicsAssetEditor::MakeConstraintScaleWidget()
{
return MakeScaleWidget(0.0f, 4.0f, [this]() -> float& { return SharedData->EditorOptions->ConstraintDrawSize; }, TEXT("ConstraintScaleWidget"));
}
TSharedRef<SWidget> FPhysicsAssetEditor::MakeCoMMarkerScaleWidget()
{
return MakeScaleWidget(0.0f, 4.0f, [this]() -> float& { return SharedData->EditorOptions->COMRenderSize; }, TEXT("CoMMarkerScaleWidget"));
}
TSharedRef<SWidget> FPhysicsAssetEditor::MakeCollisionOpacityWidget()
{
return
SNew(SBox)
.HAlign(HAlign_Right)
[
SNew(SBox)
.Padding(FMargin(4.0f, 0.0f, 0.0f, 0.0f))
.WidthOverride(100.0f)
[
SNew(SNumericEntryBox<float>)
.Font(FAppStyle::GetFontStyle(TEXT("MenuItem.Font")))
.AllowSpin(true)
.MinValue(0.0f)
.MaxValue(1.0f)
.MinSliderValue(0.0f)
.MaxSliderValue(1.0f)
.Value_Lambda([this]() { return SharedData->EditorOptions->CollisionOpacity; })
.OnValueChanged_Lambda([this](float InValue) { SharedData->EditorOptions->CollisionOpacity = InValue; })
.OnValueCommitted_Lambda([this](float InValue, ETextCommit::Type InCommitType)
{
SharedData->EditorOptions->CollisionOpacity = InValue;
SharedData->EditorOptions->SaveConfig();
ViewportCommandList->WidgetInteraction(TEXT("CollisionOpacityWidget"));
})
]
];
}
#undef LOCTEXT_NAMESPACE