You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
3272 lines
94 KiB
C++
3272 lines
94 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "PhATModule.h"
|
|
#include "AssetSelection.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "ObjectTools.h"
|
|
#include "Toolkits/IToolkitHost.h"
|
|
#include "PreviewScene.h"
|
|
#include "PhATPreviewViewportClient.h"
|
|
#include "SPhATPreviewViewport.h"
|
|
#include "PhATActions.h"
|
|
#include "PhATSharedData.h"
|
|
#include "PhATEdSkeletalMeshComponent.h"
|
|
#include "PhAT.h"
|
|
|
|
#include "Editor/WorkspaceMenuStructure/Public/WorkspaceMenuStructureModule.h"
|
|
#include "Editor/PropertyEditor/Public/PropertyEditorModule.h"
|
|
#include "Editor/PropertyEditor/Public/IDetailsView.h"
|
|
#include "Editor/ContentBrowser/Public/ContentBrowserModule.h"
|
|
|
|
#include "WorkflowOrientedApp/SContentReference.h"
|
|
#include "AssetData.h"
|
|
#include "Developer/MeshUtilities/Public/MeshUtilities.h"
|
|
#include "UnrealEd.h"
|
|
|
|
#include "EngineAnalytics.h"
|
|
#include "Runtime/Analytics/Analytics/Public/Interfaces/IAnalyticsProvider.h"
|
|
#include "SDockTab.h"
|
|
#include "PhysicsEngine/BodySetup.h"
|
|
#include "PhysicsEngine/PhysicsConstraintTemplate.h"
|
|
#include "Engine/Selection.h"
|
|
#include "Engine/StaticMesh.h"
|
|
#include "EngineLogs.h"
|
|
#include "PhysicalMaterials/PhysicalMaterial.h"
|
|
DEFINE_LOG_CATEGORY(LogPhAT);
|
|
#define LOCTEXT_NAMESPACE "PhAT"
|
|
|
|
namespace PhAT
|
|
{
|
|
const float DefaultPrimSize = 15.0f;
|
|
const float DuplicateXOffset = 10.0f;
|
|
}
|
|
|
|
|
|
class FPhATTreeInfo
|
|
{
|
|
public:
|
|
FPhATTreeInfo( FName InName,
|
|
bool bInBold,
|
|
int32 InParentBoneIdx = INDEX_NONE,
|
|
int32 InBoneOrConstraintIdx = INDEX_NONE,
|
|
int32 InBodyIdx = INDEX_NONE,
|
|
int32 InCollisionIdx = INDEX_NONE,
|
|
EKCollisionPrimitiveType InCollisionType = KPT_Unknown)
|
|
: Name(InName)
|
|
, bBold(bInBold)
|
|
, ParentBoneIdx(InParentBoneIdx)
|
|
, BoneOrConstraintIdx(InBoneOrConstraintIdx)
|
|
, BodyIdx(InBodyIdx)
|
|
, CollisionIdx(InCollisionIdx)
|
|
, CollisionType(InCollisionType)
|
|
{
|
|
}
|
|
|
|
FName Name;
|
|
bool bBold;
|
|
int32 ParentBoneIdx;
|
|
int32 BoneOrConstraintIdx;
|
|
int32 BodyIdx;
|
|
int32 CollisionIdx;
|
|
EKCollisionPrimitiveType CollisionType;
|
|
};
|
|
|
|
|
|
static const FName PhATPreviewViewportName("PhAT_PreviewViewport");
|
|
static const FName PhATPropertiesName("PhAT_Properties");
|
|
static const FName PhATHierarchyName("PhAT_Hierarchy");
|
|
|
|
|
|
|
|
void FPhAT::RegisterTabSpawners(const TSharedRef<class FTabManager>& TabManager)
|
|
{
|
|
WorkspaceMenuCategory = TabManager->AddLocalWorkspaceMenuCategory( LOCTEXT( "WorkspaceMenu_PhAT", "Physics Asset Editor" ) );
|
|
auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef();
|
|
|
|
FAssetEditorToolkit::RegisterTabSpawners( TabManager );
|
|
|
|
TabManager->RegisterTabSpawner( PhATPreviewViewportName, FOnSpawnTab::CreateSP( this, &FPhAT::SpawnTab, PhATPreviewViewportName ) )
|
|
.SetDisplayName( LOCTEXT( "ViewportTab", "Viewport" ) )
|
|
.SetGroup( WorkspaceMenuCategoryRef )
|
|
.SetIcon( FSlateIcon( FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports" ) );
|
|
|
|
TabManager->RegisterTabSpawner( PhATPropertiesName, FOnSpawnTab::CreateSP( this, &FPhAT::SpawnTab, PhATPropertiesName ) )
|
|
.SetDisplayName( LOCTEXT( "PropertiesTab", "Details" ) )
|
|
.SetGroup( WorkspaceMenuCategoryRef )
|
|
.SetIcon( FSlateIcon( FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Details" ) );
|
|
|
|
TabManager->RegisterTabSpawner( PhATHierarchyName, FOnSpawnTab::CreateSP( this, &FPhAT::SpawnTab, PhATHierarchyName ) )
|
|
.SetDisplayName( LOCTEXT( "HierarchyTab", "Hierarchy" ) )
|
|
.SetGroup( WorkspaceMenuCategoryRef )
|
|
.SetIcon( FSlateIcon( FEditorStyle::GetStyleSetName(), "Kismet.Tabs.Palette" ) );
|
|
}
|
|
|
|
void FPhAT::UnregisterTabSpawners(const TSharedRef<class FTabManager>& TabManager)
|
|
{
|
|
FAssetEditorToolkit::UnregisterTabSpawners(TabManager);
|
|
|
|
TabManager->UnregisterTabSpawner(PhATPreviewViewportName);
|
|
TabManager->UnregisterTabSpawner(PhATPropertiesName);
|
|
TabManager->UnregisterTabSpawner(PhATHierarchyName);
|
|
}
|
|
|
|
TSharedRef<SDockTab> FPhAT::SpawnTab( const FSpawnTabArgs& TabSpawnArgs, FName TabIdentifier )
|
|
{
|
|
if (TabIdentifier == PhATPreviewViewportName)
|
|
{
|
|
const TSharedRef<SDockTab> SpawnedTab = SNew(SDockTab)
|
|
.Label(LOCTEXT( "PhATViewportTitle", "Viewport" ) )
|
|
[
|
|
PreviewViewport.ToSharedRef()
|
|
];
|
|
|
|
PreviewViewport->ParentTab = SpawnedTab;
|
|
|
|
return SpawnedTab;
|
|
}
|
|
else if (TabIdentifier == PhATPropertiesName)
|
|
{
|
|
return SNew(SDockTab)
|
|
.Icon(FEditorStyle::GetBrush("PhAT.Tabs.Properties"))
|
|
.Label(LOCTEXT( "PhATPropertiesTitle", "Details" ) )
|
|
[
|
|
Properties.ToSharedRef()
|
|
];
|
|
}
|
|
else if (TabIdentifier == PhATHierarchyName)
|
|
{
|
|
const TSharedRef<SDockTab> NewTab = SNew(SDockTab)
|
|
.Icon(FEditorStyle::GetBrush("PhAT.Tabs.Hierarchy"))
|
|
.Label(LOCTEXT( "PhATHierarchyTitle", "Hierarchy" ) )
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
.Padding(FMargin(5.0f, 0.0f, 0.0f, 5.0f))
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.HAlign(HAlign_Left)
|
|
[
|
|
SAssignNew(HierarchyFilter, SComboButton)
|
|
.ContentPadding(3)
|
|
.OnGetMenuContent(this, &FPhAT::BuildHierarchyFilterMenu)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &FPhAT::GetHierarchyFilter)
|
|
]
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.f)
|
|
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.Padding(FMargin(0.f, 0.f, 0.f, 0.f))
|
|
[
|
|
HierarchyControl.ToSharedRef()
|
|
]
|
|
];
|
|
|
|
RefreshHierachyTree();
|
|
|
|
return NewTab;
|
|
}
|
|
else
|
|
{
|
|
return SNew(SDockTab);
|
|
}
|
|
|
|
}
|
|
|
|
FPhAT::~FPhAT()
|
|
{
|
|
if( SharedData->bRunningSimulation )
|
|
{
|
|
// Disable simulation when shutting down
|
|
ImpToggleSimulation();
|
|
}
|
|
|
|
GEditor->UnregisterForUndo(this);
|
|
}
|
|
|
|
void FPhAT::InitPhAT(const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, UPhysicsAsset* ObjectToEdit)
|
|
{
|
|
SimulationMode = EPSM_Normal;
|
|
HierarchyFilterMode = PHFM_All;
|
|
SelectedAnimation = NULL;
|
|
SelectedSimulation = false;
|
|
BeforeSimulationWidgetMode = FWidget::EWidgetMode::WM_None;
|
|
|
|
SharedData = MakeShareable(new FPhATSharedData);
|
|
|
|
SharedData->SelectionChangedEvent.AddRaw(this, &FPhAT::SetPropertiesSelection);
|
|
SharedData->GroupSelectionChangedEvent.AddRaw(this, &FPhAT::SetPropertiesGroupSelection);
|
|
SharedData->HierarchyChangedEvent.AddRaw(this, &FPhAT::RefreshHierachyTree);
|
|
SharedData->HierarchySelectionChangedEvent.AddRaw(this, &FPhAT::RefreshHierachyTreeSelection);
|
|
SharedData->PreviewChangedEvent.AddRaw(this, &FPhAT::RefreshPreviewViewport);
|
|
|
|
SharedData->PhysicsAsset = ObjectToEdit;
|
|
|
|
SharedData->Initialize();
|
|
|
|
InsideSelChanged = false;
|
|
|
|
GEditor->RegisterForUndo(this);
|
|
|
|
// Register our commands. This will only register them if not previously registered
|
|
FPhATCommands::Register();
|
|
|
|
BindCommands();
|
|
|
|
CreateInternalWidgets();
|
|
|
|
const TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout("Standalone_PhAT_Layout_v2")
|
|
->AddArea
|
|
(
|
|
FTabManager::NewPrimaryArea()
|
|
->SetOrientation(Orient_Vertical)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetSizeCoefficient(0.1f)
|
|
->SetHideTabWell(true)
|
|
->AddTab(GetToolbarTabId(), ETabState::OpenedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()
|
|
->SetSizeCoefficient(0.9f)
|
|
->SetOrientation(Orient_Horizontal)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack()
|
|
->SetSizeCoefficient(0.8f)
|
|
->AddTab(PhATPreviewViewportName, ETabState::OpenedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewSplitter()
|
|
->SetSizeCoefficient(0.2f)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack() ->AddTab(PhATPropertiesName, ETabState::OpenedTab)
|
|
)
|
|
->Split
|
|
(
|
|
FTabManager::NewStack() ->AddTab(PhATHierarchyName, ETabState::OpenedTab)
|
|
)
|
|
|
|
)
|
|
)
|
|
);
|
|
|
|
const bool bCreateDefaultStandaloneMenu = true;
|
|
const bool bCreateDefaultToolbar = true;
|
|
FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, PhATAppIdentifier, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, ObjectToEdit);
|
|
|
|
IPhATModule* PhATModule = &FModuleManager::LoadModuleChecked<IPhATModule>( "PhAT" );
|
|
ExtendMenu();
|
|
ExtendToolbar();
|
|
RegenerateMenusAndToolbars();
|
|
|
|
// @todo toolkit world centric editing
|
|
/*if (IsWorldCentricAssetEditor())
|
|
{
|
|
SpawnToolkitTab(GetToolbarTabId(), FString(), EToolkitTabSpot::ToolBar);
|
|
SpawnToolkitTab(FName(TEXT("PhAT_PreviewViewport")), FString(), EToolkitTabSpot::Viewport);
|
|
SpawnToolkitTab(FName(TEXT("PhAT_Properties")), FString(), EToolkitTabSpot::Details);
|
|
SpawnToolkitTab(FName(TEXT("PhAT_Hierarchy")), FString(), EToolkitTabSpot::Details);
|
|
}*/
|
|
}
|
|
|
|
TSharedPtr<FPhATSharedData> FPhAT::GetSharedData() const
|
|
{
|
|
return SharedData;
|
|
}
|
|
|
|
void FPhAT::SetPropertiesSelection(UObject* Obj, FPhATSharedData::FSelection * Body)
|
|
{
|
|
if (Properties.IsValid())
|
|
{
|
|
TArray<UObject*> Selection;
|
|
Selection.Add(Obj);
|
|
Properties->SetObjects(Selection);
|
|
}
|
|
|
|
if (Hierarchy.IsValid())
|
|
{
|
|
if (Body)
|
|
{
|
|
bool bFound = false;
|
|
for (int32 ItemIdx = 0; ItemIdx < TreeElements.Num(); ++ItemIdx)
|
|
{
|
|
FPhATTreeInfo& Info = *TreeElements[ItemIdx];
|
|
if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
if ((Info.BodyIdx == Body->Index) && (Info.CollisionType == Body->PrimitiveType) && (Info.CollisionIdx == Body->PrimitiveIndex))
|
|
{
|
|
Hierarchy->ClearSelection();
|
|
Hierarchy->SetItemSelection(TreeElements[ItemIdx], true);
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Info.BoneOrConstraintIdx == Body->Index)
|
|
{
|
|
Hierarchy->ClearSelection();
|
|
Hierarchy->SetItemSelection(TreeElements[ItemIdx], true);
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bFound && (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit))
|
|
{
|
|
int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->BodySetup[Body->Index]->BoneName);
|
|
for (int32 ItemIdx = 0; ItemIdx < TreeElements.Num(); ++ItemIdx)
|
|
{
|
|
FPhATTreeInfo& Info = *TreeElements[ItemIdx];
|
|
if (Info.BoneOrConstraintIdx == BoneIndex)
|
|
{
|
|
Hierarchy->ClearSelection();
|
|
Hierarchy->SetItemSelection(TreeElements[ItemIdx], true);
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!InsideSelChanged && (Hierarchy->GetNumItemsSelected() > 0))
|
|
{
|
|
Hierarchy->RequestScrollIntoView(Hierarchy->GetSelectedItems()[0]);
|
|
}
|
|
|
|
// Couldn't find the item in the tree view
|
|
check(bFound);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FPhAT::SetPropertiesGroupSelection(const TArray<UObject*> & Objs)
|
|
{
|
|
if (Properties.IsValid())
|
|
{
|
|
Properties->SetObjects(Objs);
|
|
}
|
|
}
|
|
|
|
bool TreeElemSelected(FTreeElemPtr TreeElem, TSharedPtr<FPhATSharedData> SharedData, TSharedPtr< STreeView<FTreeElemPtr> > Hierarchy)
|
|
{
|
|
bool bIsExpanded = Hierarchy->IsItemExpanded(TreeElem);
|
|
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
if(TreeElem->BoneOrConstraintIdx != INDEX_NONE && bIsExpanded == false) //we're selecting a bone so ignore prims, but make sure to only do this if not expanded
|
|
{
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
if (SharedData->PhysicsAsset->BodySetup[SharedData->SelectedBodies[i].Index]->BoneName == (*TreeElem).Name)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}else
|
|
{
|
|
FPhATSharedData::FSelection Selection(TreeElem->BodyIdx, TreeElem->CollisionType, TreeElem->CollisionIdx);
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
if(Selection == SharedData->SelectedBodies[i])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}else
|
|
{
|
|
//for(int32 i=0; i<SharedData->SetSelectedConstraint())
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FPhAT::RefreshHierachyTreeSelection()
|
|
{
|
|
if(InsideSelChanged) //we only want to update if the change came from viewport
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(Hierarchy.Get())
|
|
{
|
|
for(int32 i=0; i<TreeElements.Num(); ++i)
|
|
{
|
|
Hierarchy->SetItemSelection(TreeElements[i], TreeElemSelected(TreeElements[i], SharedData, Hierarchy), ESelectInfo::Direct);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FPhAT::FilterTreeElement(FTreeElemPtr TreeElem) const
|
|
{
|
|
if (HierarchyFilterMode == PHFM_All)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (HierarchyFilterMode == PHFM_Bodies)
|
|
{
|
|
if (TreeElem->bBold || TreeElem->BodyIdx != INDEX_NONE)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FPhAT::RefreshHierachyTree()
|
|
{
|
|
TreeElements.Empty();
|
|
RootBone.Empty();
|
|
|
|
// if next event is selecting a bone to create a new body, Fill up tree with bone names
|
|
if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
for (int32 i = 0; i < SharedData->PhysicsAsset->BodySetup.Num(); ++i)
|
|
{
|
|
const int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->BodySetup[i]->BoneName);
|
|
if (BoneIndex != INDEX_NONE)
|
|
{
|
|
const FKAggregateGeom& AggGeom = SharedData->PhysicsAsset->BodySetup[i]->AggGeom;
|
|
|
|
if (AggGeom.SphereElems.Num() + AggGeom.BoxElems.Num() + AggGeom.SphylElems.Num() + AggGeom.ConvexElems.Num() > 0)
|
|
{
|
|
TreeElements.Add(FTreeElemPtr(new FPhATTreeInfo(SharedData->PhysicsAsset->BodySetup[i]->BoneName, true, INDEX_NONE, BoneIndex)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// fill tree with constraints
|
|
for (int32 i = 0; i < SharedData->PhysicsAsset->ConstraintSetup.Num(); ++i)
|
|
{
|
|
const UPhysicsConstraintTemplate* Setup = SharedData->PhysicsAsset->ConstraintSetup[i];
|
|
|
|
// try to find the joint name in the hierarchy
|
|
const int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(Setup->DefaultInstance.JointName);
|
|
|
|
if (BoneIndex == INDEX_NONE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TreeElements.Add(FTreeElemPtr(new FPhATTreeInfo(SharedData->EditorSkelMesh->RefSkeleton.GetBoneName(BoneIndex), true, INDEX_NONE, i)));
|
|
}
|
|
}
|
|
|
|
// Add inert bones
|
|
for (int32 BoneIndex = 0; BoneIndex < SharedData->EditorSkelMesh->RefSkeleton.GetNum(); ++BoneIndex)
|
|
{
|
|
bool bFound = false;
|
|
for (int32 ItemIdx = 0; ItemIdx < TreeElements.Num(); ++ItemIdx)
|
|
{
|
|
const FPhATTreeInfo& Info = *TreeElements[ItemIdx];
|
|
if (SharedData->EditorSkelComp->GetBoneIndex(Info.Name) == BoneIndex)
|
|
{
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
const int32 BoneOrConstraintIdx = (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)? BoneIndex: INDEX_NONE;
|
|
TreeElements.Add(FTreeElemPtr(new FPhATTreeInfo(SharedData->EditorSkelMesh->RefSkeleton.GetBoneName(BoneIndex), false, INDEX_NONE, BoneOrConstraintIdx)));
|
|
}
|
|
}
|
|
|
|
struct FCompareBoneIndex
|
|
{
|
|
TSharedPtr<FPhATSharedData> SharedData;
|
|
|
|
FCompareBoneIndex(TSharedPtr<FPhATSharedData> InSharedData)
|
|
: SharedData(InSharedData)
|
|
{
|
|
}
|
|
|
|
FORCEINLINE bool operator()(const FTreeElemPtr &A, const FTreeElemPtr &B) const
|
|
{
|
|
const int32 ValA = SharedData->EditorSkelComp->GetBoneIndex((*A).Name);
|
|
const int32 ValB = SharedData->EditorSkelComp->GetBoneIndex((*B).Name);
|
|
return ValA < ValB;
|
|
}
|
|
};
|
|
|
|
TreeElements.Sort(FCompareBoneIndex(SharedData));
|
|
|
|
RootBone.Add(TreeElements[0]);
|
|
|
|
if (Hierarchy.IsValid())
|
|
{
|
|
Hierarchy->RequestTreeRefresh();
|
|
|
|
for (int32 BoneIndex = 0; BoneIndex < TreeElements.Num(); ++BoneIndex)
|
|
{
|
|
Hierarchy->SetItemExpansion(TreeElements[BoneIndex], true);
|
|
}
|
|
|
|
// Force the tree to refresh now instead of next tick
|
|
const FGeometry Stub;
|
|
Hierarchy->Tick( Stub, 0.0f, 0.0f );
|
|
|
|
if (!InsideSelChanged && (Hierarchy->GetNumItemsSelected() > 0))
|
|
{
|
|
Hierarchy->RequestScrollIntoView(Hierarchy->GetSelectedItems()[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPhAT::RefreshPreviewViewport()
|
|
{
|
|
if (PreviewViewport.IsValid())
|
|
{
|
|
PreviewViewport->RefreshViewport();
|
|
}
|
|
}
|
|
|
|
FName FPhAT::GetToolkitFName() const
|
|
{
|
|
return FName("PhAT");
|
|
}
|
|
|
|
FText FPhAT::GetBaseToolkitName() const
|
|
{
|
|
return LOCTEXT("AppLabel", "PhAT");
|
|
}
|
|
|
|
FString FPhAT::GetWorldCentricTabPrefix() const
|
|
{
|
|
return LOCTEXT( "WorldCentricTabPrefix", "PhAT ").ToString();
|
|
}
|
|
|
|
FLinearColor FPhAT::GetWorldCentricTabColorScale() const
|
|
{
|
|
return FLinearColor(0.3f, 0.2f, 0.5f, 0.5f);
|
|
}
|
|
|
|
void PopulateLayoutMenu(FMenuBuilder& MenuBuilder, const TSharedRef<SDockTabStack>& DockTabStack)
|
|
{
|
|
|
|
}
|
|
|
|
void FPhAT::AddReferencedObjects(FReferenceCollector& Collector)
|
|
{
|
|
Collector.AddReferencedObject(SharedData->PhysicsAsset);
|
|
Collector.AddReferencedObject(SharedData->EditorSimOptions);
|
|
|
|
if (PreviewViewport.IsValid())
|
|
{
|
|
SharedData->PreviewScene.AddReferencedObjects(Collector);
|
|
}
|
|
|
|
Collector.AddReferencedObject(SharedData->MouseHandle);
|
|
}
|
|
|
|
void FPhAT::PostUndo(bool bSuccess)
|
|
{
|
|
SharedData->PostUndo();
|
|
RefreshHierachyTree();
|
|
|
|
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
|
|
}
|
|
|
|
|
|
void FPhAT::PostRedo( bool bSuccess )
|
|
{
|
|
for (int32 BodyIdx=0; BodyIdx < SharedData->PhysicsAsset->BodySetup.Num(); ++BodyIdx)
|
|
{
|
|
UBodySetup* Body = SharedData->PhysicsAsset->BodySetup[BodyIdx];
|
|
|
|
bool bRecreate = false;
|
|
for (int32 ElemIdx=0; ElemIdx < Body->AggGeom.ConvexElems.Num(); ++ElemIdx)
|
|
{
|
|
FKConvexElem& Element = Body->AggGeom.ConvexElems[ElemIdx];
|
|
|
|
if (Element.ConvexMesh == NULL)
|
|
{
|
|
bRecreate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bRecreate)
|
|
{
|
|
Body->InvalidatePhysicsData();
|
|
Body->CreatePhysicsMeshes();
|
|
}
|
|
|
|
}
|
|
|
|
PostUndo(bSuccess);
|
|
}
|
|
|
|
|
|
void FPhAT::CreateInternalWidgets()
|
|
{
|
|
PreviewViewport =
|
|
SNew(SPhATPreviewViewport)
|
|
.PhAT(SharedThis(this));
|
|
|
|
FDetailsViewArgs Args;
|
|
Args.bHideSelectionTip = true;
|
|
|
|
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
Properties = PropertyModule.CreateDetailView( Args );
|
|
Properties->SetObject(SharedData->EditorSimOptions);
|
|
|
|
|
|
HierarchyControl =
|
|
SNew(SBorder)
|
|
.Padding(8)
|
|
[
|
|
SAssignNew(Hierarchy, STreeView<FTreeElemPtr>)
|
|
.SelectionMode(ESelectionMode::Multi)
|
|
.TreeItemsSource(&RootBone)
|
|
.OnGetChildren(this, &FPhAT::OnGetChildrenForTree)
|
|
.OnGenerateRow(this, &FPhAT::OnGenerateRowForTree)
|
|
.OnSelectionChanged(this, &FPhAT::OnTreeSelectionChanged)
|
|
.OnMouseButtonDoubleClick(this, &FPhAT::OnTreeDoubleClick)
|
|
.OnContextMenuOpening(this, &FPhAT::OnTreeRightClick)
|
|
.IsEnabled(this, &FPhAT::IsNotSimulation)
|
|
.HeaderRow
|
|
(
|
|
SNew(SHeaderRow)
|
|
.Visibility(EVisibility::Collapsed)
|
|
+SHeaderRow::Column(FName(TEXT("Hierarchy")))
|
|
.DefaultLabel( LOCTEXT( "Hierarchy", "Hierarchy" ) )
|
|
)
|
|
];
|
|
}
|
|
|
|
FText FPhAT::GetRepeatLastSimulationToolTip() const
|
|
{
|
|
if(IsSimulationMode(EPSM_Normal))
|
|
{
|
|
return FPhATCommands::Get().SimulationNormal->GetDescription();
|
|
}else
|
|
{
|
|
return FPhATCommands::Get().SimulationNoGravity->GetDescription();
|
|
}
|
|
}
|
|
|
|
FSlateIcon FPhAT::GetRepeatLastSimulationIcon() const
|
|
{
|
|
if(IsSimulationMode(EPSM_Normal))
|
|
{
|
|
return FPhATCommands::Get().SimulationNormal->GetIcon();
|
|
}else
|
|
{
|
|
return FPhATCommands::Get().SimulationNoGravity->GetIcon();
|
|
}
|
|
}
|
|
|
|
FText FPhAT::GetEditModeLabel() const
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
return FPhATCommands::Get().EditingMode_Body->GetLabel();
|
|
}else
|
|
{
|
|
return FPhATCommands::Get().EditingMode_Constraint->GetLabel();
|
|
}
|
|
}
|
|
|
|
FText FPhAT::GetEditModeToolTip() const
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
return FPhATCommands::Get().EditingMode_Body->GetDescription();
|
|
}else
|
|
{
|
|
return FPhATCommands::Get().EditingMode_Constraint->GetDescription();
|
|
}
|
|
}
|
|
|
|
FSlateIcon FPhAT::GetEditModeIcon() const
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
return FPhATCommands::Get().EditingMode_Body->GetIcon();
|
|
}else
|
|
{
|
|
return FPhATCommands::Get().EditingMode_Constraint->GetIcon();
|
|
}
|
|
}
|
|
|
|
void FPhAT::ExtendToolbar()
|
|
{
|
|
struct Local
|
|
{
|
|
static TSharedRef< SWidget > FillSimulateOptions(TSharedRef<FUICommandList> InCommandList)
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList );
|
|
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
|
|
MenuBuilder.AddMenuEntry(Commands.SimulationNormal);
|
|
MenuBuilder.AddMenuEntry(Commands.SimulationNoGravity);
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
static TSharedRef< SWidget > FillEditMode(TSharedRef<FUICommandList> InCommandList)
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true;
|
|
FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList );
|
|
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
|
|
MenuBuilder.AddMenuEntry(Commands.EditingMode_Body);
|
|
MenuBuilder.AddMenuEntry(Commands.EditingMode_Constraint);
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
static void FillToolbar(FToolBarBuilder& ToolbarBuilder, TSharedRef<SWidget> PhATAnimation, FPhATSharedData::EPhATEditingMode InPhATEditingMode, FPhAT * Phat )
|
|
{
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
TSharedRef<FUICommandList> InCommandList = Phat->GetToolkitCommands();
|
|
|
|
ToolbarBuilder.BeginSection("PhATSimulation");
|
|
// Simulate
|
|
ToolbarBuilder.AddToolBarButton(
|
|
Commands.RepeatLastSimulation,
|
|
NAME_None,
|
|
LOCTEXT("RepeatLastSimulation","Simulate"),
|
|
TAttribute< FText >::Create( TAttribute< FText >::FGetter::CreateSP( Phat, &FPhAT::GetRepeatLastSimulationToolTip) ),
|
|
TAttribute< FSlateIcon >::Create( TAttribute< FSlateIcon >::FGetter::CreateSP( Phat, &FPhAT::GetRepeatLastSimulationIcon) )
|
|
);
|
|
|
|
//simulate mode combo
|
|
|
|
FUIAction SimulationMode;
|
|
SimulationMode.CanExecuteAction = FCanExecuteAction::CreateSP(Phat, &FPhAT::IsNotSimulation);
|
|
{
|
|
ToolbarBuilder.AddComboButton(
|
|
SimulationMode,
|
|
FOnGetContent::CreateStatic( &FillSimulateOptions, InCommandList ),
|
|
LOCTEXT( "SimulateCombo_Label", "Simulate Options" ),
|
|
LOCTEXT( "SimulateComboToolTip", "Options for Simulation" ),
|
|
Commands.RepeatLastSimulation->GetIcon(),
|
|
true
|
|
);
|
|
}
|
|
ToolbarBuilder.EndSection();
|
|
|
|
//selected simulation
|
|
ToolbarBuilder.AddToolBarButton(Commands.ToggleSelectedSimulation);
|
|
|
|
//phat edit mode combo
|
|
FUIAction PhatMode;
|
|
PhatMode.CanExecuteAction = FCanExecuteAction::CreateSP(Phat, &FPhAT::IsNotSimulation);
|
|
ToolbarBuilder.BeginSection("PhATMode");
|
|
{
|
|
ToolbarBuilder.AddComboButton(
|
|
PhatMode,
|
|
FOnGetContent::CreateStatic( &FillEditMode, InCommandList ),
|
|
TAttribute< FText >::Create( TAttribute< FText >::FGetter::CreateSP( Phat, &FPhAT::GetEditModeLabel) ),
|
|
TAttribute< FText >::Create( TAttribute< FText >::FGetter::CreateSP( Phat, &FPhAT::GetEditModeToolTip) ),
|
|
TAttribute< FSlateIcon >::Create( TAttribute< FSlateIcon >::FGetter::CreateSP( Phat, &FPhAT::GetEditModeIcon) )
|
|
);
|
|
}
|
|
ToolbarBuilder.EndSection();
|
|
|
|
|
|
if ( InPhATEditingMode == FPhATSharedData::PEM_BodyEdit )
|
|
{
|
|
ToolbarBuilder.BeginSection("PhATCollision");
|
|
{
|
|
ToolbarBuilder.AddToolBarButton(Commands.WeldToBody);
|
|
ToolbarBuilder.AddToolBarButton(Commands.EnableCollision);
|
|
ToolbarBuilder.AddToolBarButton(Commands.DisableCollision);
|
|
}
|
|
ToolbarBuilder.EndSection();
|
|
}
|
|
|
|
if ( InPhATEditingMode == FPhATSharedData::PEM_ConstraintEdit )
|
|
{
|
|
ToolbarBuilder.BeginSection("PhATConstraint");
|
|
{
|
|
ToolbarBuilder.AddToolBarButton(Commands.ConvertToBallAndSocket);
|
|
ToolbarBuilder.AddToolBarButton(Commands.ConvertToHinge);
|
|
ToolbarBuilder.AddToolBarButton(Commands.ConvertToPrismatic);
|
|
ToolbarBuilder.AddToolBarButton(Commands.ConvertToSkeletal);
|
|
ToolbarBuilder.AddToolBarButton(Commands.SnapConstraint);
|
|
}
|
|
ToolbarBuilder.EndSection();
|
|
}
|
|
|
|
ToolbarBuilder.BeginSection("PhATPlayAnimation");
|
|
{
|
|
ToolbarBuilder.AddToolBarButton(Commands.PlayAnimation);
|
|
}
|
|
ToolbarBuilder.EndSection();
|
|
|
|
ToolbarBuilder.BeginSection("PhATAnimation");
|
|
{
|
|
ToolbarBuilder.AddWidget(PhATAnimation);
|
|
}
|
|
ToolbarBuilder.EndSection();
|
|
|
|
ToolbarBuilder.BeginSection("PhATRecord");
|
|
{
|
|
ToolbarBuilder.AddToolBarButton(Commands.RecordAnimation,
|
|
NAME_None,
|
|
TAttribute<FText>(Phat, &FPhAT::GetRecordStatusLabel),
|
|
TAttribute<FText>(Phat, &FPhAT::GetRecordStatusTooltip),
|
|
TAttribute<FSlateIcon>(Phat, &FPhAT::GetRecordStatusImage),
|
|
NAME_None);
|
|
}
|
|
ToolbarBuilder.EndSection();
|
|
}
|
|
};
|
|
|
|
// If the ToolbarExtender is valid, remove it before rebuilding it
|
|
if ( ToolbarExtender.IsValid() )
|
|
{
|
|
RemoveToolbarExtender( ToolbarExtender );
|
|
ToolbarExtender.Reset();
|
|
}
|
|
|
|
ToolbarExtender = MakeShareable(new FExtender);
|
|
|
|
TSharedRef<SWidget> PhATAnimation = SNew(SBox)
|
|
.WidthOverride(250)
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text( LOCTEXT( "PhATToolbarAnimation", "Animation: " ) )
|
|
]
|
|
+SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
[
|
|
SNew(SContentReference)
|
|
.WidthOverride(80.0f)
|
|
.AllowSelectingNewAsset(true)
|
|
.AssetReference(this, &FPhAT::GetSelectedAnimation)
|
|
.AllowedClass(UAnimSequence::StaticClass())
|
|
.OnShouldFilterAsset(this, &FPhAT::ShouldFilterAssetBasedOnSkeleton)
|
|
.OnSetReference(this, &FPhAT::AnimationSelectionChanged)
|
|
.IsEnabled(this, &FPhAT::IsToggleSimulation)
|
|
]
|
|
];
|
|
|
|
ToolbarExtender->AddToolBarExtension(
|
|
"Asset",
|
|
EExtensionHook::After,
|
|
GetToolkitCommands(),
|
|
FToolBarExtensionDelegate::CreateStatic( &Local::FillToolbar, PhATAnimation, SharedData->EditingMode, this)
|
|
);
|
|
|
|
AddToolbarExtender(ToolbarExtender);
|
|
|
|
IPhATModule* PhATModule = &FModuleManager::LoadModuleChecked<IPhATModule>( "PhAT" );
|
|
AddToolbarExtender(PhATModule->GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
|
|
}
|
|
|
|
void FPhAT::ExtendMenu()
|
|
{
|
|
struct Local
|
|
{
|
|
static void FillEdit( FMenuBuilder& MenuBarBuilder )
|
|
{
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
MenuBarBuilder.BeginSection("Selection", LOCTEXT("PhatEditSelection", "Selection"));
|
|
MenuBarBuilder.AddMenuEntry(Commands.SelectAllObjects);
|
|
MenuBarBuilder.EndSection();
|
|
}
|
|
|
|
static void FillAsset( FMenuBuilder& MenuBarBuilder )
|
|
{
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
MenuBarBuilder.BeginSection("Settings", LOCTEXT("PhatAssetSettings", "Settings"));
|
|
MenuBarBuilder.AddMenuEntry(Commands.ChangeDefaultMesh);
|
|
MenuBarBuilder.AddMenuEntry(Commands.ApplyPhysicalMaterial);
|
|
MenuBarBuilder.AddMenuEntry(Commands.ResetEntireAsset);
|
|
MenuBarBuilder.EndSection();
|
|
}
|
|
};
|
|
MenuExtender = MakeShareable(new FExtender);
|
|
MenuExtender->AddMenuExtension(
|
|
"EditHistory",
|
|
EExtensionHook::After,
|
|
GetToolkitCommands(),
|
|
FMenuExtensionDelegate::CreateStatic( &Local::FillEdit ) );
|
|
|
|
MenuExtender->AddMenuExtension(
|
|
"AssetEditorActions",
|
|
EExtensionHook::After,
|
|
GetToolkitCommands(),
|
|
FMenuExtensionDelegate::CreateStatic( &Local::FillAsset )
|
|
);
|
|
|
|
AddMenuExtender(MenuExtender);
|
|
|
|
IPhATModule* PhATModule = &FModuleManager::LoadModuleChecked<IPhATModule>( "PhAT" );
|
|
AddMenuExtender(PhATModule->GetToolBarExtensibilityManager()->GetAllExtenders(GetToolkitCommands(), GetEditingObjects()));
|
|
}
|
|
|
|
void FPhAT::BindCommands()
|
|
{
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ChangeDefaultMesh,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnChangeDefaultMesh),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ResetEntireAsset,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnResetEntireAsset),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.RestetBoneCollision,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnResetBoneCollision),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditBodyMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ApplyPhysicalMaterial,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnApplyPhysicalMaterial),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.EditingMode_Body,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnEditingMode, (int32)FPhATSharedData::PEM_BodyEdit),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsEditingMode, (int32)FPhATSharedData::PEM_BodyEdit));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.EditingMode_Constraint,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnEditingMode, (int32)FPhATSharedData::PEM_ConstraintEdit),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsEditingMode, (int32)FPhATSharedData::PEM_ConstraintEdit));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.CopyProperties,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnCopyProperties),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanCopyProperties),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsCopyProperties));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.PasteProperties,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnPasteProperties),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanPasteProperties));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.InstanceProperties,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnInstanceProperties),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsInstanceProperties));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.RepeatLastSimulation,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleSimulation),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanStartSimulation),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsToggleSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.SimulationNormal,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSetSimulationMode, EPSM_Normal),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanStartSimulation),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsSimulationMode, EPSM_Normal));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.SimulationNoGravity,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSetSimulationMode, EPSM_Gravity),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanStartSimulation),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsSimulationMode, EPSM_Gravity));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleSelectedSimulation,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleSelectedSimulation),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanStartSimulation),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsSelectedSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.MeshRenderingMode_Solid,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnMeshRenderingMode, FPhATSharedData::PRM_Solid),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsMeshRenderingMode, FPhATSharedData::PRM_Solid));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.MeshRenderingMode_Wireframe,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnMeshRenderingMode, FPhATSharedData::PRM_Wireframe),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsMeshRenderingMode, FPhATSharedData::PRM_Wireframe));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.MeshRenderingMode_None,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnMeshRenderingMode, FPhATSharedData::PRM_None),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsMeshRenderingMode, FPhATSharedData::PRM_None));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.CollisionRenderingMode_Solid,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnCollisionRenderingMode, FPhATSharedData::PRM_Solid),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsCollisionRenderingMode, FPhATSharedData::PRM_Solid));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.CollisionRenderingMode_Wireframe,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnCollisionRenderingMode, FPhATSharedData::PRM_Wireframe),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsCollisionRenderingMode, FPhATSharedData::PRM_Wireframe));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.CollisionRenderingMode_None,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnCollisionRenderingMode, FPhATSharedData::PRM_None),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsCollisionRenderingMode, FPhATSharedData::PRM_None));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ConstraintRenderingMode_None,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnConstraintRenderingMode, FPhATSharedData::PCV_None),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsConstraintRenderingMode, FPhATSharedData::PCV_None));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ConstraintRenderingMode_AllPositions,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnConstraintRenderingMode, FPhATSharedData::PCV_AllPositions),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsConstraintRenderingMode, FPhATSharedData::PCV_AllPositions));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ConstraintRenderingMode_AllLimits,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnConstraintRenderingMode, FPhATSharedData::PCV_AllLimits),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsConstraintRenderingMode, FPhATSharedData::PCV_AllLimits));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ShowKinematicBodies,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnShowFixedBodies),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsShowFixedBodies));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DrawGroundBox,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDrawGroundBox),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsDrawGroundBox));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleGraphicsHierarchy,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleGraphicsHierarchy),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsToggleGraphicsHierarchy));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleBoneInfuences,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleBoneInfluences),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsToggleBoneInfluences));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleMassProperties,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleMassProperties),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsToggleMassProperties));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DisableCollision,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSetCollision, false),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanSetCollision));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.EnableCollision,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSetCollision, true),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanSetCollision));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.WeldToBody,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnWeldToBody),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanWeldToBody));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.AddNewBody,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnAddNewBody),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsEditBodyMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.AddSphere,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnAddSphere),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanAddPrimitive));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.AddSphyl,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnAddSphyl),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanAddPrimitive));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.AddBox,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnAddBox),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanAddPrimitive));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DeletePrimitive,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDeletePrimitive),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditBodyMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DuplicatePrimitive,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDuplicatePrimitive),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::CanDuplicatePrimitive));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ResetConstraint,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnResetConstraint),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditConstraintMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.SnapConstraint,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSnapConstraint),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditConstraintMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ConvertToBallAndSocket,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnConvertToBallAndSocket),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditConstraintMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ConvertToHinge,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnConvertToHinge),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditConstraintMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ConvertToPrismatic,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnConvertToPrismatic),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditConstraintMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ConvertToSkeletal,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnConvertToSkeletal),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditConstraintMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DeleteConstraint,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDeleteConstraint),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsSelectedEditConstraintMode));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.PlayAnimation,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnPlayAnimation),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsToggleSimulation),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsPlayAnimation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ShowSkeleton,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnShowSkeleton),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsShowSkeleton));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.PerspectiveView,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnViewType, ELevelViewportType::LVT_Perspective),
|
|
FCanExecuteAction()
|
|
);
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.TopView,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnViewType, ELevelViewportType::LVT_OrthoXY),
|
|
FCanExecuteAction()
|
|
);
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.LeftView,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnViewType, ELevelViewportType::LVT_OrthoYZ),
|
|
FCanExecuteAction()
|
|
);
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.FrontView,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnViewType, ELevelViewportType::LVT_OrthoXZ),
|
|
FCanExecuteAction()
|
|
);
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.BottomView,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnViewType, ELevelViewportType::LVT_OrthoNegativeXY),
|
|
FCanExecuteAction()
|
|
);
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.RightView,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnViewType, ELevelViewportType::LVT_OrthoNegativeYZ),
|
|
FCanExecuteAction()
|
|
);
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.BackView,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnViewType, ELevelViewportType::LVT_OrthoNegativeXZ),
|
|
FCanExecuteAction()
|
|
);
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.MakeBodyKinematic,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSetBodyPhysicsType, EPhysicsType::PhysType_Kinematic ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsBodyPhysicsType, EPhysicsType::PhysType_Kinematic ) );
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.MakeBodySimulated,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSetBodyPhysicsType, EPhysicsType::PhysType_Simulated ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsBodyPhysicsType, EPhysicsType::PhysType_Simulated ) );
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.MakeBodyDefault,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSetBodyPhysicsType, EPhysicsType::PhysType_Default ),
|
|
FCanExecuteAction(),
|
|
FIsActionChecked::CreateSP(this, &FPhAT::IsBodyPhysicsType, EPhysicsType::PhysType_Default ) );
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.KinematicAllBodiesBelow,
|
|
FExecuteAction::CreateSP(this, &FPhAT::SetBodiesBelowSelectedPhysicsType, EPhysicsType::PhysType_Kinematic, true) );
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.SimulatedAllBodiesBelow,
|
|
FExecuteAction::CreateSP(this, &FPhAT::SetBodiesBelowSelectedPhysicsType, EPhysicsType::PhysType_Simulated, true) );
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.MakeAllBodiesBelowDefault,
|
|
FExecuteAction::CreateSP(this, &FPhAT::SetBodiesBelowSelectedPhysicsType, EPhysicsType::PhysType_Default, true) );
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DeleteBody,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDeleteBody));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DeleteAllBodiesBelow,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDeleteAllBodiesBelow));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleMotor,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleMotor));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.EnableMotorsBelow,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnEnableMotorsBelow));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DisableMotorsBelow,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDisableMotorsBelow));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.SelectionLock,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnLockSelection),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.DeleteSelected,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnDeleteSelection),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.CycleConstraintOrientation,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnCycleConstraintOrientation),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.CycleConstraintActive,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnCycleConstraintActive),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleSwing1,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleSwing1),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleSwing2,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleSwing2),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.ToggleTwist,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnToggleTwist),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.FocusOnSelection,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnFocusSelection),
|
|
FCanExecuteAction());
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.SelectAllObjects,
|
|
FExecuteAction::CreateSP(this, &FPhAT::OnSelectAll));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.HierarchyFilterAll,
|
|
FExecuteAction::CreateSP(this, &FPhAT::SetHierarchyFilter, PHFM_All));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.HierarchyFilterBodies,
|
|
FExecuteAction::CreateSP(this, &FPhAT::SetHierarchyFilter, PHFM_Bodies));
|
|
|
|
ToolkitCommands->MapAction(
|
|
Commands.Mirror,
|
|
FExecuteAction::CreateSP(this, &FPhAT::Mirror),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsNotSimulation)
|
|
);
|
|
|
|
// record animation
|
|
ToolkitCommands->MapAction(
|
|
Commands.RecordAnimation,
|
|
FExecuteAction::CreateSP(this, &FPhAT::RecordAnimation),
|
|
FCanExecuteAction::CreateSP(this, &FPhAT::IsRecordAvailable),
|
|
FIsActionChecked(),
|
|
FIsActionButtonVisible()
|
|
);
|
|
}
|
|
|
|
void FPhAT::Mirror()
|
|
{
|
|
SharedData->Mirror();
|
|
}
|
|
|
|
TSharedRef<ITableRow> FPhAT::OnGenerateRowForTree(FTreeElemPtr Item, const TSharedRef<STableViewBase>& OwnerTable)
|
|
{
|
|
class SHoverDetectTableRow : public STableRow<FTreeElemPtr>
|
|
{
|
|
public:
|
|
SLATE_BEGIN_ARGS(SHoverDetectTableRow)
|
|
{}
|
|
SLATE_DEFAULT_SLOT( FArguments, Content )
|
|
SLATE_END_ARGS()
|
|
|
|
void Construct( const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, FPhAT * phat )
|
|
{
|
|
STableRow::Construct(
|
|
|
|
STableRow::FArguments()
|
|
[
|
|
InArgs._Content.Widget
|
|
]
|
|
|
|
,
|
|
|
|
InOwnerTableView
|
|
);
|
|
|
|
Phat = phat;
|
|
}
|
|
|
|
virtual void OnMouseEnter( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override
|
|
{
|
|
Phat->OnTreeHighlightChanged();
|
|
}
|
|
|
|
virtual void OnMouseLeave( const FPointerEvent& MouseEvent ) override
|
|
{
|
|
Phat->OnTreeHighlightChanged();
|
|
}
|
|
|
|
private:
|
|
FPhAT * Phat;
|
|
};
|
|
|
|
return
|
|
SNew(SHoverDetectTableRow, OwnerTable, this)
|
|
[
|
|
SNew(STextBlock)
|
|
.Font((*Item).bBold? FEditorStyle::GetFontStyle("BoldFont"): FEditorStyle::GetFontStyle("NormalFont"))
|
|
.Text(FText::FromName((*Item).Name))
|
|
];
|
|
}
|
|
|
|
void FPhAT::OnTreeHighlightChanged()
|
|
{
|
|
}
|
|
|
|
void FPhAT::OnGetChildrenForTree(FTreeElemPtr Parent, TArray<FTreeElemPtr>& OutChildren)
|
|
{
|
|
if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
for (int32 i = 0; i < SharedData->PhysicsAsset->BodySetup.Num(); ++i)
|
|
{
|
|
if (SharedData->PhysicsAsset->BodySetup[i]->BoneName == (*Parent).Name)
|
|
{
|
|
int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->BodySetup[i]->BoneName);
|
|
FKAggregateGeom& AggGeom = SharedData->PhysicsAsset->BodySetup[i]->AggGeom;
|
|
|
|
if (AggGeom.SphereElems.Num() + AggGeom.BoxElems.Num() + AggGeom.SphylElems.Num() + AggGeom.ConvexElems.Num() > 1)
|
|
{
|
|
FTreeElemPtr NewElem;
|
|
for (int32 j = 0; j < AggGeom.SphereElems.Num(); ++j)
|
|
{
|
|
NewElem = FTreeElemPtr(new FPhATTreeInfo(FName(*FString::Printf(TEXT("Sphere %d"), j)), false, BoneIndex, INDEX_NONE, i, j, KPT_Sphere));
|
|
TreeElements.Add(NewElem);
|
|
OutChildren.Add(NewElem);
|
|
}
|
|
|
|
for (int32 j = 0; j < AggGeom.BoxElems.Num(); ++j)
|
|
{
|
|
NewElem = FTreeElemPtr(new FPhATTreeInfo(FName(*FString::Printf(TEXT("Box %d"), j)), false, BoneIndex, INDEX_NONE, i, j, KPT_Box));
|
|
TreeElements.Add(NewElem);
|
|
OutChildren.Add(NewElem);
|
|
}
|
|
|
|
for (int32 j = 0; j < AggGeom.SphylElems.Num(); ++j)
|
|
{
|
|
NewElem = FTreeElemPtr(new FPhATTreeInfo(FName(*FString::Printf(TEXT("Sphyl %d"), j)), false, BoneIndex, INDEX_NONE, i, j, KPT_Sphyl));
|
|
TreeElements.Add(NewElem);
|
|
OutChildren.Add(NewElem);
|
|
}
|
|
|
|
for (int32 j = 0; j < AggGeom.ConvexElems.Num(); ++j)
|
|
{
|
|
NewElem = FTreeElemPtr(new FPhATTreeInfo(FName(*FString::Printf(TEXT("Convex %d"), j)), false, BoneIndex, INDEX_NONE, i, j, KPT_Convex));
|
|
TreeElements.Add(NewElem);
|
|
OutChildren.Add(NewElem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 ParentIndex = SharedData->EditorSkelComp->GetBoneIndex((*Parent).Name);
|
|
for (int32 BoneIndex = 0; BoneIndex < SharedData->EditorSkelMesh->RefSkeleton.GetNum(); ++BoneIndex)
|
|
{
|
|
const FMeshBoneInfo& Bone = SharedData->EditorSkelMesh->RefSkeleton.GetRefBoneInfo()[BoneIndex];
|
|
if (Bone.ParentIndex != INDEX_NONE)
|
|
{
|
|
const FMeshBoneInfo& ParentBone = SharedData->EditorSkelMesh->RefSkeleton.GetRefBoneInfo()[Bone.ParentIndex];
|
|
if ((BoneIndex != ParentIndex) && (ParentBone.Name == (*Parent).Name))
|
|
{
|
|
for (int32 i = 0; i < TreeElements.Num(); ++i)
|
|
{
|
|
if ((*TreeElements[i]).Name == Bone.Name)
|
|
{
|
|
if (FilterTreeElement(TreeElements[i]))
|
|
{
|
|
//normal element gets added
|
|
OutChildren.Add(TreeElements[i]);
|
|
}
|
|
else
|
|
{
|
|
//we still need to see if any of this element's children get added
|
|
OnGetChildrenForTree(TreeElements[i], OutChildren);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnTreeSelectionChanged(FTreeElemPtr TreeElem, ESelectInfo::Type SelectInfo)
|
|
{
|
|
// prevent re-entrancy
|
|
if (InsideSelChanged)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!TreeElem.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
InsideSelChanged = true;
|
|
|
|
TArray<FTreeElemPtr> SelectedElems = Hierarchy->GetSelectedItems();
|
|
|
|
//clear selection first
|
|
if(SelectedElems.Num() && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
SharedData->SetSelectedBody(NULL);
|
|
}
|
|
else if(SelectedElems.Num() && SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit)
|
|
{
|
|
SharedData->SetSelectedConstraint(INDEX_NONE);
|
|
}
|
|
|
|
for(FTreeElemPtr SelectedElem : SelectedElems)
|
|
{
|
|
int32 ObjIndex = (*SelectedElem).BoneOrConstraintIdx;
|
|
if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
if (ObjIndex != INDEX_NONE)
|
|
{
|
|
for (int32 i = 0; i < SharedData->PhysicsAsset->BodySetup.Num(); ++i)
|
|
{
|
|
if (SharedData->PhysicsAsset->BodySetup[i]->BoneName == (*SelectedElem).Name)
|
|
{
|
|
FKAggregateGeom& AggGeom = SharedData->PhysicsAsset->BodySetup[i]->AggGeom;
|
|
|
|
//select all primitives
|
|
for(int32 j=0; j<AggGeom.BoxElems.Num(); ++j)
|
|
{
|
|
SharedData->HitBone(i, KPT_Box, j, true, false);
|
|
}
|
|
|
|
for(int32 j=0; j<AggGeom.SphereElems.Num(); ++j)
|
|
{
|
|
SharedData->HitBone(i, KPT_Sphere, j, true, false);
|
|
}
|
|
|
|
for(int32 j=0; j<AggGeom.SphylElems.Num(); ++j)
|
|
{
|
|
SharedData->HitBone(i, KPT_Sphyl, j, true, false);
|
|
}
|
|
|
|
for(int32 j=0; j<AggGeom.ConvexElems.Num(); ++j)
|
|
{
|
|
SharedData->HitBone(i, KPT_Convex, j, true, false);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FPhATTreeInfo Info = *SelectedElem;
|
|
if (Info.ParentBoneIdx != INDEX_NONE)
|
|
{
|
|
SharedData->HitBone(Info.BodyIdx, Info.CollisionType, Info.CollisionIdx, true, false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ObjIndex != INDEX_NONE)
|
|
{
|
|
SharedData->HitConstraint(ObjIndex, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
InsideSelChanged = false;
|
|
}
|
|
|
|
void FPhAT::OnTreeDoubleClick(FTreeElemPtr TreeElem)
|
|
{
|
|
if(!TreeElem->bBold && TreeElem->BodyIdx == INDEX_NONE) //if bone without body add new body
|
|
{
|
|
OnAddNewBody();
|
|
}else if(TreeElem->bBold)
|
|
{
|
|
OnResetBoneCollision();
|
|
}
|
|
}
|
|
|
|
TSharedPtr<SWidget> FPhAT::OnTreeRightClick()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
if(SharedData->SelectedBodies.Num()) //if we have anything selected just give us context menu of it
|
|
{
|
|
return BuildMenuWidgetBody(true);
|
|
}else
|
|
{
|
|
//otherwise check if we've selected a bone
|
|
TArray<FTreeElemPtr> Elems = Hierarchy->GetSelectedItems();
|
|
for(int32 i=0; i<Elems.Num(); ++i)
|
|
{
|
|
if(Elems[i]->BoneOrConstraintIdx != INDEX_NONE)
|
|
{
|
|
return BuildMenuWidgetBone();
|
|
}
|
|
}
|
|
}
|
|
}else
|
|
{
|
|
if(SharedData->SelectedConstraints.Num()) //if we have anything selected just give us context menu of it
|
|
{
|
|
return BuildMenuWidgetConstraint(true);
|
|
}else
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
TSharedPtr<SWidget> FPhAT::BuildMenuWidgetBody(bool bHierarchy /*= false*/)
|
|
{
|
|
if (!SharedData->GetSelectedBody())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
const bool bShouldCloseWindowAfterMenuSelection = true; // Set the menu to automatically close when the user commits to a choice
|
|
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, GetToolkitCommands());
|
|
{
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
|
|
struct FLocal
|
|
{
|
|
static void FillPhysicsTypeMenu(FMenuBuilder& InMenuBuilder, bool bInHierarchy)
|
|
{
|
|
const FPhATCommands& PhATCommands = FPhATCommands::Get();
|
|
|
|
InMenuBuilder.BeginSection("BodyPhysicsTypeActions", LOCTEXT("BodyPhysicsTypeHeader", "Body Physics Type"));
|
|
InMenuBuilder.AddMenuEntry(PhATCommands.MakeBodyKinematic);
|
|
InMenuBuilder.AddMenuEntry(PhATCommands.MakeBodySimulated);
|
|
InMenuBuilder.AddMenuEntry(PhATCommands.MakeBodyDefault);
|
|
InMenuBuilder.EndSection();
|
|
InMenuBuilder.EndSection();
|
|
|
|
if (bInHierarchy)
|
|
{
|
|
InMenuBuilder.BeginSection("BodiesBelowPhysicsTypeActions", LOCTEXT("BodiesBelowPhysicsTypeHeader", "Bodies Below Physics Type"));
|
|
InMenuBuilder.AddMenuEntry(PhATCommands.KinematicAllBodiesBelow);
|
|
InMenuBuilder.AddMenuEntry(PhATCommands.SimulatedAllBodiesBelow);
|
|
InMenuBuilder.AddMenuEntry(PhATCommands.MakeAllBodiesBelowDefault);
|
|
InMenuBuilder.EndSection();
|
|
}
|
|
}
|
|
};
|
|
|
|
MenuBuilder.BeginSection( "BoneActions", LOCTEXT( "BoneHeader", "Bone" ) );
|
|
MenuBuilder.AddMenuEntry( Commands.AddBox );
|
|
MenuBuilder.AddMenuEntry( Commands.AddSphere );
|
|
MenuBuilder.AddMenuEntry( Commands.AddSphyl );
|
|
MenuBuilder.AddMenuEntry( Commands.RestetBoneCollision );
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection( "BodyActions", LOCTEXT( "BodyHeader", "Body" ) );
|
|
MenuBuilder.AddSubMenu( LOCTEXT("BodyPhysicsTypeMenu", "Physics Type"), LOCTEXT("BodyPhysicsTypeMenu_ToolTip", "Physics Type"),
|
|
FNewMenuDelegate::CreateStatic( &FLocal::FillPhysicsTypeMenu, bHierarchy ) );
|
|
|
|
MenuBuilder.AddMenuEntry( Commands.CopyProperties );
|
|
MenuBuilder.AddMenuEntry( Commands.PasteProperties );
|
|
MenuBuilder.AddMenuEntry( Commands.WeldToBody );
|
|
MenuBuilder.AddMenuEntry( Commands.EnableCollision );
|
|
MenuBuilder.AddMenuEntry( Commands.DisableCollision );
|
|
MenuBuilder.AddMenuEntry( Commands.DeleteBody );
|
|
if(bHierarchy)
|
|
{
|
|
MenuBuilder.AddMenuEntry( Commands.DeleteAllBodiesBelow );
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection( "PrimitiveActions", LOCTEXT( "PrimitiveHeader", "Primitive" ) );
|
|
MenuBuilder.AddMenuEntry( Commands.DuplicatePrimitive );
|
|
MenuBuilder.AddMenuEntry( Commands.DeletePrimitive );
|
|
MenuBuilder.EndSection();
|
|
|
|
if(SharedData->SelectedBodies.Num() > 1) //different context menu if we have group selection
|
|
{
|
|
|
|
}else
|
|
{
|
|
|
|
SAssignNew(PickerComboButton, SComboButton)
|
|
.ContentPadding(3)
|
|
.OnGetMenuContent(this, &FPhAT::BuildStaticMeshAssetPicker)
|
|
.ButtonContent()
|
|
[
|
|
SNew(STextBlock) .Text(LOCTEXT("AddCollisionfromStaticMesh ", "Copy Collision From StaticMesh"))
|
|
];
|
|
|
|
MenuBuilder.BeginSection( "Advanced", LOCTEXT( "AdvancedHeading", "Advanced" ) );
|
|
MenuBuilder.AddWidget(PickerComboButton.ToSharedRef(), FText());
|
|
MenuBuilder.EndSection();
|
|
}
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
TSharedPtr<SWidget> FPhAT::BuildMenuWidgetConstraint(bool bHierarchy /*= false*/)
|
|
{
|
|
if (!SharedData->GetSelectedConstraint())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
const bool bShouldCloseWindowAfterMenuSelection = true; // Set the menu to automatically close when the user commits to a choice
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, GetToolkitCommands());
|
|
{
|
|
MenuBuilder.BeginSection( "MotorTypeActions", LOCTEXT("ConstraintMotorTypeHeader", "Motors" ) );
|
|
MenuBuilder.AddMenuEntry(Commands.ToggleMotor);
|
|
if(bHierarchy)
|
|
{
|
|
MenuBuilder.AddMenuEntry(Commands.EnableMotorsBelow);
|
|
MenuBuilder.AddMenuEntry(Commands.DisableMotorsBelow);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection( "EditTypeActions", LOCTEXT("ConstraintEditTypeHeader", "Edit" ) );
|
|
MenuBuilder.AddMenuEntry(Commands.CopyProperties);
|
|
MenuBuilder.AddMenuEntry(Commands.PasteProperties);
|
|
MenuBuilder.AddMenuEntry(Commands.ResetConstraint);
|
|
MenuBuilder.AddMenuEntry(Commands.DeleteConstraint);
|
|
MenuBuilder.EndSection();
|
|
|
|
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
TSharedPtr<SWidget> FPhAT::BuildMenuWidgetBone()
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true; // Set the menu to automatically close when the user commits to a choice
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, GetToolkitCommands());
|
|
{
|
|
MenuBuilder.BeginSection( "BodyTypeAction", LOCTEXT("BodyTypeHeader", "New Body" ) );
|
|
MenuBuilder.AddMenuEntry( Commands.AddNewBody );
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void FPhAT::AnimationSelectionChanged( UObject* Object )
|
|
{
|
|
SelectedAnimation = Cast<UAnimationAsset>( Object );
|
|
SharedData->EditorSkelComp->SetAnimation( SelectedAnimation );
|
|
}
|
|
|
|
UObject* FPhAT::GetSelectedAnimation() const
|
|
{
|
|
return SelectedAnimation;
|
|
}
|
|
|
|
bool FPhAT::ShouldFilterAssetBasedOnSkeleton( const FAssetData& AssetData )
|
|
{
|
|
// @TODO This is a duplicate of FPersona::ShouldFilterAssetBasedOnSkeleton(), but should go away once PhAT is integrated with Persona
|
|
const FString* SkeletonName = AssetData.TagsAndValues.Find(TEXT("Skeleton"));
|
|
|
|
if ( SkeletonName )
|
|
{
|
|
USkeleton* Skeleton = SharedData->EditorSkelMesh->Skeleton;
|
|
|
|
if ( Skeleton && (*SkeletonName) == FString::Printf(TEXT("%s'%s'"), *Skeleton->GetClass()->GetName(), *Skeleton->GetPathName()) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FPhAT::SnapConstraintToBone(int32 ConstraintIndex, const FTransform& ParentFrame)
|
|
{
|
|
UPhysicsConstraintTemplate* ConstraintSetup = SharedData->PhysicsAsset->ConstraintSetup[ConstraintIndex];
|
|
ConstraintSetup->Modify();
|
|
|
|
// Get child bone transform
|
|
int32 BoneIndex = SharedData->EditorSkelMesh->RefSkeleton.FindBoneIndex(ConstraintSetup->DefaultInstance.ConstraintBone1);
|
|
check(BoneIndex != INDEX_NONE);
|
|
|
|
FTransform BoneTM = SharedData->EditorSkelComp->GetBoneTransform(BoneIndex);
|
|
FTransform RelTM = BoneTM.GetRelativeTransform(ParentFrame);
|
|
|
|
FTransform Con1Matrix = ConstraintSetup->DefaultInstance.GetRefFrame(EConstraintFrame::Frame2);
|
|
FTransform Con0Matrix = ConstraintSetup->DefaultInstance.GetRefFrame(EConstraintFrame::Frame1);
|
|
|
|
ConstraintSetup->DefaultInstance.SetRefFrame(EConstraintFrame::Frame2, Con0Matrix * RelTM * Con1Matrix);
|
|
}
|
|
|
|
void FPhAT::CreateOrConvertConstraint(EPhATConstraintType ConstraintType)
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "CreateConvertConstraint", "Create Or Convert Constraint" ) );
|
|
|
|
for(int32 i=0; i<SharedData->SelectedConstraints.Num(); ++i)
|
|
{
|
|
UPhysicsConstraintTemplate* ConstraintSetup = SharedData->PhysicsAsset->ConstraintSetup[SharedData->SelectedConstraints[i].Index];
|
|
ConstraintSetup->Modify();
|
|
|
|
if(ConstraintType == EPCT_BSJoint)
|
|
{
|
|
ConstraintSetup->DefaultInstance.ConfigureAsBS();
|
|
}
|
|
else if(ConstraintType == EPCT_Hinge)
|
|
{
|
|
ConstraintSetup->DefaultInstance.ConfigureAsHinge();
|
|
}
|
|
else if(ConstraintType == EPCT_Prismatic)
|
|
{
|
|
ConstraintSetup->DefaultInstance.ConfigureAsPrismatic();
|
|
}
|
|
else if(ConstraintType == EPCT_SkelJoint)
|
|
{
|
|
ConstraintSetup->DefaultInstance.ConfigureAsSkelJoint();
|
|
}
|
|
}
|
|
|
|
RefreshHierachyTree();
|
|
RefreshPreviewViewport();
|
|
|
|
}
|
|
|
|
void FPhAT::SetConstraintsBelowSelectedMotorised(bool bMotorised)
|
|
{
|
|
SharedData->PhysicsAsset->Modify();
|
|
|
|
for(int32 i=0; i<SharedData->SelectedConstraints.Num(); ++i)
|
|
{
|
|
// Get the index of this constraint
|
|
UPhysicsConstraintTemplate* BaseSetup = SharedData->PhysicsAsset->ConstraintSetup[SharedData->SelectedConstraints[i].Index];
|
|
|
|
TArray<int32> BelowConstraints;
|
|
int32 BaseIndex = SharedData->EditorSkelMesh->RefSkeleton.FindBoneIndex(BaseSetup->DefaultInstance.JointName);
|
|
|
|
// Iterate over all other joints, looking for 'children' of this one
|
|
for (int32 j = 0; j < SharedData->PhysicsAsset->ConstraintSetup.Num(); ++j)
|
|
{
|
|
UPhysicsConstraintTemplate* ConstraintSetup = SharedData->PhysicsAsset->ConstraintSetup[j];
|
|
FName TestName = ConstraintSetup->DefaultInstance.JointName;
|
|
int32 TestIndex = SharedData->EditorSkelMesh->RefSkeleton.FindBoneIndex(TestName);
|
|
|
|
// We want to return this constraint as well.
|
|
if (TestIndex == BaseIndex || SharedData->EditorSkelMesh->RefSkeleton.BoneIsChildOf(TestIndex, BaseIndex))
|
|
{
|
|
BelowConstraints.Add(j);
|
|
}
|
|
}
|
|
|
|
for (int32 j = 0; j < BelowConstraints.Num(); ++j)
|
|
{
|
|
int32 ConIndex = BelowConstraints[j];
|
|
FConstraintInstance* ConstraintInstance = &SharedData->PhysicsAsset->ConstraintSetup[ConIndex]->DefaultInstance;
|
|
|
|
ConstraintInstance->bAngularOrientationDrive = bMotorised;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void FPhAT::AddNewPrimitive(EKCollisionPrimitiveType InPrimitiveType, bool bCopySelected)
|
|
{
|
|
check(!bCopySelected || SharedData->SelectedBodies.Num() == 1); //we only support this for one selection
|
|
int32 NewPrimIndex = 0;
|
|
TArray<FPhATSharedData::FSelection> NewSelection;
|
|
{
|
|
// 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") );
|
|
|
|
//first we need to grab all the bodies we're modifying (removes duplicates from multiple primitives)
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
|
|
NewSelection.AddUnique(FPhATSharedData::FSelection(SharedData->SelectedBodies[i].Index, KPT_Unknown, 0)); //only care about body index for now, we'll later update the primitive index
|
|
}
|
|
|
|
for(int32 i=0; i<NewSelection.Num(); ++i)
|
|
{
|
|
UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[NewSelection[i].Index];
|
|
EKCollisionPrimitiveType PrimitiveType;
|
|
if (bCopySelected)
|
|
{
|
|
PrimitiveType = SharedData->GetSelectedBody()->PrimitiveType;
|
|
}
|
|
else
|
|
{
|
|
PrimitiveType = InPrimitiveType;
|
|
}
|
|
|
|
|
|
BodySetup->Modify();
|
|
|
|
if (PrimitiveType == KPT_Sphere)
|
|
{
|
|
NewPrimIndex = BodySetup->AggGeom.SphereElems.Add(FKSphereElem());
|
|
NewSelection[i].PrimitiveType = KPT_Sphere;
|
|
NewSelection[i].PrimitiveIndex = NewPrimIndex;
|
|
FKSphereElem* SphereElem = &BodySetup->AggGeom.SphereElems[NewPrimIndex];
|
|
|
|
if (!bCopySelected)
|
|
{
|
|
SphereElem->Center = FVector::ZeroVector;
|
|
|
|
SphereElem->Radius = PhAT::DefaultPrimSize;
|
|
}
|
|
else
|
|
{
|
|
SphereElem->Center = BodySetup->AggGeom.SphereElems[SharedData->GetSelectedBody()->PrimitiveIndex].Center;
|
|
SphereElem->Center.X += PhAT::DuplicateXOffset;
|
|
|
|
SphereElem->Radius = BodySetup->AggGeom.SphereElems[SharedData->GetSelectedBody()->PrimitiveIndex].Radius;
|
|
}
|
|
}
|
|
else if (PrimitiveType == KPT_Box)
|
|
{
|
|
NewPrimIndex = BodySetup->AggGeom.BoxElems.Add(FKBoxElem());
|
|
NewSelection[i].PrimitiveType = KPT_Box;
|
|
NewSelection[i].PrimitiveIndex = NewPrimIndex;
|
|
FKBoxElem* BoxElem = &BodySetup->AggGeom.BoxElems[NewPrimIndex];
|
|
|
|
if (!bCopySelected)
|
|
{
|
|
BoxElem->SetTransform( FTransform::Identity );
|
|
|
|
BoxElem->X = 0.5f * PhAT::DefaultPrimSize;
|
|
BoxElem->Y = 0.5f * PhAT::DefaultPrimSize;
|
|
BoxElem->Z = 0.5f * PhAT::DefaultPrimSize;
|
|
}
|
|
else
|
|
{
|
|
BoxElem->SetTransform( BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBody()->PrimitiveIndex].GetTransform() );
|
|
BoxElem->Center.X += PhAT::DuplicateXOffset;
|
|
|
|
BoxElem->X = BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBody()->PrimitiveIndex].X;
|
|
BoxElem->Y = BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBody()->PrimitiveIndex].Y;
|
|
BoxElem->Z = BodySetup->AggGeom.BoxElems[SharedData->GetSelectedBody()->PrimitiveIndex].Z;
|
|
}
|
|
}
|
|
else if (PrimitiveType == KPT_Sphyl)
|
|
{
|
|
NewPrimIndex = BodySetup->AggGeom.SphylElems.Add(FKSphylElem());
|
|
NewSelection[i].PrimitiveType = KPT_Sphyl;
|
|
NewSelection[i].PrimitiveIndex = NewPrimIndex;
|
|
FKSphylElem* SphylElem = &BodySetup->AggGeom.SphylElems[NewPrimIndex];
|
|
|
|
if (!bCopySelected)
|
|
{
|
|
SphylElem->SetTransform( FTransform::Identity );
|
|
|
|
SphylElem->Length = PhAT::DefaultPrimSize;
|
|
SphylElem->Radius = PhAT::DefaultPrimSize;
|
|
}
|
|
else
|
|
{
|
|
SphylElem->SetTransform( BodySetup->AggGeom.SphylElems[SharedData->GetSelectedBody()->PrimitiveIndex].GetTransform() );
|
|
SphylElem->Center.X += PhAT::DuplicateXOffset;
|
|
|
|
SphylElem->Length = BodySetup->AggGeom.SphylElems[SharedData->GetSelectedBody()->PrimitiveIndex].Length;
|
|
SphylElem->Radius = BodySetup->AggGeom.SphylElems[SharedData->GetSelectedBody()->PrimitiveIndex].Radius;
|
|
}
|
|
}
|
|
else if (PrimitiveType == KPT_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 = KPT_Convex;
|
|
NewSelection[i].PrimitiveIndex = NewPrimIndex;
|
|
FKConvexElem* ConvexElem = &BodySetup->AggGeom.ConvexElems[NewPrimIndex];
|
|
|
|
ConvexElem->SetTransform(BodySetup->AggGeom.ConvexElems[SharedData->GetSelectedBody()->PrimitiveIndex].GetTransform());
|
|
|
|
// Copy all of the vertices of the convex element
|
|
for (FVector v : BodySetup->AggGeom.ConvexElems[SharedData->GetSelectedBody()->PrimitiveIndex].VertexData)
|
|
{
|
|
v.X += PhAT::DuplicateXOffset;
|
|
ConvexElem->VertexData.Add(v);
|
|
}
|
|
ConvexElem->UpdateElemBox();
|
|
|
|
BodySetup->InvalidatePhysicsData();
|
|
BodySetup->CreatePhysicsMeshes();
|
|
}
|
|
else
|
|
{
|
|
check(0); //unrecognized primitive type
|
|
}
|
|
}
|
|
} // ScopedTransaction
|
|
|
|
//clear selection
|
|
SharedData->SetSelectedBody(NULL);
|
|
for(int32 i=0; i<NewSelection.Num(); ++i)
|
|
{
|
|
SharedData->SetSelectedBody(&NewSelection[i], true);
|
|
}
|
|
|
|
RefreshHierachyTree();
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
void FPhAT::SetBodiesBelowSelectedPhysicsType( EPhysicsType InPhysicsType, bool bMarkAsDirty)
|
|
{
|
|
TArray<int32> Indices;
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
Indices.Add(SharedData->SelectedBodies[i].Index);
|
|
}
|
|
|
|
SetBodiesBelowPhysicsType(InPhysicsType, Indices, bMarkAsDirty);
|
|
}
|
|
|
|
void FPhAT::SetBodiesBelowPhysicsType( EPhysicsType InPhysicsType, const TArray<int32> & Indices, bool bMarkAsDirty)
|
|
{
|
|
TArray<int32> BelowBodies;
|
|
|
|
for(int32 i=0; i<Indices.Num(); ++i)
|
|
{
|
|
// Get the index of this body
|
|
UBodySetup* BaseSetup = SharedData->PhysicsAsset->BodySetup[Indices[i]];
|
|
SharedData->PhysicsAsset->GetBodyIndicesBelow(BelowBodies, BaseSetup->BoneName, SharedData->EditorSkelMesh);
|
|
}
|
|
|
|
for (int32 i = 0; i < BelowBodies.Num(); ++i)
|
|
{
|
|
int32 BodyIndex = BelowBodies[i];
|
|
UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[BodyIndex];
|
|
if (bMarkAsDirty)
|
|
{
|
|
BodySetup->Modify();
|
|
}
|
|
|
|
BodySetup->PhysicsType = InPhysicsType;
|
|
}
|
|
}
|
|
|
|
bool FPhAT::IsNotSimulation() const
|
|
{
|
|
return !SharedData->bRunningSimulation;
|
|
}
|
|
|
|
bool FPhAT::IsEditBodyMode() const
|
|
{
|
|
return IsNotSimulation() && (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit);
|
|
}
|
|
|
|
bool FPhAT::IsSelectedEditBodyMode() const
|
|
{
|
|
return IsEditBodyMode() && (SharedData->GetSelectedBody());
|
|
}
|
|
|
|
bool FPhAT::IsEditConstraintMode() const
|
|
{
|
|
return IsNotSimulation() && (SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit);
|
|
}
|
|
|
|
bool FPhAT::IsSelectedEditConstraintMode() const
|
|
{
|
|
return IsEditConstraintMode() && (SharedData->GetSelectedConstraint());
|
|
}
|
|
|
|
bool FPhAT::IsSelectedEditMode() const
|
|
{
|
|
return IsSelectedEditBodyMode() || IsSelectedEditConstraintMode();
|
|
}
|
|
|
|
void FPhAT::OnChangeDefaultMesh()
|
|
{
|
|
// Get the currently selected SkeletalMesh. Fail if there ain't one.
|
|
FEditorDelegates::LoadSelectedAssetsIfNeeded.Broadcast();
|
|
|
|
USkeletalMesh* NewSkelMesh = GEditor->GetSelectedObjects()->GetTop<USkeletalMesh>();
|
|
if (!NewSkelMesh)
|
|
{
|
|
FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("NoSkelMeshSelected", "No SkeletalMesh Selected.\nSelect the SkeletalMesh in the Content Browser that you want to use as the new Default SkeletalMesh for this PhysicsAsset."));
|
|
return;
|
|
}
|
|
|
|
// Confirm they want to do this.
|
|
bool bDoChange = EAppReturnType::Yes == FMessageDialog::Open(EAppMsgType::YesNo,
|
|
FText::Format(NSLOCTEXT("UnrealEd", "SureChangeAssetSkelMesh", "Are you sure you want to change the PhysicsAsset '{0}' to use the SkeletalMesh '{1}'?"),
|
|
FText::FromString(SharedData->PhysicsAsset->GetName()), FText::FromString(NewSkelMesh->GetName()) ));
|
|
if (bDoChange)
|
|
{
|
|
// See if any bones are missing from the skeletal mesh we are trying to use
|
|
// @todo Could do more here - check for bone lengths etc. Maybe modify asset?
|
|
for (int32 i = 0; i <SharedData->PhysicsAsset->BodySetup.Num(); ++i)
|
|
{
|
|
FName BodyName = SharedData->PhysicsAsset->BodySetup[i]->BoneName;
|
|
int32 BoneIndex = NewSkelMesh->RefSkeleton.FindBoneIndex(BodyName);
|
|
if (BoneIndex == INDEX_NONE)
|
|
{
|
|
FMessageDialog::Open(EAppMsgType::Ok,
|
|
FText::Format( NSLOCTEXT("UnrealEd", "BoneMissingFromSkelMesh", "The SkeletalMesh is missing bone '{0}' needed by this PhysicsAsset."), FText::FromName(BodyName) ));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We have all the bones - go ahead and make the change.
|
|
SharedData->PhysicsAsset->PreviewSkeletalMesh = NewSkelMesh;
|
|
|
|
// Change preview
|
|
SharedData->EditorSkelMesh = NewSkelMesh;
|
|
SharedData->EditorSkelComp->SetSkeletalMesh(NewSkelMesh);
|
|
|
|
IMeshUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshUtilities>("MeshUtilities");
|
|
// Update various infos based on the mesh
|
|
MeshUtilities.CalcBoneVertInfos(SharedData->EditorSkelMesh, SharedData->DominantWeightBoneInfos, true);
|
|
MeshUtilities.CalcBoneVertInfos(SharedData->EditorSkelMesh, SharedData->AnyWeightBoneInfos, false);
|
|
RefreshHierachyTree();
|
|
|
|
// Mark asset's package as dirty as its changed.
|
|
SharedData->PhysicsAsset->MarkPackageDirty();
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnResetEntireAsset()
|
|
{
|
|
bool bDoRecalc = EAppReturnType::Yes == FMessageDialog::Open(EAppMsgType::YesNo, NSLOCTEXT("UnrealEd", "Prompt_12", "This will completely replace the current asset.\nAre you sure?"));
|
|
if (!bDoRecalc)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Make sure rendering is done - so we are not changing data being used by collision drawing.
|
|
FlushRenderingCommands();
|
|
|
|
// Then calculate a new one.
|
|
|
|
SharedData->OpenNewBodyDlg();
|
|
|
|
if (SharedData->NewBodyResponse != EAppReturnType::Cancel)
|
|
{
|
|
// Deselect everything.
|
|
SharedData->SetSelectedBody(NULL);
|
|
SharedData->SetSelectedConstraint(INDEX_NONE);
|
|
|
|
// Empty current asset data.
|
|
SharedData->PhysicsAsset->BodySetup.Empty();
|
|
SharedData->PhysicsAsset->BodySetupIndexMap.Empty();
|
|
SharedData->PhysicsAsset->ConstraintSetup.Empty();
|
|
|
|
FText ErrorMessage;
|
|
if (FPhysicsAssetUtils::CreateFromSkeletalMesh(SharedData->PhysicsAsset, SharedData->EditorSkelMesh, SharedData->NewBodyData, ErrorMessage) == false)
|
|
{
|
|
FMessageDialog::Open(EAppMsgType::Ok, ErrorMessage);
|
|
}
|
|
|
|
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
|
|
|
|
RefreshHierachyTree();
|
|
RefreshPreviewViewport();
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnResetBoneCollision()
|
|
{
|
|
bool bDoRecalc = EAppReturnType::Yes == FMessageDialog::Open(EAppMsgType::YesNo, NSLOCTEXT("UnrealEd", "Prompt_13", "This will completely replace the current bone collision.\nAre you sure?"));
|
|
if (!bDoRecalc)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SharedData->OpenNewBodyDlg();
|
|
|
|
if (SharedData->NewBodyResponse == EAppReturnType::Cancel)
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
TArray<int32> BodyIndices;
|
|
const FScopedTransaction Transaction( NSLOCTEXT("PhAT", "ResetBoneCollision", "Reset Bone Collision") );
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[SharedData->SelectedBodies[i].Index];
|
|
check(BodySetup);
|
|
BodySetup->Modify();
|
|
|
|
int32 BoneIndex = SharedData->EditorSkelMesh->RefSkeleton.FindBoneIndex(BodySetup->BoneName);
|
|
check(BoneIndex != INDEX_NONE);
|
|
|
|
FPhysicsAssetUtils::CreateCollisionFromBone(BodySetup, SharedData->EditorSkelMesh, BoneIndex, SharedData->NewBodyData, (SharedData->NewBodyData.VertWeight == EVW_DominantWeight)? SharedData->DominantWeightBoneInfos: SharedData->AnyWeightBoneInfos);
|
|
BodyIndices.AddUnique(SharedData->SelectedBodies[i].Index);
|
|
}
|
|
|
|
//deselect first
|
|
SharedData->SetSelectedBody(NULL);
|
|
for(int32 i=0; i<BodyIndices.Num(); ++i)
|
|
{
|
|
SharedData->SetSelectedBodyAnyPrim(BodyIndices[i], true);
|
|
}
|
|
} // scoped transaction
|
|
|
|
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
void FPhAT::OnApplyPhysicalMaterial()
|
|
{
|
|
FEditorDelegates::LoadSelectedAssetsIfNeeded.Broadcast();
|
|
UPhysicalMaterial* SelectedPhysMaterial = GEditor->GetSelectedObjects()->GetTop<UPhysicalMaterial>();
|
|
|
|
if (SelectedPhysMaterial)
|
|
{
|
|
for (int32 BodyIdx=0; BodyIdx<SharedData->PhysicsAsset->BodySetup.Num(); BodyIdx++)
|
|
{
|
|
UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[BodyIdx];
|
|
BodySetup->Modify();
|
|
BodySetup->PhysMaterial = SelectedPhysMaterial;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnEditingMode(int32 Mode)
|
|
{
|
|
if (Mode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
SharedData->EditingMode = FPhATSharedData::PEM_BodyEdit;
|
|
RefreshHierachyTree();
|
|
SharedData->SetSelectedBody(NULL, true); // Forces properties panel to update...
|
|
}
|
|
else
|
|
{
|
|
SharedData->EditingMode = FPhATSharedData::PEM_ConstraintEdit;
|
|
RefreshHierachyTree();
|
|
SharedData->SetSelectedConstraint(INDEX_NONE, true);
|
|
}
|
|
|
|
RefreshPreviewViewport();
|
|
|
|
// Rebuild the toolbar, as the icons shown will have changed
|
|
ExtendToolbar();
|
|
RegenerateMenusAndToolbars();
|
|
|
|
OnAddPhatRecord(TEXT("ModeSelected"), false, true);
|
|
}
|
|
|
|
bool FPhAT::IsEditingMode(int32 Mode) const
|
|
{
|
|
return (FPhATSharedData::EPhATEditingMode)Mode == SharedData->EditingMode;
|
|
}
|
|
|
|
void FPhAT::OnCopyProperties()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
SharedData->CopyBody();
|
|
}else
|
|
{
|
|
SharedData->CopyConstraint();
|
|
}
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
void FPhAT::OnPasteProperties()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
SharedData->PasteBodyProperties();
|
|
}else
|
|
{
|
|
SharedData->PasteConstraintProperties();
|
|
}
|
|
|
|
}
|
|
|
|
bool FPhAT::CanCopyProperties() const
|
|
{
|
|
if(IsSelectedEditMode())
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit && SharedData->SelectedBodies.Num() == 1)
|
|
{
|
|
return true;
|
|
}else if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->SelectedConstraints.Num() == 1)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FPhAT::CanPasteProperties() const
|
|
{
|
|
return IsSelectedEditMode() && IsCopyProperties();
|
|
}
|
|
|
|
bool FPhAT::IsCopyProperties() const
|
|
{
|
|
return (SharedData->CopiedBodySetup && SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit) || (SharedData->CopiedConstraintTemplate && SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit);
|
|
}
|
|
|
|
void FPhAT::OnInstanceProperties()
|
|
{
|
|
SharedData->ToggleInstanceProperties();
|
|
}
|
|
|
|
bool FPhAT::IsInstanceProperties() const
|
|
{
|
|
return SharedData->bShowInstanceProps;
|
|
}
|
|
|
|
//We need to save and restore physics states based on the mode we use to simulate
|
|
void FPhAT::FixPhysicsState()
|
|
{
|
|
UPhysicsAsset * PhysicsAsset = SharedData->PhysicsAsset;
|
|
TArray<UBodySetup*> & BodySetup = PhysicsAsset->BodySetup;
|
|
|
|
if(!SharedData->bRunningSimulation)
|
|
{
|
|
PhysicsTypeState.Reset();
|
|
for(int32 i=0; i<SharedData->PhysicsAsset->BodySetup.Num(); ++i)
|
|
{
|
|
PhysicsTypeState.Add(BodySetup[i]->PhysicsType);
|
|
}
|
|
}else
|
|
{
|
|
for(int32 i=0; i<PhysicsTypeState.Num(); ++i)
|
|
{
|
|
BodySetup[i]->PhysicsType = PhysicsTypeState[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPhAT::ImpToggleSimulation()
|
|
{
|
|
static const int32 PrevMaxFPS = GEngine->GetMaxFPS();
|
|
|
|
TSharedPtr<FPhATEdPreviewViewportClient> PreviewClient = PreviewViewport->GetViewportClient();
|
|
if(!SharedData->bRunningSimulation)
|
|
{
|
|
BeforeSimulationWidgetMode = PreviewClient->GetWidgetMode();
|
|
PreviewClient->SetWidgetMode(FWidget::EWidgetMode::WM_None);
|
|
GEngine->SetMaxFPS(SharedData->EditorSimOptions->MaxFPS);
|
|
}
|
|
else
|
|
{
|
|
PreviewClient->SetWidgetMode(BeforeSimulationWidgetMode);
|
|
GEngine->SetMaxFPS(PrevMaxFPS);
|
|
}
|
|
|
|
|
|
SharedData->ToggleSimulation();
|
|
|
|
if (!PreviewViewport->GetViewportClient()->IsRealtime() && !IsPIERunning())
|
|
{
|
|
PreviewViewport->GetViewportClient()->SetRealtime(true);
|
|
}
|
|
|
|
// add to analytics record
|
|
OnAddPhatRecord(TEXT("ToggleSimulate"), true, true);
|
|
}
|
|
|
|
void FPhAT::OnSetSimulationMode(EPhATSimulationMode Mode)
|
|
{
|
|
SimulationMode = Mode;
|
|
OnToggleSimulation();
|
|
}
|
|
|
|
bool FPhAT::IsSimulationMode(EPhATSimulationMode Mode) const
|
|
{
|
|
return SimulationMode == Mode;
|
|
}
|
|
|
|
void FPhAT::OnToggleSimulation()
|
|
{
|
|
// 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())
|
|
{
|
|
OnSelectedSimulation();
|
|
}
|
|
SharedData->bNoGravitySimulation = IsSimulationMode(EPSM_Gravity);
|
|
ImpToggleSimulation();
|
|
}
|
|
|
|
bool FPhAT::IsSelectedSimulation()
|
|
{
|
|
return SelectedSimulation;
|
|
}
|
|
|
|
void FPhAT::OnToggleSelectedSimulation()
|
|
{
|
|
SelectedSimulation = !SelectedSimulation;
|
|
}
|
|
|
|
void FPhAT::OnSelectedSimulation()
|
|
{
|
|
|
|
//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<UBodySetup*> & BodySetup = PhysicsAsset->BodySetup;
|
|
|
|
//first we fix all the bodies
|
|
for(int32 i=0; i<SharedData->PhysicsAsset->BodySetup.Num(); ++i)
|
|
{
|
|
BodySetup[i]->PhysicsType = PhysType_Kinematic;
|
|
}
|
|
|
|
//Find which bodes have been selected
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
//Bodies already have a function that does this
|
|
SetBodiesBelowSelectedPhysicsType(PhysType_Simulated, false);
|
|
}else
|
|
{
|
|
//constraints need some more work
|
|
TArray<int32> BodyIndices;
|
|
TArray<UPhysicsConstraintTemplate*> & ConstraintSetup = PhysicsAsset->ConstraintSetup;
|
|
for(int32 i=0; i<SharedData->SelectedConstraints.Num(); ++i)
|
|
{
|
|
int32 ConstraintIndex = SharedData->SelectedConstraints[i].Index;
|
|
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 FPhAT::IsToggleSimulation() const
|
|
{
|
|
return SharedData->bRunningSimulation;
|
|
}
|
|
|
|
void FPhAT::OnMeshRenderingMode(FPhATSharedData::EPhATRenderMode Mode)
|
|
{
|
|
if (SharedData->bRunningSimulation)
|
|
{
|
|
SharedData->Sim_MeshViewMode = Mode;
|
|
}
|
|
else if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
SharedData->BodyEdit_MeshViewMode = Mode;
|
|
}
|
|
else
|
|
{
|
|
SharedData->ConstraintEdit_MeshViewMode = Mode;
|
|
}
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsMeshRenderingMode(FPhATSharedData::EPhATRenderMode Mode) const
|
|
{
|
|
return Mode == SharedData->GetCurrentMeshViewMode();
|
|
}
|
|
|
|
void FPhAT::OnCollisionRenderingMode(FPhATSharedData::EPhATRenderMode Mode)
|
|
{
|
|
if (SharedData->bRunningSimulation)
|
|
{
|
|
SharedData->Sim_CollisionViewMode = Mode;
|
|
}
|
|
else if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
SharedData->BodyEdit_CollisionViewMode = Mode;
|
|
}
|
|
else
|
|
{
|
|
SharedData->ConstraintEdit_CollisionViewMode = Mode;
|
|
}
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsCollisionRenderingMode(FPhATSharedData::EPhATRenderMode Mode) const
|
|
{
|
|
return Mode == SharedData->GetCurrentCollisionViewMode();
|
|
}
|
|
|
|
void FPhAT::OnConstraintRenderingMode(FPhATSharedData::EPhATConstraintViewMode Mode)
|
|
{
|
|
if (SharedData->bRunningSimulation)
|
|
{
|
|
SharedData->Sim_ConstraintViewMode = Mode;
|
|
}
|
|
else if (SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
SharedData->BodyEdit_ConstraintViewMode = Mode;
|
|
}
|
|
else
|
|
{
|
|
SharedData->ConstraintEdit_ConstraintViewMode = Mode;
|
|
}
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsConstraintRenderingMode(FPhATSharedData::EPhATConstraintViewMode Mode) const
|
|
{
|
|
return Mode == SharedData->GetCurrentConstraintViewMode();
|
|
}
|
|
|
|
void FPhAT::OnShowFixedBodies()
|
|
{
|
|
SharedData->bShowFixedStatus = !SharedData->bShowFixedStatus;
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsShowFixedBodies() const
|
|
{
|
|
return SharedData->bShowFixedStatus;
|
|
}
|
|
|
|
void FPhAT::OnDrawGroundBox()
|
|
{
|
|
SharedData->bDrawGround = !SharedData->bDrawGround;
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsDrawGroundBox() const
|
|
{
|
|
return SharedData->bDrawGround;
|
|
}
|
|
|
|
void FPhAT::OnToggleGraphicsHierarchy()
|
|
{
|
|
SharedData->bShowHierarchy = !SharedData->bShowHierarchy;
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsToggleGraphicsHierarchy() const
|
|
{
|
|
return SharedData->bShowHierarchy;
|
|
}
|
|
|
|
void FPhAT::OnToggleBoneInfluences()
|
|
{
|
|
SharedData->bShowInfluences = !SharedData->bShowInfluences;
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsToggleBoneInfluences() const
|
|
{
|
|
return SharedData->bShowInfluences;
|
|
}
|
|
|
|
void FPhAT::OnToggleMassProperties()
|
|
{
|
|
SharedData->bShowCOM = !SharedData->bShowCOM;
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsToggleMassProperties() const
|
|
{
|
|
return SharedData->bShowCOM;
|
|
}
|
|
|
|
void FPhAT::OnSetCollision(bool bEnable)
|
|
{
|
|
SharedData->SetCollisionBetweenSelected(bEnable);
|
|
}
|
|
|
|
bool FPhAT::CanSetCollision() const
|
|
{
|
|
if(IsSelectedEditBodyMode())
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit && SharedData->SelectedBodies.Num() > 1)
|
|
{
|
|
return true;
|
|
}else if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->SelectedConstraints.Num() > 1)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void FPhAT::OnWeldToBody()
|
|
{
|
|
SharedData->WeldSelectedBodies();
|
|
}
|
|
|
|
bool FPhAT::CanWeldToBody()
|
|
{
|
|
return IsSelectedEditBodyMode() && SharedData->WeldSelectedBodies(false);
|
|
}
|
|
|
|
void FPhAT::OnAddNewBody()
|
|
{
|
|
TArray<FTreeElemPtr> Elems = Hierarchy->GetSelectedItems();
|
|
|
|
if(Elems.Num())
|
|
{
|
|
SharedData->OpenNewBodyDlg();
|
|
|
|
if (SharedData->NewBodyResponse == EAppReturnType::Cancel)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FScopedTransaction Transaction( NSLOCTEXT("PhAT", "AddNewPrimitive", "Add New Body") );
|
|
|
|
// Make sure rendering is done - so we are not changing data being used by collision drawing.
|
|
FlushRenderingCommands();
|
|
|
|
for(int32 i=0; i<Elems.Num(); ++i)
|
|
{
|
|
int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(Elems[i]->Name);
|
|
if (BoneIndex != INDEX_NONE)
|
|
{
|
|
SharedData->MakeNewBody(BoneIndex);
|
|
}
|
|
}
|
|
|
|
RefreshPreviewViewport();
|
|
RefreshHierachyTree();
|
|
}
|
|
|
|
}
|
|
|
|
void FPhAT::OnAddSphere()
|
|
{
|
|
AddNewPrimitive(KPT_Sphere);
|
|
}
|
|
|
|
void FPhAT::OnAddSphyl()
|
|
{
|
|
AddNewPrimitive(KPT_Sphyl);
|
|
}
|
|
|
|
void FPhAT::OnAddBox()
|
|
{
|
|
AddNewPrimitive(KPT_Box);
|
|
}
|
|
|
|
bool FPhAT::CanAddPrimitive() const
|
|
{
|
|
return IsEditBodyMode();
|
|
}
|
|
|
|
void FPhAT::OnDeletePrimitive()
|
|
{
|
|
SharedData->DeleteCurrentPrim();
|
|
}
|
|
|
|
void FPhAT::OnDuplicatePrimitive()
|
|
{
|
|
AddNewPrimitive(KPT_Unknown, true);
|
|
}
|
|
|
|
bool FPhAT::CanDuplicatePrimitive() const
|
|
{
|
|
return IsSelectedEditBodyMode() && SharedData->SelectedBodies.Num() == 1;
|
|
}
|
|
|
|
void FPhAT::OnResetConstraint()
|
|
{
|
|
SharedData->SetSelectedConstraintRelTM(FTransform::Identity);
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
void FPhAT::OnSnapConstraint()
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "SnapConstraints", "Snap Constraints" ) );
|
|
|
|
for(int32 i=0; i<SharedData->SelectedConstraints.Num(); ++i)
|
|
{
|
|
FTransform ParentFrame = SharedData->GetConstraintWorldTM(&SharedData->SelectedConstraints[i], EConstraintFrame::Frame2);
|
|
SnapConstraintToBone(SharedData->SelectedConstraints[i].Index, ParentFrame);
|
|
}
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
void FPhAT::OnConvertToBallAndSocket()
|
|
{
|
|
CreateOrConvertConstraint(EPCT_BSJoint);
|
|
}
|
|
|
|
void FPhAT::OnConvertToHinge()
|
|
{
|
|
CreateOrConvertConstraint(EPCT_Hinge);
|
|
}
|
|
|
|
void FPhAT::OnConvertToPrismatic()
|
|
{
|
|
CreateOrConvertConstraint(EPCT_Prismatic);
|
|
}
|
|
|
|
void FPhAT::OnConvertToSkeletal()
|
|
{
|
|
CreateOrConvertConstraint(EPCT_SkelJoint);
|
|
}
|
|
|
|
void FPhAT::OnDeleteConstraint()
|
|
{
|
|
SharedData->DeleteCurrentConstraint();
|
|
}
|
|
|
|
void FPhAT::OnPlayAnimation()
|
|
{
|
|
if (!SharedData->EditorSkelComp->IsPlaying() && SharedData->bRunningSimulation)
|
|
{
|
|
SharedData->EditorSkelComp->SetAnimation(SelectedAnimation);
|
|
SharedData->EditorSkelComp->Play(true);
|
|
}
|
|
else
|
|
{
|
|
SharedData->EditorSkelComp->Stop();
|
|
}
|
|
}
|
|
|
|
bool FPhAT::IsPlayAnimation() const
|
|
{
|
|
return SharedData->EditorSkelComp->IsPlaying();
|
|
}
|
|
|
|
void FPhAT::OnViewType(ELevelViewportType ViewType)
|
|
{
|
|
if (PreviewViewport.IsValid())
|
|
{
|
|
PreviewViewport->SetViewportType(ViewType);
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnShowSkeleton()
|
|
{
|
|
SharedData->bShowAnimSkel = !SharedData->bShowAnimSkel;
|
|
|
|
RefreshPreviewViewport();
|
|
}
|
|
|
|
bool FPhAT::IsShowSkeleton() const
|
|
{
|
|
return SharedData->bShowAnimSkel;
|
|
}
|
|
|
|
void FPhAT::OnSetBodyPhysicsType( EPhysicsType InPhysicsType )
|
|
{
|
|
if (SharedData->GetSelectedBody())
|
|
{
|
|
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[SharedData->SelectedBodies[i].Index];
|
|
BodySetup->Modify();
|
|
BodySetup->PhysicsType = InPhysicsType;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
bool FPhAT::IsBodyPhysicsType( EPhysicsType InPhysicsType )
|
|
{
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[SharedData->SelectedBodies[i].Index];
|
|
if(BodySetup->PhysicsType == InPhysicsType)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FPhAT::OnDeleteBody()
|
|
{
|
|
if(SharedData->SelectedBodies.Num())
|
|
{
|
|
//first build the bodysetup array because deleting bodies modifies the selected array
|
|
TArray<UBodySetup*> BodySetups;
|
|
BodySetups.Reserve(SharedData->SelectedBodies.Num());
|
|
|
|
for(int32 i=0; i<SharedData->SelectedBodies.Num(); ++i)
|
|
{
|
|
BodySetups.Add( SharedData->PhysicsAsset->BodySetup[SharedData->SelectedBodies[i].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 PhAT function to delete action (so undo works etc)
|
|
SharedData->DeleteBody(BodyIndex, false);
|
|
}
|
|
}
|
|
|
|
SharedData->RefreshPhysicsAssetChange(SharedData->PhysicsAsset);
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnDeleteAllBodiesBelow()
|
|
{
|
|
TArray<UBodySetup*> BodySetups;
|
|
|
|
for (FPhATSharedData::FSelection SelectedBody : SharedData->SelectedBodies)
|
|
{
|
|
UBodySetup* BaseSetup = SharedData->PhysicsAsset->BodySetup[SelectedBody.Index];
|
|
|
|
// Build a list of BodySetups below this one
|
|
TArray<int32> BelowBodies;
|
|
SharedData->PhysicsAsset->GetBodyIndicesBelow(BelowBodies, BaseSetup->BoneName, SharedData->EditorSkelMesh);
|
|
|
|
for (const int32 BodyIndex : BelowBodies)
|
|
{
|
|
UBodySetup* BodySetup = SharedData->PhysicsAsset->BodySetup[BodyIndex];
|
|
BodySetups.Add(BodySetup);
|
|
}
|
|
}
|
|
|
|
if(BodySetups.Num())
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "DeleteBodiesBelow", "Delete Bodies Below" ) );
|
|
|
|
// Now remove each one
|
|
for (UBodySetup* BodySetup : BodySetups)
|
|
{
|
|
// Use PhAT 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 FPhAT::OnToggleMotor()
|
|
{
|
|
for(int32 i=0; i<SharedData->SelectedConstraints.Num(); ++i)
|
|
{
|
|
UPhysicsConstraintTemplate* ConSetup = SharedData->PhysicsAsset->ConstraintSetup[SharedData->SelectedConstraints[i].Index];
|
|
FConstraintInstance* CI = &ConSetup->DefaultInstance;
|
|
|
|
CI->bAngularOrientationDrive = !CI->bAngularOrientationDrive;
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnEnableMotorsBelow()
|
|
{
|
|
SetConstraintsBelowSelectedMotorised(true);
|
|
}
|
|
|
|
void FPhAT::OnDisableMotorsBelow()
|
|
{
|
|
SetConstraintsBelowSelectedMotorised(false);
|
|
}
|
|
|
|
void FPhAT::OnLockSelection()
|
|
{
|
|
SharedData->bSelectionLock = !SharedData->bSelectionLock;
|
|
}
|
|
|
|
void FPhAT::OnDeleteSelection()
|
|
{
|
|
switch(SharedData->EditingMode)
|
|
{
|
|
case FPhATSharedData::PEM_BodyEdit:
|
|
{
|
|
SharedData->DeleteCurrentPrim();
|
|
}
|
|
break;
|
|
case FPhATSharedData::PEM_ConstraintEdit:
|
|
{
|
|
SharedData->DeleteCurrentConstraint();
|
|
}
|
|
break;
|
|
/** Add new editor modes here */
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnCycleConstraintOrientation()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->GetSelectedConstraint())
|
|
{
|
|
SharedData->CycleCurrentConstraintOrientation();
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnCycleConstraintActive()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->GetSelectedConstraint())
|
|
{
|
|
SharedData->CycleCurrentConstraintActive();
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnToggleSwing1()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->GetSelectedConstraint())
|
|
{
|
|
SharedData->ToggleConstraint(FPhATSharedData::PCT_Swing1);
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnToggleSwing2()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->GetSelectedConstraint())
|
|
{
|
|
SharedData->ToggleConstraint(FPhATSharedData::PCT_Swing2);
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnToggleTwist()
|
|
{
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit && SharedData->GetSelectedConstraint())
|
|
{
|
|
SharedData->ToggleConstraint(FPhATSharedData::PCT_Twist);
|
|
}
|
|
}
|
|
|
|
void FPhAT::OnFocusSelection()
|
|
{
|
|
switch(SharedData->EditingMode)
|
|
{
|
|
case FPhATSharedData::PEM_BodyEdit:
|
|
{
|
|
if(SharedData->GetSelectedBody())
|
|
{
|
|
int32 BoneIdx = SharedData->EditorSkelComp->GetBoneIndex(SharedData->PhysicsAsset->BodySetup[SharedData->GetSelectedBody()->Index]->BoneName);
|
|
FMatrix BoneTransform = SharedData->EditorSkelComp->GetBoneMatrix(BoneIdx);
|
|
FBoxSphereBounds Bounds(BoneTransform.GetOrigin(), FVector(20), 20);
|
|
|
|
PreviewViewport->GetViewportClient()->FocusViewportOnBox(Bounds.GetBox());
|
|
}
|
|
}
|
|
break;
|
|
case FPhATSharedData::PEM_ConstraintEdit:
|
|
{
|
|
if(SharedData->GetSelectedConstraint())
|
|
{
|
|
FTransform ConstraintTransform = SharedData->GetConstraintMatrix(SharedData->GetSelectedConstraint()->Index, EConstraintFrame::Frame2, 1.0f);
|
|
FBoxSphereBounds Bounds(ConstraintTransform.GetTranslation(), FVector(20), 20);
|
|
|
|
PreviewViewport->GetViewportClient()->FocusViewportOnBox(Bounds.GetBox());
|
|
}
|
|
}
|
|
break;
|
|
/** Add any new edit modes here */
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
TSharedRef<SWidget> FPhAT::BuildStaticMeshAssetPicker()
|
|
{
|
|
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
|
|
|
|
FAssetPickerConfig AssetPickerConfig;
|
|
AssetPickerConfig.Filter.ClassNames.Add(UStaticMesh::StaticClass()->GetFName());
|
|
AssetPickerConfig.OnAssetDoubleClicked = FOnAssetDoubleClicked::CreateSP(this, &FPhAT::OnAssetSelectedFromStaticMeshAssetPicker);
|
|
AssetPickerConfig.bAllowNullSelection = true;
|
|
AssetPickerConfig.InitialAssetViewType = EAssetViewType::List;
|
|
AssetPickerConfig.bFocusSearchBoxWhenOpened = true;
|
|
AssetPickerConfig.bShowBottomToolbar = false;
|
|
AssetPickerConfig.SelectionMode = ESelectionMode::Single;
|
|
|
|
return SNew(SBox)
|
|
.WidthOverride(384)
|
|
.HeightOverride(768)
|
|
[
|
|
ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig)
|
|
];
|
|
}
|
|
|
|
TSharedRef<SWidget> FPhAT::BuildHierarchyFilterMenu()
|
|
{
|
|
const bool bShouldCloseWindowAfterMenuSelection = true; // Set the menu to automatically close when the user commits to a choice
|
|
const FPhATCommands& Commands = FPhATCommands::Get();
|
|
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, GetToolkitCommands());
|
|
{
|
|
MenuBuilder.AddMenuEntry(Commands.HierarchyFilterAll);
|
|
MenuBuilder.AddMenuEntry(Commands.HierarchyFilterBodies);
|
|
}
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
FText FPhAT::GetHierarchyFilter() const
|
|
{
|
|
FText FilterMenuText;
|
|
|
|
switch (HierarchyFilterMode)
|
|
{
|
|
case PHFM_All:
|
|
FilterMenuText = FPhATCommands::Get().HierarchyFilterAll->GetLabel();
|
|
break;
|
|
case PHFM_Bodies:
|
|
FilterMenuText = FPhATCommands::Get().HierarchyFilterBodies->GetLabel();
|
|
break;
|
|
default:
|
|
// Unknown mode
|
|
check(0);
|
|
break;
|
|
}
|
|
|
|
return FilterMenuText;
|
|
|
|
}
|
|
|
|
void FPhAT::OnAssetSelectedFromStaticMeshAssetPicker( const FAssetData& AssetData )
|
|
{
|
|
PickerComboButton->SetIsOpen(false);
|
|
|
|
const FScopedTransaction Transaction( NSLOCTEXT("PhAT", "Import Convex", "Import Convex") );
|
|
// Make sure rendering is done - so we are not changing data being used by collision drawing.
|
|
FlushRenderingCommands();
|
|
|
|
if (SharedData->GetSelectedBody())
|
|
{
|
|
SharedData->PhysicsAsset->Modify();
|
|
|
|
// Build a list of BodySetups below this one
|
|
UBodySetup* BaseSetup = SharedData->PhysicsAsset->BodySetup[SharedData->GetSelectedBody()->Index];
|
|
BaseSetup->Modify();
|
|
|
|
UStaticMesh* SM = Cast<UStaticMesh>(AssetData.GetAsset());
|
|
|
|
if (SM && SM->BodySetup && SM->BodySetup->AggGeom.GetElementCount() > 0)
|
|
{
|
|
BaseSetup->AddCollisionFrom(SM->BodySetup);
|
|
|
|
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());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FPhAT::CanStartSimulation() const
|
|
{
|
|
return !IsPIERunning();
|
|
}
|
|
|
|
bool FPhAT::IsPIERunning()
|
|
{
|
|
for (const FWorldContext& Context : GEngine->GetWorldContexts())
|
|
{
|
|
if (Context.World()->IsPlayInEditor())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FPhAT::SetHierarchyFilter(EPhatHierarchyFilterMode Mode)
|
|
{
|
|
HierarchyFilterMode = Mode;
|
|
RefreshHierachyTree();
|
|
RefreshHierachyTreeSelection();
|
|
}
|
|
|
|
void FPhAT::OnSelectAll()
|
|
{
|
|
UPhysicsAsset * const PhysicsAsset = SharedData->EditorSkelComp->GetPhysicsAsset();
|
|
|
|
|
|
if(SharedData->EditingMode == FPhATSharedData::PEM_BodyEdit)
|
|
{
|
|
//Bodies
|
|
//first deselect everything
|
|
SharedData->SetSelectedBody(NULL);
|
|
|
|
//go through every body and add every geom
|
|
for (int32 i = 0; i <PhysicsAsset->BodySetup.Num(); ++i)
|
|
{
|
|
int32 BoneIndex = SharedData->EditorSkelComp->GetBoneIndex(PhysicsAsset->BodySetup[i]->BoneName);
|
|
|
|
// If we found a bone for it, add all geom
|
|
if (BoneIndex != INDEX_NONE)
|
|
{
|
|
FKAggregateGeom* AggGeom = &PhysicsAsset->BodySetup[i]->AggGeom;
|
|
|
|
for (int32 j = 0; j <AggGeom->SphereElems.Num(); ++j)
|
|
{
|
|
FPhATSharedData::FSelection Selection(i, KPT_Sphere, j);
|
|
SharedData->SetSelectedBody(&Selection, true);
|
|
}
|
|
|
|
for (int32 j = 0; j <AggGeom->BoxElems.Num(); ++j)
|
|
{
|
|
FPhATSharedData::FSelection Selection(i, KPT_Box, j);
|
|
SharedData->SetSelectedBody(&Selection, true);
|
|
}
|
|
|
|
for (int32 j = 0; j <AggGeom->SphylElems.Num(); ++j)
|
|
{
|
|
FPhATSharedData::FSelection Selection(i, KPT_Sphyl, j);
|
|
SharedData->SetSelectedBody(&Selection, true);
|
|
}
|
|
|
|
for (int32 j = 0; j <AggGeom->ConvexElems.Num(); ++j)
|
|
{
|
|
FPhATSharedData::FSelection Selection(i, KPT_Convex, j);
|
|
SharedData->SetSelectedBody(&Selection, true);
|
|
}
|
|
|
|
}
|
|
}
|
|
}else
|
|
{
|
|
//Constraints
|
|
//Deselect everything first
|
|
SharedData->SetSelectedConstraint(INDEX_NONE);
|
|
|
|
//go through every constraint and add it
|
|
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)
|
|
{
|
|
SharedData->SetSelectedConstraint(i, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 FPhAT::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"), IsSimulationMode(EPSM_Gravity)? TEXT("ON") : TEXT("OFF")));
|
|
}
|
|
}
|
|
|
|
if (bRecordMode)
|
|
{
|
|
Attribs.Add(FAnalyticsEventAttribute(TEXT("EditMode"), SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit? TEXT("Constraint") : TEXT("Body")));
|
|
}
|
|
|
|
FString EventString = FString::Printf(TEXT("Editor.Usage.PHAT.%s"), *Action);
|
|
FEngineAnalytics::GetProvider().RecordEvent(EventString, Attribs);
|
|
}
|
|
}
|
|
|
|
// only during simulation
|
|
bool FPhAT::IsRecordAvailable() const
|
|
{
|
|
// make sure mesh exists
|
|
return (SharedData->EditorSkelComp && SharedData->EditorSkelComp->SkeletalMesh/* && SharedData->bRunningSimulation */);
|
|
}
|
|
|
|
FSlateIcon FPhAT::GetRecordStatusImage() const
|
|
{
|
|
if(SharedData->Recorder.InRecording())
|
|
{
|
|
return FSlateIcon(FEditorStyle::GetStyleSetName(), "Persona.StopRecordAnimation");
|
|
}
|
|
|
|
return FSlateIcon(FEditorStyle::GetStyleSetName(), "Persona.StartRecordAnimation");
|
|
}
|
|
|
|
FText FPhAT::GetRecordMenuLabel() const
|
|
{
|
|
if(SharedData->Recorder.InRecording())
|
|
{
|
|
return LOCTEXT("Persona_StopRecordAnimationMenuLabel", "Stop Record Animation");
|
|
}
|
|
|
|
return LOCTEXT("Persona_StartRecordAnimationLabel", "Start Record Animation");
|
|
}
|
|
|
|
FText FPhAT::GetRecordStatusLabel() const
|
|
{
|
|
if(SharedData->Recorder.InRecording())
|
|
{
|
|
return LOCTEXT("Persona_StopRecordAnimationLabel", "Stop");
|
|
}
|
|
|
|
return LOCTEXT("Persona_StartRecordAnimationLabel", "Record");
|
|
}
|
|
|
|
FText FPhAT::GetRecordStatusTooltip() const
|
|
{
|
|
if(SharedData->Recorder.InRecording())
|
|
{
|
|
return LOCTEXT("Persona_StopRecordAnimation", "Stop Record Animation");
|
|
}
|
|
|
|
return LOCTEXT("Persona_StartRecordAnimation", "Start Record Animation");
|
|
}
|
|
|
|
void FPhAT::RecordAnimation()
|
|
{
|
|
if(!SharedData->EditorSkelComp || !SharedData->EditorSkelComp->SkeletalMesh)
|
|
{
|
|
// error
|
|
return;
|
|
}
|
|
|
|
if(SharedData->Recorder.InRecording())
|
|
{
|
|
UAnimSequence * AnimSeq = SharedData->Recorder.StopRecord(true);
|
|
|
|
// open the asset?
|
|
if (AnimSeq)
|
|
{
|
|
TArray<UObject*> ObjectsToSync;
|
|
ObjectsToSync.Add(AnimSeq);
|
|
|
|
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
|
|
ContentBrowserModule.Get().SyncBrowserToAssets(ObjectsToSync, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SharedData->Recorder.TriggerRecordAnimation(SharedData->EditorSkelComp);
|
|
}
|
|
}
|
|
|
|
void FPhAT::Tick(float DeltaTime)
|
|
{
|
|
}
|
|
|
|
TStatId FPhAT::GetStatId() const
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(FPhAT, STATGROUP_Tickables);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|