1231 lines
45 KiB
C++
1231 lines
45 KiB
C++
/*
|
|
* Copyright (c) <2017> Side Effects Software Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "SHoudiniToolPalette.h"
|
|
|
|
#include "HoudiniApi.h"
|
|
#include "Editor.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "Widgets/SBoxPanel.h"
|
|
#include "SlateOptMacros.h"
|
|
#include "Styling/CoreStyle.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
#include "Styling/SlateTypes.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Widgets/Layout/SScrollBorder.h"
|
|
#include "Widgets/Layout/SBorder.h"
|
|
#include "Widgets/Views/STableViewBase.h"
|
|
#include "Widgets/Views/STableRow.h"
|
|
#include "Widgets/Views/SListView.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Input/SComboBox.h"
|
|
#include "EditorStyleSet.h"
|
|
#include "HoudiniAsset.h"
|
|
#include "HoudiniAssetActor.h"
|
|
#include "HoudiniAssetComponent.h"
|
|
#include "AssetRegistryModule.h"
|
|
#include "DragAndDrop/AssetDragDropOp.h"
|
|
#include "ActorFactories/ActorFactory.h"
|
|
#include "Engine/Selection.h"
|
|
#include "ContentBrowserModule.h"
|
|
#include "IContentBrowserSingleton.h"
|
|
#include "EditorViewportClient.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "AssetToolsModule.h"
|
|
#include "EditorFramework/AssetImportData.h"
|
|
#include "Toolkits/GlobalEditorCommonCommands.h"
|
|
#include "Landscape.h"
|
|
#include "LandscapeProxy.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "HAL/PlatformFilemanager.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "PropertyEditorModule.h"
|
|
#include "Interfaces/IMainFrameModule.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "HoudiniToolPalette"
|
|
|
|
|
|
UHoudiniToolProperties::UHoudiniToolProperties(const FObjectInitializer & ObjectInitializer)
|
|
:Super( ObjectInitializer )
|
|
, Name()
|
|
, Type(EHoudiniToolType::HTOOLTYPE_OPERATOR_SINGLE)
|
|
, SelectionType(EHoudiniToolSelectionType::HTOOL_SELECTION_ALL)
|
|
, ToolTip()
|
|
, IconPath(FFilePath())
|
|
, AssetPath(FFilePath())
|
|
, HelpURL()
|
|
{
|
|
};
|
|
|
|
UHoudiniToolDirectoryProperties::UHoudiniToolDirectoryProperties(const FObjectInitializer & ObjectInitializer)
|
|
:Super(ObjectInitializer)
|
|
, CustomHoudiniToolsDirectories()
|
|
{
|
|
};
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
void
|
|
SHoudiniToolPalette::Construct( const FArguments& InArgs )
|
|
{
|
|
FHoudiniEngineEditor& HoudiniEngineEditor = FModuleManager::GetModuleChecked<FHoudiniEngineEditor>("HoudiniEngineEditor");
|
|
|
|
UpdateHoudiniToolDirectories();
|
|
|
|
SAssignNew( HoudiniToolListView, SHoudiniToolListView )
|
|
.SelectionMode( ESelectionMode::Single )
|
|
.ListItemsSource( &HoudiniEngineEditor.GetHoudiniTools() )
|
|
.OnGenerateRow( this, &SHoudiniToolPalette::MakeListViewWidget )
|
|
.OnSelectionChanged( this, &SHoudiniToolPalette::OnSelectionChanged )
|
|
.OnMouseButtonDoubleClick( this, &SHoudiniToolPalette::OnDoubleClickedListViewWidget )
|
|
.OnContextMenuOpening( this, &SHoudiniToolPalette::ConstructHoudiniToolContextMenu )
|
|
.ItemHeight( 35 );
|
|
|
|
int32 ToolDirComboSelectedIdx = HoudiniEngineEditor.CurrentHoudiniToolDirIndex + 1;
|
|
if (!HoudiniToolDirArray.IsValidIndex(ToolDirComboSelectedIdx))
|
|
ToolDirComboSelectedIdx = 0;
|
|
|
|
TSharedRef<SComboBox< TSharedPtr< FString > > > HoudiniToolDirComboBox =
|
|
SNew(SComboBox< TSharedPtr< FString > >)
|
|
.OptionsSource( &HoudiniToolDirArray)
|
|
.InitiallySelectedItem(HoudiniToolDirArray[ ToolDirComboSelectedIdx ])
|
|
.OnGenerateWidget( SComboBox< TSharedPtr< FString > >::FOnGenerateWidget::CreateLambda(
|
|
[]( TSharedPtr< FString > ChoiceEntry )
|
|
{
|
|
FText ChoiceEntryText = FText::FromString( *ChoiceEntry );
|
|
return SNew( STextBlock )
|
|
.Text( ChoiceEntryText )
|
|
.ToolTipText( ChoiceEntryText )
|
|
.Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) );
|
|
} ) )
|
|
.OnSelectionChanged( SComboBox< TSharedPtr< FString > >::FOnSelectionChanged::CreateLambda(
|
|
[=]( TSharedPtr< FString > NewChoice, ESelectInfo::Type SelectType )
|
|
{
|
|
if ( !NewChoice.IsValid() )
|
|
return;
|
|
OnDirectoryChange(*(NewChoice.Get()) );
|
|
} ) )
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SHoudiniToolPalette::OnGetSelectedDirText )
|
|
.ToolTipText( this, &SHoudiniToolPalette::OnGetSelectedDirText )
|
|
.Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont")))
|
|
];
|
|
TSharedPtr< SButton > EditButton;
|
|
FText EditButtonText = FText::FromString(TEXT("Edit"));
|
|
FText EditButtonTooltip = FText::FromString(TEXT("Add, Remove or Edit custom Houdini tool directories"));
|
|
|
|
FText ActiveShelfText = FText::FromString(TEXT("Active Shelf:"));
|
|
FText ActiveShelfTooltip = FText::FromString(TEXT("Name of the currently selected Houdini Tool Shelf"));
|
|
|
|
ChildSlot
|
|
[
|
|
SNew(SVerticalBox)
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
SNew(SScrollBorder, HoudiniToolListView.ToSharedRef())
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(ActiveShelfText)
|
|
.ToolTipText(ActiveShelfTooltip)
|
|
.Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.BoldFont")))
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
[
|
|
HoudiniToolDirComboBox
|
|
]
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
[
|
|
SAssignNew(EditButton, SButton)
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
.Text(EditButtonText)
|
|
.ToolTipText(EditButtonTooltip)
|
|
.OnClicked(FOnClicked::CreateSP(this, &SHoudiniToolPalette::OnEditToolDirectories))
|
|
]
|
|
]
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
.FillHeight(1.0f)
|
|
[
|
|
SNew(SScrollBorder, HoudiniToolListView.ToSharedRef())
|
|
[
|
|
HoudiniToolListView.ToSharedRef()
|
|
]
|
|
]
|
|
];
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
TSharedRef< ITableRow >
|
|
SHoudiniToolPalette::MakeListViewWidget( TSharedPtr< FHoudiniTool > HoudiniTool, const TSharedRef< STableViewBase >& OwnerTable )
|
|
{
|
|
check( HoudiniTool.IsValid() );
|
|
|
|
auto HelpDefault = FEditorStyle::GetBrush( "HelpIcon" );
|
|
auto HelpHovered = FEditorStyle::GetBrush( "HelpIcon.Hovered" );
|
|
auto HelpPressed = FEditorStyle::GetBrush( "HelpIcon.Pressed" );
|
|
auto DefaultTool = FHoudiniEngineStyle::Get()->GetBrush( "HoudiniEngine.HoudiniEngineLogo");
|
|
TSharedPtr< SImage > HelpButtonImage;
|
|
TSharedPtr< SButton > HelpButton;
|
|
TSharedPtr< SImage > DefaultToolImage;
|
|
|
|
// Building the tool's tooltip
|
|
FString HoudiniToolTip = HoudiniTool->Name.ToString() + TEXT( "\n" );
|
|
if ( HoudiniTool->HoudiniAsset.IsValid() )
|
|
HoudiniToolTip += HoudiniTool->HoudiniAsset.ToSoftObjectPath().ToString() /*->AssetFileName */+ TEXT( "\n\n" );
|
|
if ( !HoudiniTool->ToolTipText.IsEmpty() )
|
|
HoudiniToolTip += HoudiniTool->ToolTipText.ToString() + TEXT("\n\n");
|
|
|
|
// Adding a description of the tools behavior deriving from its type
|
|
switch ( HoudiniTool->Type )
|
|
{
|
|
case EHoudiniToolType::HTOOLTYPE_OPERATOR_SINGLE:
|
|
HoudiniToolTip += TEXT("Operator (Single):\nDouble clicking on this tool will instantiate the asset in the world.\nThe current selection will be automatically assigned to the asset's first input.\n" );
|
|
break;
|
|
case EHoudiniToolType::HTOOLTYPE_OPERATOR_MULTI:
|
|
HoudiniToolTip += TEXT("Operator (Multiple):\nDouble clicking on this tool will instantiate the asset in the world.\nEach of the selected object will be assigned to one of the asset's input (object1 to input1, object2 to input2 etc.)\n" );
|
|
break;
|
|
case EHoudiniToolType::HTOOLTYPE_OPERATOR_BATCH:
|
|
HoudiniToolTip += TEXT("Operator (Batch):\nDouble clicking on this tool will instantiate the asset in the world.\nAn instance of the asset will be created for each of the selected object, and the asset's first input will be set to that object.\n");
|
|
break;
|
|
case EHoudiniToolType::HTOOLTYPE_GENERATOR:
|
|
default:
|
|
HoudiniToolTip += TEXT("Generator:\nDouble clicking on this tool will instantiate the asset in the world.\n");
|
|
break;
|
|
}
|
|
|
|
// Add a description from the tools selection type
|
|
if ( HoudiniTool->Type != EHoudiniToolType::HTOOLTYPE_GENERATOR )
|
|
{
|
|
switch ( HoudiniTool->SelectionType )
|
|
{
|
|
case EHoudiniToolSelectionType::HTOOL_SELECTION_ALL:
|
|
HoudiniToolTip += TEXT("\nBoth Content Browser and World Outliner selection will be considered.\nIf objects are selected in the content browser, the world selection will be ignored.\n");
|
|
break;
|
|
|
|
case EHoudiniToolSelectionType::HTOOL_SELECTION_WORLD_ONLY:
|
|
HoudiniToolTip += TEXT("\nOnly World Outliner selection will be considered.\n");
|
|
break;
|
|
|
|
case EHoudiniToolSelectionType::HTOOL_SELECTION_CB_ONLY:
|
|
HoudiniToolTip += TEXT("\nOnly Content Browser selection will be considered.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( HoudiniTool->Type != EHoudiniToolType::HTOOLTYPE_OPERATOR_BATCH )
|
|
{
|
|
if ( HoudiniTool->SelectionType != EHoudiniToolSelectionType::HTOOL_SELECTION_CB_ONLY )
|
|
HoudiniToolTip += TEXT( "If objects are selected in the world outliner, the asset's transform will default to the mean Transform of the select objects.\n" );
|
|
}
|
|
else
|
|
{
|
|
if ( HoudiniTool->SelectionType != EHoudiniToolSelectionType::HTOOL_SELECTION_CB_ONLY )
|
|
HoudiniToolTip += TEXT( "If objects are selected in the world outliner, each created asset will use its object transform.\n" );
|
|
}
|
|
|
|
FText ToolTipText = FText::FromString( HoudiniToolTip );
|
|
|
|
// Build the Help Tooltip
|
|
FString HelpURL = HoudiniTool->HelpURL;
|
|
bool HasHelp = HelpURL.Len() > 0;
|
|
FText HelpTip = HasHelp ? FText::Format( LOCTEXT( "OpenHelp", "Click to view tool help: {0}" ), FText::FromString( HelpURL ) ) : HoudiniTool->Name;
|
|
|
|
// Build the "DefaultTool" tool tip
|
|
bool IsDefault = HoudiniTool->DefaultTool;
|
|
FText DefaultToolText = FText::FromString( TEXT("Default Houdini Engine for Unreal plug-in tool") );
|
|
|
|
TSharedRef< STableRow<TSharedPtr<FHoudiniTool>> > TableRowWidget =
|
|
SNew( STableRow<TSharedPtr<FHoudiniTool>>, OwnerTable )
|
|
.Style( FHoudiniEngineStyle::Get(), "HoudiniEngine.TableRow" )
|
|
.OnDragDetected( this, &SHoudiniToolPalette::OnDraggingListViewWidget );
|
|
|
|
TSharedPtr< SHorizontalBox > ContentBox;
|
|
TSharedRef<SWidget> Content =
|
|
SNew( SBorder )
|
|
.BorderImage( FCoreStyle::Get().GetBrush( "NoBorder" ) )
|
|
.Padding( 0 )
|
|
.ToolTip( SNew( SToolTip ).Text( ToolTipText ) )
|
|
.Cursor( EMouseCursor::GrabHand )
|
|
[ SAssignNew( ContentBox, SHorizontalBox ) ];
|
|
|
|
// Tool Icon
|
|
ContentBox->AddSlot()
|
|
.AutoWidth()
|
|
[
|
|
SNew( SBorder )
|
|
.Padding( 4.0f )
|
|
.BorderImage( FHoudiniEngineStyle::Get()->GetBrush( "HoudiniEngine.ThumbnailShadow" ) )
|
|
[
|
|
SNew( SBox )
|
|
.WidthOverride( 35.0f )
|
|
.HeightOverride( 35.0f )
|
|
[
|
|
SNew( SBorder )
|
|
.BorderImage( FHoudiniEngineStyle::Get()->GetBrush( "HoudiniEngine.ThumbnailBackground" ) )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
[
|
|
SNew( SImage )
|
|
.Image( HoudiniTool->Icon )
|
|
.ToolTip( SNew( SToolTip ).Text( ToolTipText ) )
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
// Tool name + tooltip
|
|
ContentBox->AddSlot()
|
|
.HAlign( HAlign_Left )
|
|
.VAlign( VAlign_Center )
|
|
.FillWidth( 1.f )
|
|
[
|
|
SNew( STextBlock )
|
|
.TextStyle( FHoudiniEngineStyle::Get(), "HoudiniEngine.ThumbnailText" )
|
|
.Text( HoudiniTool->Name )
|
|
.ToolTip( SNew( SToolTip ).Text( ToolTipText ) )
|
|
];
|
|
|
|
// Default Tool Icon
|
|
if ( IsDefault )
|
|
{
|
|
ContentBox->AddSlot()
|
|
.VAlign( VAlign_Center )
|
|
.AutoWidth()
|
|
[
|
|
SAssignNew( DefaultToolImage, SImage )
|
|
.ToolTip( SNew( SToolTip ).Text( DefaultToolText ) )
|
|
];
|
|
|
|
// Houdini logo for the Default Tool
|
|
DefaultToolImage->SetImage( DefaultTool );
|
|
}
|
|
|
|
if ( HasHelp )
|
|
{
|
|
ContentBox->AddSlot()
|
|
.VAlign( VAlign_Center )
|
|
.AutoWidth()
|
|
[
|
|
SAssignNew( HelpButton, SButton )
|
|
.ContentPadding( 0 )
|
|
.ButtonStyle( FEditorStyle::Get(), "HelpButton" )
|
|
.OnClicked( FOnClicked::CreateLambda( [ HelpURL ]() {
|
|
if ( HelpURL.Len() )
|
|
FPlatformProcess::LaunchURL( *HelpURL, nullptr, nullptr );
|
|
return FReply::Handled();
|
|
} ) )
|
|
.HAlign( HAlign_Center )
|
|
.VAlign( VAlign_Center )
|
|
.ToolTip( SNew( SToolTip ).Text( HelpTip ) )
|
|
[
|
|
SAssignNew( HelpButtonImage, SImage )
|
|
.ToolTip( SNew( SToolTip ).Text( HelpTip ) )
|
|
]
|
|
];
|
|
|
|
HelpButtonImage->SetImage( TAttribute<const FSlateBrush *>::Create( TAttribute<const FSlateBrush *>::FGetter::CreateLambda( [=]()
|
|
{
|
|
if ( HelpButton->IsPressed() )
|
|
{
|
|
return HelpPressed;
|
|
}
|
|
|
|
if ( HelpButtonImage->IsHovered() )
|
|
{
|
|
return HelpHovered;
|
|
}
|
|
|
|
return HelpDefault;
|
|
} ) ) );
|
|
}
|
|
|
|
TableRowWidget->SetContent( Content );
|
|
|
|
return TableRowWidget;
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::OnSelectionChanged( TSharedPtr<FHoudiniTool> ToolType, ESelectInfo::Type SelectionType )
|
|
{
|
|
if ( ToolType.IsValid() )
|
|
{
|
|
ActiveTool = ToolType;
|
|
}
|
|
else
|
|
{
|
|
ActiveTool = NULL;
|
|
}
|
|
}
|
|
|
|
FReply
|
|
SHoudiniToolPalette::OnDraggingListViewWidget( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
|
|
{
|
|
if ( MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton ) )
|
|
{
|
|
if ( ActiveTool.IsValid() )
|
|
{
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>( "AssetRegistry" );
|
|
UObject* AssetObj = ActiveTool->HoudiniAsset.LoadSynchronous();
|
|
if ( AssetObj )
|
|
{
|
|
FAssetData BackingAssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*AssetObj->GetPathName());
|
|
|
|
return FReply::Handled().BeginDragDrop( FAssetDragDropOp::New( BackingAssetData, GEditor->FindActorFactoryForActorClass( AHoudiniAssetActor::StaticClass() ) ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::OnDoubleClickedListViewWidget( TSharedPtr<FHoudiniTool> HoudiniTool )
|
|
{
|
|
if ( !HoudiniTool.IsValid() )
|
|
return;
|
|
|
|
return InstantiateHoudiniTool( HoudiniTool.Get() );
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::InstantiateHoudiniTool( FHoudiniTool* HoudiniTool )
|
|
{
|
|
if ( !HoudiniTool )
|
|
return;
|
|
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>( "AssetRegistry" );
|
|
|
|
// Load the asset
|
|
UObject* AssetObj = HoudiniTool->HoudiniAsset.LoadSynchronous();
|
|
if ( !AssetObj )
|
|
return;
|
|
|
|
// Get the asset Factory
|
|
UActorFactory* Factory = GEditor->FindActorFactoryForActorClass( AHoudiniAssetActor::StaticClass() );
|
|
if ( !Factory )
|
|
return;
|
|
|
|
// Get the current Level Editor Selection
|
|
TArray<UObject * > WorldSelection;
|
|
int32 WorldSelectionCount = FHoudiniEngineEditor::GetWorldSelection( WorldSelection );
|
|
|
|
// Get the current Content browser selection
|
|
TArray<UObject *> ContentBrowserSelection;
|
|
int32 ContentBrowserSelectionCount = FHoudiniEngineEditor::GetContentBrowserSelection( ContentBrowserSelection );
|
|
|
|
// By default, Content browser selection has a priority over the world selection
|
|
bool UseCBSelection = ContentBrowserSelectionCount > 0;
|
|
if ( HoudiniTool->SelectionType == EHoudiniToolSelectionType::HTOOL_SELECTION_CB_ONLY )
|
|
UseCBSelection = true;
|
|
else if ( HoudiniTool->SelectionType == EHoudiniToolSelectionType::HTOOL_SELECTION_WORLD_ONLY )
|
|
UseCBSelection = false;
|
|
|
|
// Modify the created actor's position from the current editor world selection
|
|
FTransform SpawnTransform = GetDefaulToolSpawnTransform();
|
|
if ( WorldSelectionCount > 0 )
|
|
{
|
|
// Get the "mean" transform of all the selected actors
|
|
SpawnTransform = GetMeanWorldSelectionTransform();
|
|
}
|
|
|
|
// If the current tool is a batch one, we'll need to create multiple instances of the HDA
|
|
if ( HoudiniTool->Type == EHoudiniToolType::HTOOLTYPE_OPERATOR_BATCH )
|
|
{
|
|
// Unselect the current selection to select the created actor after
|
|
if ( GEditor )
|
|
GEditor->SelectNone( true, true, false );
|
|
|
|
// An instance of the asset will be created for each selected object
|
|
for( int32 SelecIndex = 0; SelecIndex < ( UseCBSelection ? ContentBrowserSelectionCount : WorldSelectionCount ); SelecIndex++ )
|
|
{
|
|
// Get the current object
|
|
UObject* CurrentSelectedObject = nullptr;
|
|
if ( UseCBSelection && ContentBrowserSelection.IsValidIndex( SelecIndex ) )
|
|
CurrentSelectedObject = ContentBrowserSelection[ SelecIndex ];
|
|
|
|
if ( !UseCBSelection && WorldSelection.IsValidIndex( SelecIndex ) )
|
|
CurrentSelectedObject = WorldSelection[ SelecIndex ];
|
|
|
|
if ( !CurrentSelectedObject )
|
|
continue;
|
|
|
|
// If it's an actor, use its Transform to spawn the HDA
|
|
AActor* CurrentSelectedActor = Cast<AActor>( CurrentSelectedObject );
|
|
if ( CurrentSelectedActor )
|
|
SpawnTransform = CurrentSelectedActor->GetTransform();
|
|
else
|
|
SpawnTransform = GetDefaulToolSpawnTransform();
|
|
|
|
// Create the actor for the HDA
|
|
AActor* CreatedActor = Factory->CreateActor( AssetObj, GEditor->GetEditorWorldContext().World()->GetCurrentLevel(), SpawnTransform );
|
|
if ( !CreatedActor )
|
|
continue;
|
|
|
|
// Get the HoudiniAssetActor / HoudiniAssetComponent we just created
|
|
AHoudiniAssetActor* HoudiniAssetActor = ( AHoudiniAssetActor* )CreatedActor;
|
|
if ( !HoudiniAssetActor )
|
|
continue;
|
|
|
|
UHoudiniAssetComponent* HoudiniAssetComponent = HoudiniAssetActor->GetHoudiniAssetComponent();
|
|
if ( !HoudiniAssetComponent )
|
|
continue;
|
|
|
|
// Create and set the input preset for this HDA and selected Object
|
|
TMap< UObject*, int32 > InputPreset;
|
|
InputPreset.Add( CurrentSelectedObject, 0 );
|
|
HoudiniAssetComponent->SetHoudiniToolInputPresets( InputPreset );
|
|
|
|
// Select the Actor we just created
|
|
if ( GEditor && GEditor->CanSelectActor( CreatedActor, true, false ) )
|
|
GEditor->SelectActor( CreatedActor, true, true, true );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We only need to create a single instance of the asset, regarding of the selection
|
|
AActor* CreatedActor = Factory->CreateActor( AssetObj, GEditor->GetEditorWorldContext().World()->GetCurrentLevel(), SpawnTransform );
|
|
if ( !CreatedActor )
|
|
return;
|
|
|
|
// Generator tools don't need to preset their input
|
|
if ( HoudiniTool->Type != EHoudiniToolType::HTOOLTYPE_GENERATOR )
|
|
{
|
|
TMap<UObject*, int32> InputPresets;
|
|
AHoudiniAssetActor* HoudiniAssetActor = (AHoudiniAssetActor*)CreatedActor;
|
|
UHoudiniAssetComponent* HoudiniAssetComponent = HoudiniAssetActor ? HoudiniAssetActor->GetHoudiniAssetComponent() : nullptr;
|
|
if ( HoudiniAssetComponent )
|
|
{
|
|
// Build the preset map
|
|
int InputIndex = 0;
|
|
for ( auto CurrentObject : ( UseCBSelection ? ContentBrowserSelection : WorldSelection ) )
|
|
{
|
|
if ( !CurrentObject )
|
|
continue;
|
|
|
|
if ( HoudiniTool->Type == EHoudiniToolType::HTOOLTYPE_OPERATOR_MULTI )
|
|
{
|
|
// The selection will be applied individually to multiple inputs
|
|
// (first object to first input, second object to second input etc...)
|
|
InputPresets.Add( CurrentObject, InputIndex++ );
|
|
}
|
|
else
|
|
{
|
|
// All the selection will be applied to the asset's first input
|
|
InputPresets.Add( CurrentObject, 0 );
|
|
}
|
|
}
|
|
|
|
// Set the input preset on the HoudiniAssetComponent
|
|
if ( InputPresets.Num() > 0 )
|
|
HoudiniAssetComponent->SetHoudiniToolInputPresets( InputPresets );
|
|
}
|
|
}
|
|
|
|
// Select the Actor we just created
|
|
if ( GEditor->CanSelectActor( CreatedActor, true, true ) )
|
|
{
|
|
GEditor->SelectNone( true, true, false );
|
|
GEditor->SelectActor( CreatedActor, true, true, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
FTransform
|
|
SHoudiniToolPalette::GetDefaulToolSpawnTransform()
|
|
{
|
|
FTransform SpawnTransform = FTransform::Identity;
|
|
|
|
// Get the editor viewport LookAt position to spawn the new objects
|
|
if ( GEditor && GEditor->GetActiveViewport() )
|
|
{
|
|
FEditorViewportClient* ViewportClient = (FEditorViewportClient*)GEditor->GetActiveViewport()->GetClient();
|
|
if ( ViewportClient )
|
|
{
|
|
// We need to toggle the orbit camera on to get the proper LookAtLocation to spawn our asset
|
|
ViewportClient->ToggleOrbitCamera( true );
|
|
SpawnTransform.SetLocation( ViewportClient->GetLookAtLocation() );
|
|
ViewportClient->ToggleOrbitCamera( false );
|
|
}
|
|
}
|
|
|
|
return SpawnTransform;
|
|
}
|
|
|
|
FTransform
|
|
SHoudiniToolPalette::GetMeanWorldSelectionTransform()
|
|
{
|
|
FTransform SpawnTransform = GetDefaulToolSpawnTransform();
|
|
|
|
if ( GEditor && ( GEditor->GetSelectedActorCount() > 0 ) )
|
|
{
|
|
// Get the current Level Editor Selection
|
|
USelection* SelectedActors = GEditor->GetSelectedActors();
|
|
|
|
int NumAppliedTransform = 0;
|
|
for ( FSelectionIterator It( *SelectedActors ); It; ++It )
|
|
{
|
|
AActor * Actor = Cast< AActor >( *It );
|
|
if ( !Actor )
|
|
continue;
|
|
|
|
// Just Ignore the SkySphere...
|
|
FString ClassName = Actor->GetClass() ? Actor->GetClass()->GetName() : FString();
|
|
if ( ClassName == TEXT( "BP_Sky_Sphere_C" ) )
|
|
continue;
|
|
|
|
FTransform CurrentTransform = Actor->GetTransform();
|
|
|
|
ALandscapeProxy* Landscape = Cast< ALandscapeProxy >( Actor );
|
|
if ( Landscape )
|
|
{
|
|
// We need to offset Landscape's transform in X/Y to center them properly
|
|
FVector Origin, Extent;
|
|
Actor->GetActorBounds(false, Origin, Extent);
|
|
|
|
// Use the origin's XY Position
|
|
FVector Location = CurrentTransform.GetLocation();
|
|
Location.X = Origin.X;
|
|
Location.Y = Origin.Y;
|
|
CurrentTransform.SetLocation( Location );
|
|
}
|
|
|
|
// Accumulate all the actor transforms...
|
|
if ( NumAppliedTransform == 0 )
|
|
SpawnTransform = CurrentTransform;
|
|
else
|
|
SpawnTransform.Accumulate( CurrentTransform );
|
|
|
|
NumAppliedTransform++;
|
|
}
|
|
|
|
if ( NumAppliedTransform > 0 )
|
|
{
|
|
// "Mean" all the accumulated Transform
|
|
SpawnTransform.SetScale3D( FVector::OneVector );
|
|
SpawnTransform.NormalizeRotation();
|
|
|
|
if ( NumAppliedTransform > 1 )
|
|
SpawnTransform.SetLocation( SpawnTransform.GetLocation() / (float)NumAppliedTransform );
|
|
}
|
|
}
|
|
|
|
return SpawnTransform;
|
|
}
|
|
|
|
// Builds the context menu for the selected tool
|
|
TSharedPtr<SWidget>
|
|
SHoudiniToolPalette::ConstructHoudiniToolContextMenu()
|
|
{
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked< FAssetRegistryModule >( "AssetRegistry" );
|
|
|
|
FMenuBuilder MenuBuilder( true, NULL );
|
|
|
|
TArray< UObject* > AssetArray;
|
|
if ( ActiveTool.IsValid() && !ActiveTool->HoudiniAsset.IsNull() )
|
|
{
|
|
// Load the asset
|
|
UObject* AssetObj = ActiveTool->HoudiniAsset.LoadSynchronous();
|
|
if( AssetObj )
|
|
AssetArray.Add( AssetObj );
|
|
}
|
|
|
|
//MenuBuilder.AddMenuEntry(FGlobalEditorCommonCommands::Get().FindInContentBrowser );
|
|
|
|
FAssetToolsModule & AssetToolsModule = FModuleManager::GetModuleChecked< FAssetToolsModule >( "AssetTools" );
|
|
TSharedPtr< IAssetTypeActions > EngineActions = AssetToolsModule.Get().GetAssetTypeActionsForClass( UHoudiniAsset::StaticClass() ).Pin();
|
|
if ( EngineActions.IsValid() )
|
|
EngineActions->GetActions( AssetArray, MenuBuilder );
|
|
|
|
// Add HoudiniTools actions
|
|
MenuBuilder.AddMenuSeparator();
|
|
|
|
// Remove Tool
|
|
MenuBuilder.AddMenuEntry(
|
|
NSLOCTEXT( "HoudiniToolsTypeActions", "HoudiniTool_Remove", "Remove Tool" ),
|
|
NSLOCTEXT( "HoudiniToolsTypeActions", "HoudiniTool_RemoveTooltip", "Remove the selected tool from the list of Houdini Tools." ),
|
|
FSlateIcon( FHoudiniEngineStyle::GetStyleSetName(), "HoudiniEngine.HoudiniEngineLogo" ),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &SHoudiniToolPalette::RemoveActiveTool ),
|
|
FCanExecuteAction::CreateLambda([&] { return IsActiveHoudiniToolEditable(); } )
|
|
)
|
|
);
|
|
|
|
// Edit Tool
|
|
MenuBuilder.AddMenuEntry(
|
|
NSLOCTEXT( "HoudiniToolsTypeActions", "HoudiniTool_Edit", "Edit Tool Properties" ),
|
|
NSLOCTEXT( "HoudiniToolsTypeActions", "HoudiniTool_EditTooltip", "Edit the selected tool properties." ),
|
|
FSlateIcon( FHoudiniEngineStyle::GetStyleSetName(), "HoudiniEngine.HoudiniEngineLogo" ),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &SHoudiniToolPalette::EditActiveHoudiniTool ),
|
|
FCanExecuteAction::CreateLambda([&] { return IsActiveHoudiniToolEditable(); } )
|
|
)
|
|
);
|
|
|
|
// Add HoudiniTools actions
|
|
MenuBuilder.AddMenuSeparator();
|
|
|
|
// Generate Missing Tool Descriptions
|
|
MenuBuilder.AddMenuEntry(
|
|
NSLOCTEXT("HoudiniToolsTypeActions", "HoudiniTool_GenMissing", "Generate Missing Tool Descriptions"),
|
|
NSLOCTEXT("HoudiniToolsTypeActions", "HoudiniTool_GenMissingTooltip", "Generates the tool descriptions .json file for HDA that doesnt have one."),
|
|
FSlateIcon(FHoudiniEngineStyle::GetStyleSetName(), "HoudiniEngine.HoudiniEngineLogo"),
|
|
FUIAction(
|
|
FExecuteAction::CreateSP(this, &SHoudiniToolPalette::GenerateMissingJSONFiles ),
|
|
FCanExecuteAction::CreateLambda([&] { return true; })
|
|
)
|
|
);
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::EditActiveHoudiniTool()
|
|
{
|
|
if ( !ActiveTool.IsValid() )
|
|
return;
|
|
|
|
if ( !IsActiveHoudiniToolEditable() )
|
|
return;
|
|
|
|
// Create a new Tool property object for the property dialog
|
|
FString ToolName = ActiveTool->Name.ToString();
|
|
if ( ActiveTool->HoudiniAsset )
|
|
ToolName += TEXT(" (") + ActiveTool->HoudiniAsset->AssetFileName + TEXT(")");
|
|
|
|
UHoudiniToolProperties* NewToolProperty = NewObject< UHoudiniToolProperties >( GetTransientPackage(), FName( *ToolName ) );
|
|
NewToolProperty->AddToRoot();
|
|
|
|
// Set the default values for this asset
|
|
NewToolProperty->Name = ActiveTool->Name.ToString();
|
|
NewToolProperty->Type = ActiveTool->Type;
|
|
NewToolProperty->ToolTip = ActiveTool->ToolTipText.ToString();
|
|
FName IconResourceName = ActiveTool->Icon->GetResourceName();
|
|
NewToolProperty->IconPath.FilePath = IconResourceName.ToString();
|
|
NewToolProperty->HelpURL = ActiveTool->HelpURL;
|
|
NewToolProperty->AssetPath = ActiveTool->SourceAssetPath;
|
|
NewToolProperty->SelectionType = ActiveTool->SelectionType;
|
|
|
|
TArray<UObject *> ActiveHoudiniTools;
|
|
ActiveHoudiniTools.Add( NewToolProperty );
|
|
|
|
// Create a new property editor window
|
|
TSharedRef< SWindow > Window = CreateFloatingDetailsView( ActiveHoudiniTools );
|
|
Window->SetOnWindowClosed( FOnWindowClosed::CreateSP( this, &SHoudiniToolPalette::OnEditActiveHoudiniToolWindowClosed, ActiveHoudiniTools ) );
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::OnEditActiveHoudiniToolWindowClosed( const TSharedRef<SWindow>& InWindow, TArray<UObject *> InObjects )
|
|
{
|
|
// Sanity check, we can only edit one tool at a time!
|
|
if ( InObjects.Num() != 1 )
|
|
return;
|
|
|
|
TArray< FHoudiniTool > EditedToolArray;
|
|
for ( int32 ObjIdx = 0; ObjIdx < InObjects.Num(); ObjIdx++ )
|
|
{
|
|
UHoudiniToolProperties* ToolProperties = Cast< UHoudiniToolProperties >( InObjects[ ObjIdx ] );
|
|
if ( !ToolProperties )
|
|
continue;
|
|
|
|
FString IconPath = FPaths::ConvertRelativePathToFull( ToolProperties->IconPath.FilePath );
|
|
const FSlateBrush* CustomIconBrush = nullptr;
|
|
if ( FPaths::FileExists( IconPath ) )
|
|
{
|
|
FName BrushName = *IconPath;
|
|
CustomIconBrush = new FSlateDynamicImageBrush( BrushName, FVector2D( 40.f, 40.f ) );
|
|
}
|
|
else
|
|
{
|
|
CustomIconBrush = FHoudiniEngineStyle::Get()->GetBrush( TEXT( "HoudiniEngine.HoudiniEngineLogo40" ) );
|
|
}
|
|
|
|
// Create a new Houdini Tool from the modified properties
|
|
FHoudiniTool EditedHoudiniTool(
|
|
ActiveTool->HoudiniAsset,
|
|
FText::FromString( ToolProperties->Name ),
|
|
ToolProperties->Type,
|
|
ToolProperties->SelectionType,
|
|
FText::FromString (ToolProperties->ToolTip ),
|
|
CustomIconBrush,
|
|
ToolProperties->HelpURL,
|
|
false,
|
|
ToolProperties->AssetPath,
|
|
ActiveTool->ToolDirectory,
|
|
ActiveTool->JSONFile);
|
|
|
|
EditedToolArray.Add( EditedHoudiniTool );
|
|
|
|
// Remove the tool from Root
|
|
ToolProperties->RemoveFromRoot();
|
|
}
|
|
|
|
if ( EditedToolArray.Num() != 1 )
|
|
return;
|
|
|
|
// Add the new tools to the editor
|
|
TArray< TSharedPtr<FHoudiniTool> >* EditorTools = FHoudiniEngineEditor::Get().GetHoudiniToolsForWrite();
|
|
if ( !EditorTools )
|
|
return;
|
|
|
|
for ( int32 Idx = 0; Idx < EditedToolArray.Num(); Idx++ )
|
|
{
|
|
FHoudiniTool EditedTool = EditedToolArray[ Idx ];
|
|
|
|
// Update the editor tool list
|
|
int32 FoundIndex = -1;
|
|
bool IsDefault = false;
|
|
if ( !FHoudiniEngineEditor::Get().FindHoudiniTool( *ActiveTool, FoundIndex, IsDefault ) )
|
|
continue;
|
|
|
|
if ( IsDefault )
|
|
continue;
|
|
|
|
// Modify the Editor Tool if needed
|
|
bool bModified = false;
|
|
TSharedPtr< FHoudiniTool > EditorTool = (*EditorTools)[FoundIndex];
|
|
|
|
if (!EditorTool->Name.EqualTo(EditedTool.Name))
|
|
{
|
|
EditorTool->Name = EditedTool.Name;
|
|
bModified = true;
|
|
}
|
|
|
|
if (EditorTool->Type != EditedTool.Type)
|
|
{
|
|
EditorTool->Type = EditedTool.Type;
|
|
bModified = true;
|
|
}
|
|
|
|
if (EditorTool->SelectionType != EditedTool.SelectionType)
|
|
{
|
|
EditorTool->SelectionType = EditedTool.SelectionType;
|
|
bModified = true;
|
|
}
|
|
|
|
if (!EditorTool->ToolTipText.EqualTo(EditedTool.ToolTipText))
|
|
{
|
|
EditorTool->ToolTipText = EditedTool.ToolTipText;
|
|
bModified = true;
|
|
}
|
|
|
|
if (EditorTool->Icon && EditedTool.Icon)
|
|
{
|
|
if (EditorTool->Icon->GetResourceName().ToString() != EditedTool.Icon->GetResourceName().ToString())
|
|
{
|
|
EditorTool->Icon = EditedTool.Icon;
|
|
bModified = true;
|
|
}
|
|
}
|
|
|
|
if( EditorTool->HelpURL != EditedTool.HelpURL )
|
|
{
|
|
EditorTool->HelpURL = EditedTool.HelpURL;
|
|
bModified = true;
|
|
}
|
|
|
|
if (!FPaths::IsSamePath(EditorTool->SourceAssetPath.FilePath, EditedTool.SourceAssetPath.FilePath))
|
|
{
|
|
EditorTool->SourceAssetPath = EditedTool.SourceAssetPath;
|
|
bModified = true;
|
|
}
|
|
|
|
EditorTool->DefaultTool = false;
|
|
|
|
// Write the new tool's state to JSON
|
|
if (bModified)
|
|
FHoudiniEngineEditor::Get().WriteJSONFromHoudiniTool( EditedTool );
|
|
}
|
|
|
|
// Call construct to refresh the shelf
|
|
SHoudiniToolPalette::FArguments Args;
|
|
Construct( Args );
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::GenerateMissingJSONFiles()
|
|
{
|
|
// Get the current list of directories
|
|
TArray< FHoudiniToolDirectory > ToolDirectories;
|
|
FHoudiniEngineEditor::Get().GetHoudiniToolDirectories(FHoudiniEngineEditor::Get().CurrentHoudiniToolDirIndex, ToolDirectories );
|
|
|
|
// For all selected tool directories
|
|
for (int32 n = 0; n < ToolDirectories.Num(); n++)
|
|
{
|
|
FString ToolDirPath = ToolDirectories[n].Path.Path;
|
|
|
|
// List all the json files in the current directory
|
|
TArray<FString> JSONFiles;
|
|
IFileManager::Get().FindFiles(JSONFiles, *ToolDirPath, TEXT(".json"));
|
|
|
|
// List all the hda files in the current directory
|
|
TArray<FString> HDAFiles;
|
|
IFileManager::Get().FindFiles(HDAFiles, *ToolDirPath, TEXT(".hda"));
|
|
|
|
// For each HDA, try to find a corresponding json file
|
|
// If we find one, remove the hda from the array
|
|
for ( int32 HDAIndex = HDAFiles.Num() - 1; HDAIndex >= 0; HDAIndex-- )
|
|
{
|
|
FString HDABaseName = FPaths::GetBaseFilename( HDAFiles[ HDAIndex ] );
|
|
for (auto CurrentJSON : JSONFiles)
|
|
{
|
|
FString JSONBaseName = FPaths::GetBaseFilename( CurrentJSON );
|
|
if ( JSONBaseName.Equals( HDABaseName ) )
|
|
HDAFiles.RemoveAt( HDAIndex );
|
|
}
|
|
}
|
|
|
|
// Generate a default JSON file for each remaining HDA
|
|
for ( auto CurrentHDA : HDAFiles )
|
|
{
|
|
FString HDABaseName = FPaths::GetBaseFilename( CurrentHDA );
|
|
|
|
FHoudiniTool DefaultTool;
|
|
DefaultTool.Name = FText::FromString( HDABaseName );
|
|
DefaultTool.ToolTipText = FText::FromString( FString() );
|
|
DefaultTool.Icon = nullptr;
|
|
DefaultTool.HelpURL = FString();
|
|
DefaultTool.Type = EHoudiniToolType::HTOOLTYPE_OPERATOR_SINGLE;
|
|
DefaultTool.DefaultTool = false;
|
|
DefaultTool.SelectionType = EHoudiniToolSelectionType::HTOOL_SELECTION_ALL;
|
|
|
|
DefaultTool.SourceAssetPath.FilePath = ToolDirPath / CurrentHDA;
|
|
DefaultTool.ToolDirectory = ToolDirectories[ n ];
|
|
DefaultTool.JSONFile = HDABaseName + TEXT(".json");
|
|
|
|
FHoudiniEngineEditor::Get().WriteJSONFromHoudiniTool( DefaultTool );
|
|
}
|
|
}
|
|
|
|
// Trigger a refresh of the list
|
|
OnDirectoryChange( CurrentHoudiniToolDir );
|
|
}
|
|
|
|
TSharedRef<SWindow>
|
|
SHoudiniToolPalette::CreateFloatingDetailsView( const TArray< UObject* >& InObjects )
|
|
{
|
|
TSharedRef<SWindow> NewSlateWindow = SNew(SWindow)
|
|
.Title(NSLOCTEXT("PropertyEditor", "WindowTitle", "Houdini Tools Property Editor"))
|
|
.ClientSize(FVector2D(400, 550));
|
|
|
|
// If the main frame exists parent the window to it
|
|
TSharedPtr< SWindow > ParentWindow;
|
|
if ( FModuleManager::Get().IsModuleLoaded("MainFrame") )
|
|
{
|
|
IMainFrameModule& MainFrame = FModuleManager::GetModuleChecked<IMainFrameModule>("MainFrame");
|
|
ParentWindow = MainFrame.GetParentWindow();
|
|
}
|
|
|
|
if ( ParentWindow.IsValid() )
|
|
{
|
|
// Parent the window to the main frame
|
|
FSlateApplication::Get().AddWindowAsNativeChild( NewSlateWindow, ParentWindow.ToSharedRef() );
|
|
}
|
|
else
|
|
{
|
|
FSlateApplication::Get().AddWindow( NewSlateWindow );
|
|
}
|
|
|
|
FDetailsViewArgs Args;
|
|
Args.bHideSelectionTip = true;
|
|
Args.bLockable = false;
|
|
Args.bAllowMultipleTopLevelObjects = true;
|
|
Args.ViewIdentifier = TEXT("Houdini Tools Properties");
|
|
Args.NameAreaSettings = FDetailsViewArgs::HideNameArea;
|
|
Args.bShowPropertyMatrixButton = false;
|
|
Args.bShowOptions = false;
|
|
Args.bShowModifiedPropertiesOption = false;
|
|
Args.bShowActorLabel = false;
|
|
|
|
FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
TSharedRef<IDetailsView> DetailView = PropertyEditorModule.CreateDetailView( Args );
|
|
DetailView->SetObjects( InObjects );
|
|
|
|
NewSlateWindow->SetContent(
|
|
SNew( SBorder )
|
|
.BorderImage( FEditorStyle::GetBrush( TEXT("PropertyWindow.WindowBorder") ) )
|
|
[
|
|
DetailView
|
|
]
|
|
);
|
|
|
|
return NewSlateWindow;
|
|
}
|
|
|
|
FReply
|
|
SHoudiniToolPalette::OnKeyDown( const FGeometry& InGeometry, const FKeyEvent& InKeyEvent )
|
|
{
|
|
if ( InKeyEvent.GetKey() == EKeys::Delete )
|
|
{
|
|
RemoveActiveTool();
|
|
return FReply::Handled();
|
|
}
|
|
else if ( InKeyEvent.GetKey() == EKeys::Enter )
|
|
{
|
|
EditActiveHoudiniTool();
|
|
return FReply::Handled();
|
|
}
|
|
else if (InKeyEvent.GetKey() == EKeys::F5 )
|
|
{
|
|
OnDirectoryChange( CurrentHoudiniToolDir );
|
|
return FReply::Handled();
|
|
}
|
|
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::RemoveActiveTool()
|
|
{
|
|
if ( !ActiveTool.IsValid() )
|
|
return;
|
|
|
|
// Remove the tool from the editor list
|
|
TArray< TSharedPtr<FHoudiniTool> >* EditorTools = FHoudiniEngineEditor::Get().GetHoudiniToolsForWrite();
|
|
if ( !EditorTools )
|
|
return;
|
|
|
|
int32 EditorIndex = -1;
|
|
bool IsDefaultTool = false;
|
|
if ( !FHoudiniEngineEditor::Get().FindHoudiniTool( *ActiveTool, EditorIndex, IsDefaultTool ) )
|
|
return;
|
|
|
|
if ( IsDefaultTool )
|
|
return;
|
|
|
|
EditorTools->RemoveAt( EditorIndex );
|
|
|
|
// Delete the tool's JSON file to remove it
|
|
FString ToolJSONFilePath = ActiveTool->ToolDirectory.Path.Path / ActiveTool->JSONFile;
|
|
|
|
if ( FPaths::FileExists(ToolJSONFilePath) )
|
|
{
|
|
FPlatformFileManager::Get().GetPlatformFile().DeleteFile(*ToolJSONFilePath);
|
|
}
|
|
|
|
// Call construct to refresh the shelf
|
|
SHoudiniToolPalette::FArguments Args;
|
|
Construct( Args );
|
|
}
|
|
|
|
bool
|
|
SHoudiniToolPalette::IsActiveHoudiniToolEditable()
|
|
{
|
|
int32 FoundIndex = -1;
|
|
bool IsDefault = false;
|
|
|
|
if ( !ActiveTool.IsValid() )
|
|
return false;
|
|
|
|
if ( !FHoudiniEngineEditor::Get().FindHoudiniTool( *ActiveTool, FoundIndex, IsDefault ) )
|
|
return false;
|
|
|
|
return !IsDefault;
|
|
}
|
|
|
|
FReply
|
|
SHoudiniToolPalette::OnEditToolDirectories()
|
|
{
|
|
// Append all the custom tools directory from the runtime settings
|
|
const UHoudiniRuntimeSettings * HoudiniRuntimeSettings = GetDefault< UHoudiniRuntimeSettings >();
|
|
if (!HoudiniRuntimeSettings)
|
|
return FReply::Handled();
|
|
|
|
TArray<UObject *> NewCustomHoudiniToolDirs;
|
|
|
|
FString CustomToolDirs = TEXT("CustomToolDir");
|
|
UHoudiniToolDirectoryProperties* NewToolDirProperty = NewObject< UHoudiniToolDirectoryProperties >(GetTransientPackage(), FName(*CustomToolDirs));
|
|
NewToolDirProperty->CustomHoudiniToolsDirectories = HoudiniRuntimeSettings->CustomHoudiniToolsLocation;
|
|
NewCustomHoudiniToolDirs.Add(NewToolDirProperty);
|
|
|
|
TSharedRef< SWindow > Window = CreateFloatingDetailsView(NewCustomHoudiniToolDirs);
|
|
Window->SetOnWindowClosed(FOnWindowClosed::CreateSP(this, &SHoudiniToolPalette::OnEditToolDirectoriesWindowClosed, NewCustomHoudiniToolDirs));
|
|
|
|
return FReply::Handled();
|
|
}
|
|
|
|
|
|
void
|
|
SHoudiniToolPalette::OnEditToolDirectoriesWindowClosed(const TSharedRef<SWindow>& InWindow, TArray<UObject *> InObjects)
|
|
{
|
|
TArray<FHoudiniToolDirectory> EditedToolDirectories;
|
|
for (int32 ObjIdx = 0; ObjIdx < InObjects.Num(); ObjIdx++)
|
|
{
|
|
UHoudiniToolDirectoryProperties* ToolDirProperties = Cast< UHoudiniToolDirectoryProperties >(InObjects[ObjIdx]);
|
|
if (!ToolDirProperties)
|
|
continue;
|
|
|
|
for ( auto CurrentToolDir : ToolDirProperties->CustomHoudiniToolsDirectories )
|
|
{
|
|
EditedToolDirectories.AddUnique( CurrentToolDir );
|
|
}
|
|
|
|
// Remove the tool directory from Root
|
|
ToolDirProperties->RemoveFromRoot();
|
|
}
|
|
|
|
// Get the runtime settings to add the new custom tools there
|
|
UHoudiniRuntimeSettings * HoudiniRuntimeSettings = const_cast<UHoudiniRuntimeSettings*>(GetDefault< UHoudiniRuntimeSettings >());
|
|
if (!HoudiniRuntimeSettings)
|
|
return;
|
|
|
|
// Check if there's been any modification
|
|
bool bModified = false;
|
|
if ( EditedToolDirectories.Num() != HoudiniRuntimeSettings->CustomHoudiniToolsLocation.Num() )
|
|
{
|
|
bModified = true;
|
|
}
|
|
else
|
|
{
|
|
// Same number of directory, look for a value change
|
|
for ( int32 n = 0; n < EditedToolDirectories.Num(); n++ )
|
|
{
|
|
if (EditedToolDirectories[n] != HoudiniRuntimeSettings->CustomHoudiniToolsLocation[n])
|
|
{
|
|
bModified = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bModified )
|
|
return;
|
|
|
|
// Replace the Tool dir in the runtime settings with the edited list
|
|
HoudiniRuntimeSettings->CustomHoudiniToolsLocation.Empty();
|
|
|
|
for ( auto EditedToolDir : EditedToolDirectories )
|
|
HoudiniRuntimeSettings->CustomHoudiniToolsLocation.AddUnique( EditedToolDir );
|
|
|
|
// Save the Settings file to keep the new tools upon restart
|
|
HoudiniRuntimeSettings->SaveConfig();
|
|
|
|
// Call construct to refresh the shelf
|
|
SHoudiniToolPalette::FArguments Args;
|
|
Construct(Args);
|
|
}
|
|
|
|
bool
|
|
SHoudiniToolPalette::GetCurrentDirectoryIndex(int32& SelectedIndex ) const
|
|
{
|
|
if ( CurrentHoudiniToolDir.IsEmpty() )
|
|
return false;
|
|
|
|
for (int32 n = 0; n < HoudiniToolDirArray.Num(); n++)
|
|
{
|
|
if (!HoudiniToolDirArray[n].IsValid())
|
|
continue;
|
|
|
|
if (!HoudiniToolDirArray[n]->Equals(CurrentHoudiniToolDir))
|
|
continue;
|
|
|
|
SelectedIndex = (n - 1);
|
|
/*
|
|
FHoudiniToolDirectory EditorToolDir;
|
|
FHoudiniEngineEditor::Get().GetHoudiniToolDirectories(FoundIndex, EditorToolDir);
|
|
|
|
if ( EditorToolDir.Name.Equals(CurrentHoudiniToolDir) )
|
|
return true;
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::OnDirectoryChange(const FString& NewDir)
|
|
{
|
|
if ( NewDir.IsEmpty() )
|
|
return;
|
|
|
|
CurrentHoudiniToolDir = NewDir;
|
|
|
|
int32 DirIndex = 0;
|
|
if ( GetCurrentDirectoryIndex( DirIndex ) )
|
|
{
|
|
|
|
FHoudiniEngineEditor::Get().UpdateHoudiniToolList( DirIndex );
|
|
HoudiniToolListView->RequestListRefresh();
|
|
}
|
|
}
|
|
|
|
void
|
|
SHoudiniToolPalette::UpdateHoudiniToolDirectories()
|
|
{
|
|
int32 ChoiceIndex = FHoudiniEngineEditor::Get().CurrentHoudiniToolDirIndex;
|
|
|
|
// Refresh the Editor's Houdini Tool list
|
|
FHoudiniEngineEditor::Get().UpdateHoudiniToolList( ChoiceIndex );
|
|
|
|
// Add the directory choice lists
|
|
HoudiniToolDirArray.Empty();
|
|
|
|
TArray<FHoudiniToolDirectory> ToolDirArray;
|
|
FHoudiniEngineEditor::Get().GetAllHoudiniToolDirectories( ToolDirArray );
|
|
|
|
{
|
|
FString * ChoiceLabel = new FString(TEXT("All"));
|
|
HoudiniToolDirArray.Add(TSharedPtr< FString >(ChoiceLabel));
|
|
|
|
if (ChoiceIndex < 0 )
|
|
CurrentHoudiniToolDir = *ChoiceLabel;
|
|
}
|
|
|
|
{
|
|
FString * ChoiceLabel = new FString(TEXT("Default"));
|
|
HoudiniToolDirArray.Add(TSharedPtr< FString >(ChoiceLabel));
|
|
|
|
if ( ChoiceIndex == 0 )
|
|
CurrentHoudiniToolDir = *ChoiceLabel;
|
|
}
|
|
|
|
for (int32 n = 1; n < ToolDirArray.Num(); n++)
|
|
{
|
|
FString * ChoiceLabel = new FString( ToolDirArray[n].Name );
|
|
HoudiniToolDirArray.Add(TSharedPtr< FString >(ChoiceLabel));
|
|
|
|
if ( ChoiceIndex == n )
|
|
CurrentHoudiniToolDir = *ChoiceLabel;
|
|
}
|
|
}
|
|
|
|
FText
|
|
SHoudiniToolPalette::OnGetSelectedDirText() const
|
|
{
|
|
return FText::FromString(CurrentHoudiniToolDir);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|