/* * 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 "HoudiniParameterDetails.h" #include "HoudiniApi.h" #include "HoudiniAssetComponentDetails.h" #include "HoudiniEngineEditorPrivatePCH.h" #include "HoudiniAssetComponent.h" #include "HoudiniAssetInput.h" #include "HoudiniAssetInstanceInput.h" #include "HoudiniAssetInstanceInputField.h" #include "HoudiniAssetParameterButton.h" #include "HoudiniAssetParameterChoice.h" #include "HoudiniAssetParameterColor.h" #include "HoudiniAssetParameterFile.h" #include "HoudiniAssetParameterFolder.h" #include "HoudiniAssetParameterFolderList.h" #include "HoudiniAssetParameterFloat.h" #include "HoudiniAssetParameterInt.h" #include "HoudiniAssetParameterLabel.h" #include "HoudiniAssetParameterMultiparm.h" #include "HoudiniAssetParameterRamp.h" #include "HoudiniAssetParameterSeparator.h" #include "HoudiniAssetParameterString.h" #include "HoudiniAssetParameterToggle.h" #include "HoudiniRuntimeSettings.h" #include "SNewFilePathPicker.h" #include "Editor/CurveEditor/Public/CurveEditorSettings.h" #include "DetailLayoutBuilder.h" #include "Editor/SceneOutliner/Public/SceneOutlinerModule.h" #include "Editor/SceneOutliner/Public/SceneOutlinerPublicTypes.h" #include "Editor/UnrealEd/Public/AssetThumbnail.h" #include "Editor/PropertyEditor/Public/PropertyCustomizationHelpers.h" #include "Editor/PropertyEditor/Private/PropertyNode.h" #include "Editor/PropertyEditor/Private/SDetailsViewBase.h" #include "EditorDirectories.h" #include "Engine/Selection.h" #include "Engine/SkeletalMesh.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Internationalization/Internationalization.h" #include "IDetailGroup.h" #include "Particles/ParticleSystemComponent.h" #include "SCurveEditor.h" #include "SAssetDropTarget.h" #include "Sound/SoundBase.h" #include "Math/UnitConversion.h" #include "Math/NumericLimits.h" #include "Misc/Optional.h" #include "Widgets/Colors/SColorPicker.h" #include "Widgets/Colors/SColorBlock.h" #include "Widgets/Input/NumericUnitTypeInterface.inl" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SComboBox.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SEditableTextBox.h" #include "Widgets/Input/SNumericEntryBox.h" #include "Widgets/Input/SRotatorInputBox.h" #include "Widgets/Input/SVectorInputBox.h" #include "Widgets/Layout/SSeparator.h" #include "Widgets/Layout/SUniformGridPanel.h" #include "EngineUtils.h" #define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE void FHoudiniParameterDetails::CreateNameWidget( UHoudiniAssetParameter* InParam, FDetailWidgetRow & Row, bool WithLabel ) { if ( !InParam || InParam->IsPendingKill() ) return; FText ParameterLabelText = FText::FromString( InParam->GetParameterLabel() ); const FText & FinalParameterLabelText = WithLabel ? ParameterLabelText : FText::GetEmpty(); FText ParameterTooltip = GetParameterTooltip( InParam ); if ( InParam->bIsChildOfMultiparm && InParam->ParentParameter && !InParam->ParentParameter->IsPendingKill() ) { TSharedRef< SHorizontalBox > HorizontalBox = SNew( SHorizontalBox ); // We have to make sure the ParentParameter is a multiparm, as folders will cause issues here // ( we want to call RemoveMultiParmInstance or AddMultiParmInstance on the parent multiparm, not just the parent) UHoudiniAssetParameter * ParentMultiparm = InParam->ParentParameter; while ( ParentMultiparm && !ParentMultiparm->IsPendingKill() && !ParentMultiparm->bIsMultiparm ) ParentMultiparm = ParentMultiparm->ParentParameter; // Failed to find the multiparm parent, better have the original parent than nullptr if ( !ParentMultiparm || ParentMultiparm->IsPendingKill() ) ParentMultiparm = InParam->ParentParameter; TSharedRef< SWidget > ClearButton = PropertyCustomizationHelpers::MakeClearButton( FSimpleDelegate::CreateUObject( (UHoudiniAssetParameterMultiparm *)ParentMultiparm, &UHoudiniAssetParameterMultiparm::RemoveMultiparmInstance, InParam->MultiparmInstanceIndex ), LOCTEXT( "RemoveMultiparmInstanceToolTip", "Remove" ) ); TSharedRef< SWidget > AddButton = PropertyCustomizationHelpers::MakeAddButton( FSimpleDelegate::CreateUObject( (UHoudiniAssetParameterMultiparm *)ParentMultiparm, &UHoudiniAssetParameterMultiparm::AddMultiparmInstance, InParam->MultiparmInstanceIndex ), LOCTEXT( "InsertBeforeMultiparmInstanceToolTip", "Insert Before" ) ); if ( InParam->ChildIndex != 0 ) { AddButton.Get().SetVisibility( EVisibility::Hidden ); ClearButton.Get().SetVisibility( EVisibility::Hidden ); } // Adding eventual padding for nested multiparams UHoudiniAssetParameter* currentParentParameter = ParentMultiparm; while ( currentParentParameter && currentParentParameter->bIsChildOfMultiparm ) { if ( currentParentParameter->bIsMultiparm ) HorizontalBox->AddSlot().MaxWidth( 16.0f ); currentParentParameter = currentParentParameter->ParentParameter; } HorizontalBox->AddSlot().AutoWidth().Padding( 2.0f, 0.0f ) [ ClearButton ]; HorizontalBox->AddSlot().AutoWidth().Padding( 0.0f, 0.0f ) [ AddButton ]; HorizontalBox->AddSlot().Padding( 2, 5, 5, 2 ) [ SNew( STextBlock ) .Text( FinalParameterLabelText ) .ToolTipText( WithLabel ? ParameterTooltip : ParameterLabelText ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ]; Row.NameWidget.Widget = HorizontalBox; } else { Row.NameWidget.Widget = SNew( STextBlock ) .Text( FinalParameterLabelText ) .ToolTipText( ParameterTooltip ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ); } } FText FHoudiniParameterDetails::GetParameterTooltip( UHoudiniAssetParameter* InParam ) { if ( !InParam ) return FText(); /* FString Tooltip = InParam->GetParameterLabel() + TEXT(" (") + InParam->GetParameterName() + TEXT(")"); if ( !InParam->GetParameterHelp().IsEmpty() ) Tooltip += TEXT ( ":\n") + InParam->GetParameterHelp(); return FText::FromString( Tooltip ); */ if ( !InParam->GetParameterHelp().IsEmpty() ) return FText::FromString( InParam->GetParameterHelp() ); else return FText::FromString( InParam->GetParameterLabel() + TEXT( " (" ) + InParam->GetParameterName() + TEXT( ")" ) ); } void FHoudiniParameterDetails::CreateWidget( IDetailCategoryBuilder & LocalDetailCategoryBuilder, UHoudiniAssetParameter* InParam ) { if( !InParam || InParam->IsPendingKill() ) return; if ( auto ParamFloat = Cast( InParam ) ) { CreateWidgetFloat( LocalDetailCategoryBuilder, *ParamFloat ); } else if ( auto ParamFolder = Cast( InParam ) ) { CreateWidgetFolder( LocalDetailCategoryBuilder, *ParamFolder ); } else if ( auto ParamFolderList = Cast( InParam ) ) { CreateWidgetFolderList( LocalDetailCategoryBuilder, *ParamFolderList ); } // Test Ramp before Multiparm! else if ( auto ParamRamp = Cast( InParam ) ) { CreateWidgetRamp( LocalDetailCategoryBuilder, *ParamRamp ); } else if ( auto ParamMultiparm = Cast( InParam ) ) { CreateWidgetMultiparm( LocalDetailCategoryBuilder, *ParamMultiparm ); } else if ( auto ParamButton = Cast( InParam ) ) { CreateWidgetButton( LocalDetailCategoryBuilder, *ParamButton ); } else if ( auto ParamChoice = Cast( InParam ) ) { CreateWidgetChoice( LocalDetailCategoryBuilder, *ParamChoice ); } else if ( auto ParamColor = Cast( InParam ) ) { CreateWidgetColor( LocalDetailCategoryBuilder, *ParamColor ); } else if ( auto ParamToggle = Cast( InParam ) ) { CreateWidgetToggle( LocalDetailCategoryBuilder, *ParamToggle ); } else if ( auto ParamInput = Cast( InParam ) ) { CreateWidgetInput( LocalDetailCategoryBuilder, *ParamInput ); } else if ( auto ParamInt = Cast( InParam ) ) { CreateWidgetInt( LocalDetailCategoryBuilder, *ParamInt ); } else if ( auto ParamInstanceInput = Cast( InParam ) ) { CreateWidgetInstanceInput( LocalDetailCategoryBuilder, *ParamInstanceInput ); } else if ( auto ParamLabel = Cast( InParam ) ) { CreateWidgetLabel( LocalDetailCategoryBuilder, *ParamLabel ); } else if ( auto ParamString = Cast( InParam ) ) { CreateWidgetString( LocalDetailCategoryBuilder, *ParamString ); } else if ( auto ParamSeparator = Cast( InParam ) ) { CreateWidgetSeparator( LocalDetailCategoryBuilder, *ParamSeparator ); } else if ( auto ParamFile = Cast( InParam ) ) { CreateWidgetFile( LocalDetailCategoryBuilder, *ParamFile ); } else { check( false ); } } void FHoudiniParameterDetails::CreateWidget( TSharedPtr< SVerticalBox > VerticalBox, class UHoudiniAssetParameter* InParam ) { check( InParam ); if ( auto ParamChoice = Cast( InParam ) ) { CreateWidgetChoice( VerticalBox, *ParamChoice ); } else if ( auto ParamToggle = Cast( InParam ) ) { CreateWidgetToggle( VerticalBox, *ParamToggle ); } else { check( false ); } } void FHoudiniParameterDetails::CreateWidgetFile( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterFile& InParam ) { FDetailWidgetRow& Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); TSharedRef VerticalBox = SNew( SVerticalBox ); FString FileTypeWidgetFilter = TEXT( "All files (*.*)|*.*" ); if ( !InParam.Filters.IsEmpty() ) FileTypeWidgetFilter = FString::Printf( TEXT( "%s files (*.%s)|*.%s" ), *InParam.Filters, *InParam.Filters, *InParam.Filters ); FString BrowseWidgetDirectory = FEditorDirectories::Get().GetLastDirectory( ELastDirectory::GENERIC_OPEN ); for ( int32 Idx = 0; Idx < InParam.GetTupleSize(); ++Idx ) { FString FileWidgetPath = InParam.Values[Idx]; FString FileWidgetBrowsePath = BrowseWidgetDirectory; if ( !FileWidgetPath.IsEmpty() ) { FString FileWidgetDirPath = FPaths::GetPath( FileWidgetPath ); if ( !FileWidgetDirPath.IsEmpty() ) FileWidgetBrowsePath = FileWidgetDirPath; } VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SNew( SNewFilePathPicker ) .BrowseButtonImage( FEditorStyle::GetBrush( "PropertyWindow.Button_Ellipsis" ) ) .BrowseButtonStyle( FEditorStyle::Get(), "HoverHintOnly" ) .BrowseButtonToolTip( LOCTEXT( "FileButtonToolTipText", "Choose a file" ) ) .BrowseDirectory( FileWidgetBrowsePath ) .BrowseTitle( LOCTEXT( "PropertyEditorTitle", "File picker..." ) ) .FilePath( FileWidgetPath ) .FileTypeFilter( FileTypeWidgetFilter ) .OnPathPicked( FOnPathPicked::CreateUObject( &InParam, &UHoudiniAssetParameterFile::HandleFilePathPickerPathPicked, Idx ) ) .IsNewFile( !InParam.IsReadOnly ) ]; } Row.ValueWidget.Widget = VerticalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); Row.ValueWidget.Widget->SetEnabled( !InParam.bIsDisabled ); } void FHoudiniParameterDetails::CreateWidgetFolder( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterFolder& InParam ) { if ( InParam.ParentParameter && !InParam.ParentParameter->IsPendingKill() && InParam.ParentParameter->IsActiveChildParameter( &InParam ) ) { // Recursively create all child parameters. for ( UHoudiniAssetParameter * ChildParam : InParam.ChildParameters ) { if ( ChildParam && !ChildParam->IsPendingKill() ) FHoudiniParameterDetails::CreateWidget(LocalDetailCategoryBuilder, ChildParam); } } } void FHoudiniParameterDetails::CreateWidgetFolderList( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterFolderList& InParam ) { TWeakObjectPtr MyParam( &InParam ); TSharedRef< SHorizontalBox > HorizontalBox = SNew( SHorizontalBox ); LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ) [ SAssignNew(HorizontalBox, SHorizontalBox) ]; for ( int32 ParameterIdx = 0; ParameterIdx < InParam.ChildParameters.Num(); ++ParameterIdx ) { UHoudiniAssetParameter * HoudiniAssetParameterChild = InParam.ChildParameters[ ParameterIdx ]; if ( !HoudiniAssetParameterChild || HoudiniAssetParameterChild->IsPendingKill() ) continue; if ( HoudiniAssetParameterChild->IsA( UHoudiniAssetParameterFolder::StaticClass() ) ) { FText ParameterLabelText = FText::FromString( HoudiniAssetParameterChild->GetParameterLabel() ); FText ParameterToolTip = GetParameterTooltip( HoudiniAssetParameterChild ); HorizontalBox->AddSlot().Padding( 0, 2, 0, 2 ) [ SNew( SButton ) .VAlign( VAlign_Center ) .HAlign( HAlign_Center ) .Text( ParameterLabelText ) .ToolTipText( ParameterToolTip ) .OnClicked( FOnClicked::CreateLambda( [=]() { if ( MyParam.IsValid() ) { MyParam->ActiveChildParameter = ParameterIdx; MyParam->OnParamStateChanged(); } return FReply::Handled(); })) ]; } } // Recursively create all child parameters. for (UHoudiniAssetParameter * ChildParam : InParam.ChildParameters) { if ( ChildParam && !ChildParam->IsPendingKill() ) FHoudiniParameterDetails::CreateWidget(LocalDetailCategoryBuilder, ChildParam); } if ( InParam.ChildParameters.Num() > 1 ) { TSharedPtr< STextBlock > TextBlock; LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ) [ SAssignNew( TextBlock, STextBlock ) .Text( FText::GetEmpty() ) .ToolTipText( FText::GetEmpty() ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) .WrapTextAt( HAPI_UNREAL_DESIRED_ROW_FULL_WIDGET_WIDTH ) ]; if ( TextBlock.IsValid() ) TextBlock->SetEnabled( !InParam.bIsDisabled ); } } void FHoudiniParameterDetails::CreateWidgetMultiparm( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterMultiparm& InParam ) { FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); TSharedRef< SHorizontalBox > HorizontalBox = SNew( SHorizontalBox ); TSharedPtr< SNumericEntryBox< int32 > > NumericEntryBox; HorizontalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SAssignNew( NumericEntryBox, SNumericEntryBox< int32 > ) .AllowSpin( true ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) .Value( TAttribute< TOptional< int32 > >::Create( TAttribute< TOptional< int32 > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterMultiparm::GetValue ) ) ) .OnValueChanged( SNumericEntryBox::FOnValueChanged::CreateUObject( &InParam, &UHoudiniAssetParameterMultiparm::SetValue ) ) ]; HorizontalBox->AddSlot().AutoWidth().Padding( 2.0f, 0.0f ) [ PropertyCustomizationHelpers::MakeAddButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetParameterMultiparm::AddElement, true, true ), LOCTEXT( "AddAnotherMultiparmInstanceToolTip", "Add Another Instance" ) ) ]; HorizontalBox->AddSlot().AutoWidth().Padding( 2.0f, 0.0f ) [ PropertyCustomizationHelpers::MakeRemoveButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetParameterMultiparm::RemoveElement, true, true ), LOCTEXT( "RemoveLastMultiparmInstanceToolTip", "Remove Last Instance" ) ) ]; HorizontalBox->AddSlot().AutoWidth().Padding( 2.0f, 0.0f ) [ PropertyCustomizationHelpers::MakeEmptyButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetParameterMultiparm::SetValue, 0 ), LOCTEXT( "ClearAllMultiparmInstanesToolTip", "Clear All Instances" ) ) ]; if ( NumericEntryBox.IsValid() ) NumericEntryBox->SetEnabled( !InParam.bIsDisabled ); Row.ValueWidget.Widget = HorizontalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); // Recursively create all child parameters. for ( UHoudiniAssetParameter * ChildParam : InParam.ChildParameters ) if ( ChildParam && !ChildParam->IsPendingKill() ) FHoudiniParameterDetails::CreateWidget( LocalDetailCategoryBuilder, ChildParam ); } /** We need to inherit from curve editor in order to get subscription to mouse events. **/ class SHoudiniAssetParameterRampCurveEditor : public SCurveEditor { public: SLATE_BEGIN_ARGS( SHoudiniAssetParameterRampCurveEditor ) : _ViewMinInput( 0.0f ) , _ViewMaxInput( 10.0f ) , _ViewMinOutput( 0.0f ) , _ViewMaxOutput( 1.0f ) , _InputSnap( 0.1f ) , _OutputSnap( 0.05f ) , _InputSnappingEnabled( false ) , _OutputSnappingEnabled( false ) , _ShowTimeInFrames( false ) , _TimelineLength( 5.0f ) , _DesiredSize( FVector2D::ZeroVector ) , _DrawCurve( true ) , _HideUI( true ) , _AllowZoomOutput( true ) , _AlwaysDisplayColorCurves( false ) , _ZoomToFitVertical( true ) , _ZoomToFitHorizontal( true ) , _ShowZoomButtons( true ) , _XAxisName() , _YAxisName() , _ShowInputGridNumbers( true ) , _ShowOutputGridNumbers( true ) , _ShowCurveSelector( true ) , _GridColor( FLinearColor( 0.0f, 0.0f, 0.0f, 0.3f ) ) { _Clipping = EWidgetClipping::ClipToBounds; } SLATE_ATTRIBUTE(float, ViewMinInput) SLATE_ATTRIBUTE(float, ViewMaxInput) SLATE_ATTRIBUTE(TOptional, DataMinInput) SLATE_ATTRIBUTE(TOptional, DataMaxInput) SLATE_ATTRIBUTE(float, ViewMinOutput) SLATE_ATTRIBUTE(float, ViewMaxOutput) SLATE_ATTRIBUTE(float, InputSnap) SLATE_ATTRIBUTE(float, OutputSnap) SLATE_ATTRIBUTE(bool, InputSnappingEnabled) SLATE_ATTRIBUTE(bool, OutputSnappingEnabled) SLATE_ATTRIBUTE(bool, ShowTimeInFrames) SLATE_ATTRIBUTE(float, TimelineLength) SLATE_ATTRIBUTE(FVector2D, DesiredSize) SLATE_ATTRIBUTE(bool, AreCurvesVisible) SLATE_ARGUMENT(bool, DrawCurve) SLATE_ARGUMENT(bool, HideUI) SLATE_ARGUMENT(bool, AllowZoomOutput) SLATE_ARGUMENT(bool, AlwaysDisplayColorCurves) SLATE_ARGUMENT(bool, ZoomToFitVertical) SLATE_ARGUMENT(bool, ZoomToFitHorizontal) SLATE_ARGUMENT(bool, ShowZoomButtons) SLATE_ARGUMENT(TOptional, XAxisName) SLATE_ARGUMENT(TOptional, YAxisName) SLATE_ARGUMENT(bool, ShowInputGridNumbers) SLATE_ARGUMENT(bool, ShowOutputGridNumbers) SLATE_ARGUMENT(bool, ShowCurveSelector) SLATE_ARGUMENT(FLinearColor, GridColor) SLATE_EVENT( FOnSetInputViewRange, OnSetInputViewRange ) SLATE_EVENT( FOnSetOutputViewRange, OnSetOutputViewRange ) SLATE_EVENT( FOnSetAreCurvesVisible, OnSetAreCurvesVisible ) SLATE_EVENT( FSimpleDelegate, OnCreateAsset ) SLATE_END_ARGS() public: /** Widget construction. **/ void Construct( const FArguments & InArgs ); protected: /** Handle mouse up events. **/ virtual FReply OnMouseButtonUp( const FGeometry & MyGeometry, const FPointerEvent & MouseEvent ) override; public: /** Set parent ramp parameter. **/ void SetParentRampParameter( UHoudiniAssetParameterRamp * InHoudiniAssetParameterRamp ) { HoudiniAssetParameterRamp = InHoudiniAssetParameterRamp; } protected: /** Parent ramp parameter. **/ TWeakObjectPtr HoudiniAssetParameterRamp; }; void SHoudiniAssetParameterRampCurveEditor::Construct( const FArguments & InArgs ) { SCurveEditor::Construct( SCurveEditor::FArguments() .ViewMinInput( InArgs._ViewMinInput ) .ViewMaxInput( InArgs._ViewMaxInput ) .ViewMinOutput( InArgs._ViewMinOutput ) .ViewMaxOutput( InArgs._ViewMaxOutput ) .XAxisName( InArgs._XAxisName ) .YAxisName( InArgs._YAxisName ) .HideUI( InArgs._HideUI ) .DrawCurve( InArgs._DrawCurve ) .TimelineLength( InArgs._TimelineLength ) .AllowZoomOutput( InArgs._AllowZoomOutput ) .ShowInputGridNumbers( InArgs._ShowInputGridNumbers ) .ShowOutputGridNumbers( InArgs._ShowOutputGridNumbers ) .ShowZoomButtons( InArgs._ShowZoomButtons ) .ZoomToFitHorizontal( InArgs._ZoomToFitHorizontal ) .ZoomToFitVertical( InArgs._ZoomToFitVertical ) ); HoudiniAssetParameterRamp = nullptr; UCurveEditorSettings * CurveEditorSettings = GetSettings(); if ( CurveEditorSettings ) { //CurveEditorSettings->SetCurveVisibility( ECurveEditorCurveVisibility::AllCurves ); CurveEditorSettings->SetTangentVisibility( ECurveEditorTangentVisibility::NoTangents ); } } FReply SHoudiniAssetParameterRampCurveEditor::OnMouseButtonUp( const FGeometry & MyGeometry, const FPointerEvent & MouseEvent ) { FReply Reply = SCurveEditor::OnMouseButtonUp( MyGeometry, MouseEvent ); if ( HoudiniAssetParameterRamp.IsValid() ) HoudiniAssetParameterRamp->OnCurveEditingFinished(); return Reply; } void FHoudiniParameterDetails::CreateWidgetRamp( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterRamp& InParam ) { TWeakObjectPtr MyParam( &InParam ); FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); TSharedRef< SHorizontalBox > HorizontalBox = SNew( SHorizontalBox ); FString CurveAxisTextX = TEXT( "" ); FString CurveAxisTextY = TEXT( "" ); UClass * CurveClass = nullptr; if ( InParam.bIsFloatRamp ) { CurveAxisTextX = TEXT( HAPI_UNREAL_RAMP_FLOAT_AXIS_X ); CurveAxisTextY = TEXT( HAPI_UNREAL_RAMP_FLOAT_AXIS_Y ); CurveClass = UHoudiniAssetParameterRampCurveFloat::StaticClass(); } else { CurveAxisTextX = TEXT( HAPI_UNREAL_RAMP_COLOR_AXIS_X ); CurveAxisTextY = TEXT( HAPI_UNREAL_RAMP_COLOR_AXIS_Y ); CurveClass = UHoudiniAssetParameterRampCurveColor::StaticClass(); } HorizontalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SNew( SBorder ) .VAlign( VAlign_Fill ) [ SAssignNew( InParam.CurveEditor, SHoudiniAssetParameterRampCurveEditor ) .ViewMinInput( 0.0f ) .ViewMaxInput( 1.0f ) .HideUI( true ) .DrawCurve( true ) .ViewMinInput( 0.0f ) .ViewMaxInput( 1.0f ) .ViewMinOutput( 0.0f ) .ViewMaxOutput( 1.0f ) .TimelineLength( 1.0f ) .AllowZoomOutput( false ) .ShowInputGridNumbers( false ) .ShowOutputGridNumbers( false ) .ShowZoomButtons( false ) .ZoomToFitHorizontal( false ) .ZoomToFitVertical( false ) .XAxisName( CurveAxisTextX ) .YAxisName( CurveAxisTextY ) .ShowCurveSelector( false ) ] ]; // Set callback for curve editor events. TSharedPtr< SHoudiniAssetParameterRampCurveEditor > HoudiniRampEditor = StaticCastSharedPtr(InParam.CurveEditor ); if ( HoudiniRampEditor.IsValid() ) HoudiniRampEditor->SetParentRampParameter( &InParam ); if ( InParam.bIsFloatRamp ) { if ( !InParam.HoudiniAssetParameterRampCurveFloat && (InParam.PrimaryObject && !InParam.PrimaryObject->IsPendingKill() ) ) { InParam.HoudiniAssetParameterRampCurveFloat = Cast< UHoudiniAssetParameterRampCurveFloat >( NewObject< UHoudiniAssetParameterRampCurveFloat >( InParam.PrimaryObject, UHoudiniAssetParameterRampCurveFloat::StaticClass(), NAME_None, RF_Transactional | RF_Public ) ); InParam.HoudiniAssetParameterRampCurveFloat->SetParentRampParameter( &InParam ); } // Set curve values. InParam.GenerateCurvePoints(); // Set the curve that is being edited. InParam.CurveEditor->SetCurveOwner( InParam.HoudiniAssetParameterRampCurveFloat, true ); } else { if ( !InParam.HoudiniAssetParameterRampCurveColor && ( InParam.PrimaryObject && !InParam.PrimaryObject->IsPendingKill() ) ) { InParam.HoudiniAssetParameterRampCurveColor = Cast< UHoudiniAssetParameterRampCurveColor >( NewObject< UHoudiniAssetParameterRampCurveColor >( InParam.PrimaryObject, UHoudiniAssetParameterRampCurveColor::StaticClass(), NAME_None, RF_Transactional | RF_Public ) ); InParam.HoudiniAssetParameterRampCurveColor->SetParentRampParameter( &InParam ); } // Set curve values. InParam.GenerateCurvePoints(); // Set the curve that is being edited. InParam.CurveEditor->SetCurveOwner( InParam.HoudiniAssetParameterRampCurveColor, true ); } Row.ValueWidget.Widget = HorizontalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); // Recursively create all child parameters. for ( UHoudiniAssetParameter * ChildParam : InParam.ChildParameters ) if ( ChildParam && !ChildParam->IsPendingKill() ) FHoudiniParameterDetails::CreateWidget( LocalDetailCategoryBuilder, ChildParam ); } void FHoudiniParameterDetails::CreateWidgetButton( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterButton& InParam ) { TWeakObjectPtr MyParam( &InParam ); FDetailWidgetRow& Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); FText ParameterTooltip = GetParameterTooltip( &InParam ); TSharedRef< SHorizontalBox > HorizontalBox = SNew( SHorizontalBox ); TSharedPtr< SButton > Button; HorizontalBox->AddSlot().Padding( 1, 2, 4, 2 ) [ SAssignNew( Button, SButton ) .VAlign( VAlign_Center ) .HAlign( HAlign_Center ) .Text( ParameterLabelText ) .ToolTipText( ParameterTooltip ) .OnClicked( FOnClicked::CreateLambda( [=]() { if ( MyParam.IsValid() ) { // There's no undo operation for button. MyParam->MarkChanged(); } return FReply::Handled(); })) ]; if ( Button.IsValid() ) Button->SetEnabled( !InParam.bIsDisabled ); Row.ValueWidget.Widget = HorizontalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); } void FHoudiniParameterDetails::CreateWidgetChoice( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterChoice& InParam ) { FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); TSharedRef< SHorizontalBox > HorizontalBox = SNew( SHorizontalBox ); TSharedPtr< SComboBox< TSharedPtr< FString > > > ComboBox; TWeakObjectPtr MyParam( &InParam ); HorizontalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SAssignNew( ComboBox, SComboBox< TSharedPtr< FString > > ) .OptionsSource( &InParam.StringChoiceLabels ) .InitiallySelectedItem( InParam.StringChoiceLabels[InParam.CurrentValue] ) .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() || !MyParam.IsValid() ) return; MyParam->OnChoiceChange( NewChoice ); })) [ SNew( STextBlock ) .Text( TAttribute< FText >::Create( TAttribute< FText >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterChoice::HandleChoiceContentText ) ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] ]; if ( ComboBox.IsValid() ) ComboBox->SetEnabled( !InParam.bIsDisabled ); Row.ValueWidget.Widget = HorizontalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); } void FHoudiniParameterDetails::CreateWidgetChoice( TSharedPtr< SVerticalBox > VerticalBox, class UHoudiniAssetParameterChoice& InParam ) { FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); FText ParameterTooltip = GetParameterTooltip( &InParam ); TWeakObjectPtr MyParam( &InParam ); VerticalBox->AddSlot().Padding( 2, 2, 2, 2 ) [ SNew( SHorizontalBox ) + SHorizontalBox::Slot().MaxWidth( 80 ).Padding( 7, 1, 0, 0 ).VAlign( VAlign_Center ) [ SNew( STextBlock ) .Text( ParameterLabelText ) .ToolTipText( ParameterTooltip ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] + SHorizontalBox::Slot() [ SNew( SComboBox< TSharedPtr< FString > > ) .OptionsSource( &InParam.StringChoiceLabels ) .InitiallySelectedItem( InParam.StringChoiceLabels[InParam.CurrentValue] ) .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() || !MyParam.IsValid() ) return; MyParam->OnChoiceChange( NewChoice ); })) [ SNew( STextBlock ) .Text( TAttribute< FText >::Create( TAttribute< FText >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterChoice::HandleChoiceContentText ) ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] ] ]; } void FHoudiniParameterDetails::CreateWidgetColor( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterColor& InParam ) { TWeakObjectPtr MyParam( &InParam ); FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); TSharedPtr< SColorBlock > ColorBlock; TSharedRef< SVerticalBox > VerticalBox = SNew( SVerticalBox ); VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SAssignNew( ColorBlock, SColorBlock ) .Color( TAttribute< FLinearColor >::Create( TAttribute< FLinearColor >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterColor::GetColor ) ) ) .OnMouseButtonDown( FPointerEventHandler::CreateLambda( [=]( const FGeometry & MyGeometry, const FPointerEvent & MouseEvent ) { if ( MouseEvent.GetEffectingButton() != EKeys::LeftMouseButton || !MyParam.IsValid() ) return FReply::Unhandled(); FColorPickerArgs PickerArgs; PickerArgs.ParentWidget = ColorBlock; PickerArgs.bUseAlpha = true; PickerArgs.DisplayGamma = TAttribute< float >::Create( TAttribute< float >::FGetter::CreateUObject( GEngine, &UEngine::GetDisplayGamma ) ); PickerArgs.OnColorCommitted = FOnLinearColorValueChanged::CreateUObject( MyParam.Get(), &UHoudiniAssetParameterColor::OnPaintColorChanged, true, true ); PickerArgs.InitialColorOverride = MyParam->GetColor(); PickerArgs.bOnlyRefreshOnOk = true; OpenColorPicker( PickerArgs ); return FReply::Handled(); } )) ]; if ( ColorBlock.IsValid() ) ColorBlock->SetEnabled( !InParam.bIsDisabled ); Row.ValueWidget.Widget = VerticalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); } void FHoudiniParameterDetails::CreateWidgetToggle( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterToggle& InParam ) { FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, false ); TSharedRef< SVerticalBox > VerticalBox = SNew( SVerticalBox ); for ( int32 Idx = 0; Idx < InParam.GetTupleSize(); ++Idx ) { TSharedPtr< SCheckBox > CheckBox; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SAssignNew( CheckBox, SCheckBox ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetParameterToggle::CheckStateChanged, Idx ) ) .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterToggle::IsChecked, Idx ) ) ) .Content() [ SNew( STextBlock ) .Text( ParameterLabelText ) .ToolTipText( GetParameterTooltip( &InParam ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] ]; if ( CheckBox.IsValid() ) CheckBox->SetEnabled( !InParam.bIsDisabled ); } Row.ValueWidget.Widget = VerticalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); } void FHoudiniParameterDetails::CreateWidgetToggle( TSharedPtr< SVerticalBox > VerticalBox, class UHoudiniAssetParameterToggle& InParam ) { FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); FText ParameterTooltip = GetParameterTooltip( &InParam ); for ( int32 Idx = 0; Idx < InParam.GetTupleSize(); ++Idx ) { VerticalBox->AddSlot().Padding( 0, 2, 0, 2 ) [ SNew( SHorizontalBox ) +SHorizontalBox::Slot().MaxWidth( 8 ) [ SNew( STextBlock ) .Text( FText::GetEmpty() ) .ToolTipText( ParameterLabelText ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] +SHorizontalBox::Slot() [ SNew( SCheckBox ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetParameterToggle::CheckStateChanged, Idx ) ) .IsChecked(TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterToggle::IsChecked, Idx ) ) ) .Content() [ SNew( STextBlock ) .Text( ParameterLabelText ) .ToolTipText( ParameterTooltip ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] ] ]; } } void FHoudiniParameterDetails::CreateWidgetFloat( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterFloat& InParam ) { TWeakObjectPtr MyParam( &InParam ); /** Should we swap Y and Z fields (only relevant for Vector3) */ bool SwappedAxis3Vector = false; if ( auto Settings = UHoudiniRuntimeSettings::StaticClass()->GetDefaultObject() ) { SwappedAxis3Vector = InParam.GetTupleSize() == 3 && Settings->ImportAxis == HRSAI_Unreal; } if ( SwappedAxis3Vector ) { // Ignore the swapping if that parameter has the noswap tag if ( InParam.NoSwap ) SwappedAxis3Vector = false; } FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); // Helper function to find a unit from a string (name or abbreviation) auto ParmUnit = FUnitConversion::UnitFromString( *InParam.ValueUnit ); EUnit Unit = EUnit::Unspecified; if (FUnitConversion::Settings().ShouldDisplayUnits() && ParmUnit.IsSet()) Unit = ParmUnit.GetValue(); TSharedPtr> paramTypeInterface; paramTypeInterface = MakeShareable(new TNumericUnitTypeInterface(Unit)); if ( InParam.GetTupleSize() == 3 ) { TSharedRef< SVerticalBox > VerticalBox = SNew(SVerticalBox); VerticalBox->AddSlot().Padding(2, 2, 5, 2) [ SNew(SHorizontalBox) + SHorizontalBox::Slot().FillWidth(1.0f) [ SNew(SVectorInputBox) .bColorAxisLabels(true) .X(TAttribute< TOptional< float > >::Create(TAttribute< TOptional< float > >::FGetter::CreateUObject(&InParam, &UHoudiniAssetParameterFloat::GetValue, 0))) .Y(TAttribute< TOptional< float > >::Create(TAttribute< TOptional< float > >::FGetter::CreateUObject(&InParam, &UHoudiniAssetParameterFloat::GetValue, SwappedAxis3Vector ? 2 : 1))) .Z(TAttribute< TOptional< float > >::Create(TAttribute< TOptional< float > >::FGetter::CreateUObject(&InParam, &UHoudiniAssetParameterFloat::GetValue, SwappedAxis3Vector ? 1 : 2))) .OnXCommitted(FOnFloatValueCommitted::CreateLambda( [=](float Val, ETextCommit::Type TextCommitType) { MyParam->SetValue(Val, 0, true, true); })) .OnYCommitted(FOnFloatValueCommitted::CreateLambda( [=](float Val, ETextCommit::Type TextCommitType) { MyParam->SetValue(Val, SwappedAxis3Vector ? 2 : 1, true, true); })) .OnZCommitted(FOnFloatValueCommitted::CreateLambda( [=](float Val, ETextCommit::Type TextCommitType) { MyParam->SetValue(Val, SwappedAxis3Vector ? 1 : 2, true, true); })) .TypeInterface(paramTypeInterface) ] + SHorizontalBox::Slot().AutoWidth().Padding(2.0f, 0.0f).VAlign(VAlign_Center) [ SNew(SButton) .ToolTipText(LOCTEXT("ResetToBase", "Reset to default")) .ButtonStyle(FEditorStyle::Get(), "NoBorder") .ContentPadding(0) .Visibility(EVisibility::Visible) .OnClicked(FOnClicked::CreateUObject( &InParam, &UHoudiniAssetParameter::OnRevertParmToDefault, -1)) [ SNew(SImage) .Image(FEditorStyle::GetBrush("PropertyWindow.DiffersFromDefault")) ] ] ]; Row.ValueWidget.Widget = VerticalBox; } else { TSharedRef< SVerticalBox > VerticalBox = SNew( SVerticalBox ); for ( int32 Idx = 0; Idx < InParam.GetTupleSize(); ++Idx ) { TSharedPtr< SNumericEntryBox< float > > NumericEntryBox; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SNew(SHorizontalBox) + SHorizontalBox::Slot().FillWidth(1.0f) [ SAssignNew( NumericEntryBox, SNumericEntryBox< float > ) .AllowSpin( true ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) .MinValue( InParam.ValueMin ) .MaxValue( InParam.ValueMax ) .MinSliderValue( InParam.ValueUIMin ) .MaxSliderValue( InParam.ValueUIMax ) .Value( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterFloat::GetValue, Idx ) ) ) .OnValueChanged( SNumericEntryBox< float >::FOnValueChanged::CreateLambda( [=]( float Val ) { MyParam->SetValue( Val, Idx, false, false ); } ) ) .OnValueCommitted( SNumericEntryBox< float >::FOnValueCommitted::CreateLambda( [=]( float Val, ETextCommit::Type TextCommitType ) { MyParam->SetValue( Val, Idx, true, true ); } ) ) .OnBeginSliderMovement( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetParameterFloat::OnSliderMovingBegin, Idx ) ) .OnEndSliderMovement( SNumericEntryBox< float >::FOnValueChanged::CreateUObject( &InParam, &UHoudiniAssetParameterFloat::OnSliderMovingFinish, Idx ) ) .SliderExponent( 1.0f ) .TypeInterface( paramTypeInterface ) ] + SHorizontalBox::Slot().AutoWidth().Padding(2.0f, 0.0f).VAlign(VAlign_Center) [ SNew(SButton) .ToolTipText(LOCTEXT("ResetToBase", "Reset to default")) .ButtonStyle(FEditorStyle::Get(), "NoBorder") .ContentPadding(0) .Visibility(EVisibility::Visible) .OnClicked(FOnClicked::CreateUObject( &InParam, &UHoudiniAssetParameter::OnRevertParmToDefault, Idx)) [ SNew(SImage) .Image(FEditorStyle::GetBrush("PropertyWindow.DiffersFromDefault")) ] ] ]; } Row.ValueWidget.Widget = VerticalBox; } Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); Row.ValueWidget.Widget->SetEnabled( !InParam.bIsDisabled ); } void FHoudiniParameterDetails::CreateWidgetInt( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterInt& InParam ) { TWeakObjectPtr MyParam( &InParam ); FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); TSharedRef< SVerticalBox > VerticalBox = SNew( SVerticalBox ); // Helper function to find a unit from a string (name or abbreviation) auto ParmUnit = FUnitConversion::UnitFromString( *InParam.ValueUnit ); EUnit Unit = EUnit::Unspecified; if (FUnitConversion::Settings().ShouldDisplayUnits() && ParmUnit.IsSet()) Unit = ParmUnit.GetValue(); TSharedPtr> paramTypeInterface; paramTypeInterface = MakeShareable(new TNumericUnitTypeInterface(Unit)); for ( int32 Idx = 0; Idx < InParam.GetTupleSize(); ++Idx ) { TSharedPtr< SNumericEntryBox< int32 > > NumericEntryBox; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SNew(SHorizontalBox) + SHorizontalBox::Slot().FillWidth(1.0f) [ SAssignNew( NumericEntryBox, SNumericEntryBox< int32 > ) .AllowSpin( true ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) .MinValue( InParam.ValueMin ) .MaxValue( InParam.ValueMax ) .MinSliderValue( InParam.ValueUIMin ) .MaxSliderValue( InParam.ValueUIMax ) .Value( TAttribute< TOptional< int32 > >::Create( TAttribute< TOptional< int32 > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetParameterInt::GetValue, Idx ) ) ) .OnValueChanged( SNumericEntryBox< int32 >::FOnValueChanged::CreateLambda( [=]( int32 Val ) { MyParam->SetValue( Val, Idx, false, false ); } ) ) .OnValueCommitted( SNumericEntryBox< int32 >::FOnValueCommitted::CreateLambda( [=]( float Val, ETextCommit::Type TextCommitType ) { MyParam->SetValue( Val, Idx, true, true ); } ) ) .OnBeginSliderMovement( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetParameterInt::OnSliderMovingBegin, Idx ) ) .OnEndSliderMovement( SNumericEntryBox< int32 >::FOnValueChanged::CreateUObject( &InParam, &UHoudiniAssetParameterInt::OnSliderMovingFinish, Idx ) ) .SliderExponent( 1.0f ) .TypeInterface(paramTypeInterface) ] + SHorizontalBox::Slot().AutoWidth().Padding(2.0f, 0.0f).VAlign(VAlign_Center) [ SNew(SButton) .ToolTipText(LOCTEXT("ResetToBase", "Reset to default")) .ButtonStyle(FEditorStyle::Get(), "NoBorder") .ContentPadding(0) .Visibility(EVisibility::Visible) .OnClicked(FOnClicked::CreateUObject( &InParam, &UHoudiniAssetParameter::OnRevertParmToDefault, Idx)) [ SNew(SImage) .Image(FEditorStyle::GetBrush("PropertyWindow.DiffersFromDefault")) ] ] ]; if ( NumericEntryBox.IsValid() ) NumericEntryBox->SetEnabled( !InParam.bIsDisabled ); } Row.ValueWidget.Widget = VerticalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); } void FHoudiniParameterDetails::CreateWidgetInstanceInput( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetInstanceInput& InParam ) { TWeakObjectPtr MyParam(&InParam); if (!MyParam.IsValid()) return; // Get thumbnail pool for this builder. IDetailLayoutBuilder & DetailLayoutBuilder = LocalDetailCategoryBuilder.GetParentLayout(); TSharedPtr< FAssetThumbnailPool > AssetThumbnailPool = DetailLayoutBuilder.GetThumbnailPool(); // Classes allowed by instanced inputs. const TArray< const UClass * > AllowedClasses = { UStaticMesh::StaticClass(), AActor::StaticClass(), UBlueprint::StaticClass(), USoundBase::StaticClass(), UParticleSystem::StaticClass(), USkeletalMesh::StaticClass() }; const int32 FieldCount = InParam.InstanceInputFields.Num(); for ( int32 FieldIdx = 0; FieldIdx < FieldCount; ++FieldIdx ) { UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InParam.InstanceInputFields[ FieldIdx ]; if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() ) continue; const int32 VariationCount = HoudiniAssetInstanceInputField->InstanceVariationCount(); for( int32 VariationIdx = 0; VariationIdx < VariationCount; VariationIdx++ ) { UObject * InstancedObject = HoudiniAssetInstanceInputField->GetInstanceVariation( VariationIdx ); if ( !InstancedObject || InstancedObject->IsPendingKill() ) { HOUDINI_LOG_WARNING( TEXT("Null Object found for instance variation %d"), VariationIdx ); continue; } // Create thumbnail for this object. TSharedPtr< FAssetThumbnail > StaticMeshThumbnail = MakeShareable( new FAssetThumbnail( InstancedObject, 64, 64, AssetThumbnailPool ) ); TSharedRef< SVerticalBox > PickerVerticalBox = SNew( SVerticalBox ); TSharedPtr< SHorizontalBox > PickerHorizontalBox = nullptr; TSharedPtr< SBorder > StaticMeshThumbnailBorder; FString FieldLabel = InParam.GetFieldLabel(FieldIdx, VariationIdx); IDetailGroup& DetailGroup = LocalDetailCategoryBuilder.AddGroup(FName(*FieldLabel), FText::FromString(FieldLabel)); DetailGroup.AddWidgetRow() .NameContent() [ SNew(SSpacer) .Size(FVector2D(250, 64)) ] .ValueContent() .MinDesiredWidth(HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH) [ PickerVerticalBox ]; TWeakObjectPtr InputFieldPtr( HoudiniAssetInstanceInputField ); PickerVerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SAssetDropTarget ) .OnIsAssetAcceptableForDrop( SAssetDropTarget::FIsAssetAcceptableForDrop::CreateLambda( [=]( const UObject* Obj ) { for ( auto Klass : AllowedClasses ) { if ( Obj && Obj->IsA( Klass ) ) return true; } return false; }) ) .OnAssetDropped( SAssetDropTarget::FOnAssetDropped::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::OnStaticMeshDropped, HoudiniAssetInstanceInputField, FieldIdx, VariationIdx ) ) [ SAssignNew( PickerHorizontalBox, SHorizontalBox ) ] ]; PickerHorizontalBox->AddSlot().Padding( 0.0f, 0.0f, 2.0f, 0.0f ).AutoWidth() [ SAssignNew( StaticMeshThumbnailBorder, SBorder ) .Padding( 5.0f ) .OnMouseDoubleClick( FPointerEventHandler::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::OnThumbnailDoubleClick, InstancedObject ) ) [ SNew( SBox ) .WidthOverride( 64 ) .HeightOverride( 64 ) .ToolTipText( FText::FromString( InstancedObject->GetPathName() ) ) [ StaticMeshThumbnail->MakeThumbnailWidget() ] ] ]; StaticMeshThumbnailBorder->SetBorderImage( TAttribute< const FSlateBrush * >::Create( TAttribute< const FSlateBrush * >::FGetter::CreateLambda([=]() { if ( StaticMeshThumbnailBorder.IsValid() && StaticMeshThumbnailBorder->IsHovered() ) return FEditorStyle::GetBrush( "PropertyEditor.AssetThumbnailLight" ); else return FEditorStyle::GetBrush( "PropertyEditor.AssetThumbnailShadow" ); } ) ) ); PickerHorizontalBox->AddSlot().AutoWidth().Padding( 0.0f, 28.0f, 0.0f, 28.0f ) [ PropertyCustomizationHelpers::MakeAddButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::OnAddInstanceVariation, HoudiniAssetInstanceInputField, VariationIdx ), LOCTEXT("AddAnotherInstanceToolTip", "Add Another Instance" ) ) ]; PickerHorizontalBox->AddSlot().AutoWidth().Padding( 2.0f, 28.0f, 4.0f, 28.0f ) [ PropertyCustomizationHelpers::MakeRemoveButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::OnRemoveInstanceVariation, HoudiniAssetInstanceInputField, VariationIdx ), LOCTEXT("RemoveLastInstanceToolTip", "Remove Last Instance")) ]; TSharedPtr< SComboButton > AssetComboButton; TSharedPtr< SHorizontalBox > ButtonBox; PickerHorizontalBox->AddSlot() .FillWidth( 10.0f ) .Padding( 0.0f, 4.0f, 4.0f, 4.0f ) .VAlign( VAlign_Center ) [ SNew( SVerticalBox ) +SVerticalBox::Slot() .HAlign( HAlign_Fill ) [ SAssignNew( ButtonBox, SHorizontalBox ) +SHorizontalBox::Slot() [ SAssignNew( AssetComboButton, SComboButton ) //.ToolTipText( this, &FHoudiniAssetComponentDetails::OnGetToolTip ) .ButtonStyle( FEditorStyle::Get(), "PropertyEditor.AssetComboStyle" ) .ForegroundColor( FEditorStyle::GetColor( "PropertyEditor.AssetName.ColorAndOpacity" ) ) .OnMenuOpenChanged( FOnIsOpenChanged::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::ChangedStaticMeshComboButton, HoudiniAssetInstanceInputField, FieldIdx, VariationIdx ) ) .ContentPadding( 2.0f ) .ButtonContent() [ SNew( STextBlock ) .TextStyle( FEditorStyle::Get(), "PropertyEditor.AssetClass" ) .Font( FEditorStyle::GetFontStyle( FName( TEXT( "PropertyWindow.NormalFont" ) ) ) ) .Text( FText::FromString(InstancedObject->GetName() ) ) ] ] ] ]; // Create asset picker for this combo button. { TArray< UFactory * > NewAssetFactories; TSharedRef< SWidget > PropertyMenuAssetPicker = PropertyCustomizationHelpers::MakeAssetPickerWithMenu( FAssetData( InstancedObject ), true, AllowedClasses, NewAssetFactories, FOnShouldFilterAsset(), FOnAssetSelected::CreateLambda( [=]( const FAssetData& AssetData ) { if ( AssetComboButton.IsValid() && MyParam.IsValid() && InputFieldPtr.IsValid() ) { AssetComboButton->SetIsOpen( false ); UObject * Object = AssetData.GetAsset(); MyParam->OnStaticMeshDropped( Object, InputFieldPtr.Get(), FieldIdx, VariationIdx ); } }), FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::CloseStaticMeshComboButton, HoudiniAssetInstanceInputField, FieldIdx, VariationIdx ) ); AssetComboButton->SetMenuContent( PropertyMenuAssetPicker ); } // Create tooltip. FFormatNamedArguments Args; Args.Add( TEXT( "Asset" ), FText::FromString( InstancedObject->GetName() ) ); FText StaticMeshTooltip = FText::Format( LOCTEXT( "BrowseToSpecificAssetInContentBrowser", "Browse to '{Asset}' in Content Browser" ), Args ); ButtonBox->AddSlot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign( VAlign_Center ) [ PropertyCustomizationHelpers::MakeBrowseButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::OnInstancedObjectBrowse, InstancedObject ), TAttribute< FText >( StaticMeshTooltip ) ) ]; ButtonBox->AddSlot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign( VAlign_Center ) [ SNew( SButton ) .ToolTipText( LOCTEXT( "ResetToBase", "Reset to default static mesh" ) ) .ButtonStyle( FEditorStyle::Get(), "NoBorder" ) .ContentPadding( 0 ) .Visibility( EVisibility::Visible ) .OnClicked( FOnClicked::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::OnResetStaticMeshClicked, HoudiniAssetInstanceInputField, FieldIdx, VariationIdx ) ) [ SNew( SImage ) .Image( FEditorStyle::GetBrush( "PropertyWindow.DiffersFromDefault" ) ) ] ]; TSharedRef< SVerticalBox > OffsetVerticalBox = SNew( SVerticalBox ); FText LabelRotationText = LOCTEXT( "HoudiniRotationOffset", "Rotation Offset" ); DetailGroup.AddWidgetRow() .NameContent() [ SNew( STextBlock ) .Text( LabelRotationText ) .ToolTipText( LabelRotationText ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .ValueContent() .MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH - 17 ) [ SNew( SHorizontalBox ) + SHorizontalBox::Slot().MaxWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH - 17 ) [ SNew( SRotatorInputBox ) .AllowSpin( true ) .bColorAxisLabels( true ) .Roll( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::GetRotationRoll, HoudiniAssetInstanceInputField, VariationIdx ) ) ) .Pitch( TAttribute< TOptional< float> >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::GetRotationPitch, HoudiniAssetInstanceInputField, VariationIdx ) ) ) .Yaw( TAttribute >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::GetRotationYaw, HoudiniAssetInstanceInputField, VariationIdx ) ) ) .OnRollChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::SetRotationRoll, HoudiniAssetInstanceInputField, VariationIdx ) ) .OnPitchChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::SetRotationPitch, HoudiniAssetInstanceInputField, VariationIdx ) ) .OnYawChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::SetRotationYaw, HoudiniAssetInstanceInputField, VariationIdx ) ) ] ]; FText LabelScaleText = LOCTEXT( "HoudiniScaleOffset", "Scale Offset" ); DetailGroup.AddWidgetRow() .NameContent() [ SNew( STextBlock ) .Text( LabelScaleText ) .ToolTipText( LabelScaleText ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .ValueContent() .MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ) [ SNew( SHorizontalBox ) + SHorizontalBox::Slot().MaxWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ) [ SNew( SVectorInputBox ) .bColorAxisLabels( true ) .X( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::GetScaleX, HoudiniAssetInstanceInputField, VariationIdx ) ) ) .Y( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float> >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::GetScaleY, HoudiniAssetInstanceInputField, VariationIdx ) ) ) .Z( TAttribute< TOptional< float> >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::GetScaleZ, HoudiniAssetInstanceInputField, VariationIdx ) ) ) .OnXChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::SetScaleX, HoudiniAssetInstanceInputField, VariationIdx ) ) .OnYChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::SetScaleY, HoudiniAssetInstanceInputField, VariationIdx ) ) .OnZChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInstanceInput::SetScaleZ, HoudiniAssetInstanceInputField, VariationIdx ) ) ] + SHorizontalBox::Slot().AutoWidth() [ // Add a checkbox to toggle between preserving the ratio of x,y,z components of scale when a value is entered SNew( SCheckBox ) .Style( FEditorStyle::Get(), "TransparentCheckBox" ) .ToolTipText( LOCTEXT( "PreserveScaleToolTip", "When locked, scales uniformly based on the current xyz scale values so the object maintains its shape in each direction when scaled" ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateLambda( [=]( ECheckBoxState NewState ) { if ( MyParam.IsValid() && InputFieldPtr.IsValid() ) MyParam->CheckStateChanged( NewState == ECheckBoxState::Checked, InputFieldPtr.Get(), VariationIdx ); })) .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute::FGetter::CreateLambda( [=]() { if ( InputFieldPtr.IsValid() && InputFieldPtr->AreOffsetsScaledLinearly( VariationIdx ) ) return ECheckBoxState::Checked; return ECheckBoxState::Unchecked; }))) [ SNew( SImage ) .Image( TAttribute::Create( TAttribute::FGetter::CreateLambda( [=]() { if ( InputFieldPtr.IsValid() && InputFieldPtr->AreOffsetsScaledLinearly( VariationIdx ) ) { return FEditorStyle::GetBrush( TEXT( "GenericLock" ) ); } return FEditorStyle::GetBrush( TEXT( "GenericUnlock" ) ); }))) .ColorAndOpacity( FSlateColor::UseForeground() ) ] ] ]; } } } FMenuBuilder FHoudiniParameterDetails::Helper_CreateCustomActorPickerWidget( UHoudiniAssetInput& InParam, const TAttribute& HeadingText, const bool& bShowCurrentSelectionSection ) { // Custom Actor Picker showing only the desired Actor types. // Note: Code stolen from SPropertyMenuActorPicker.cpp FOnShouldFilterActor ActorFilter = FOnShouldFilterActor::CreateUObject( &InParam, &UHoudiniAssetInput::OnShouldFilterActor ); //FHoudiniEngineEditor & HoudiniEngineEditor = FHoudiniEngineEditor::Get(); //TSharedPtr< ISlateStyle > StyleSet = GEditor->GetSlateStyle(); FMenuBuilder MenuBuilder( true, NULL ); if ( bShowCurrentSelectionSection ) { MenuBuilder.BeginSection( NAME_None, LOCTEXT( "CurrentActorOperationsHeader", "Current Selection" ) ); { MenuBuilder.AddMenuEntry( TAttribute::Create( TAttribute::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetCurrentSelectionText ) ), TAttribute::Create( TAttribute::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetCurrentSelectionText ) ), FSlateIcon(),//StyleSet->GetStyleSetName(), "HoudiniEngine.HoudiniEngineLogo")), FUIAction(), NAME_None, EUserInterfaceActionType::Button, NAME_None ); } MenuBuilder.EndSection(); } MenuBuilder.BeginSection( NAME_None, HeadingText ); { FSceneOutlinerModule & SceneOutlinerModule = FModuleManager::Get().LoadModuleChecked< FSceneOutlinerModule >(TEXT("SceneOutliner")); SceneOutliner::FInitializationOptions InitOptions; { InitOptions.Mode = ESceneOutlinerMode::ActorPicker; InitOptions.Filters->AddFilterPredicate(ActorFilter); InitOptions.bFocusSearchBoxWhenOpened = true; InitOptions.bShowCreateNewFolder = false; // Add the gutter so we can change the selection's visibility InitOptions.ColumnMap.Add(SceneOutliner::FBuiltInColumnTypes::Gutter(), SceneOutliner::FColumnInfo(SceneOutliner::EColumnVisibility::Visible, 0)); InitOptions.ColumnMap.Add(SceneOutliner::FBuiltInColumnTypes::Label(), SceneOutliner::FColumnInfo(SceneOutliner::EColumnVisibility::Visible, 10)); InitOptions.ColumnMap.Add(SceneOutliner::FBuiltInColumnTypes::ActorInfo(), SceneOutliner::FColumnInfo(SceneOutliner::EColumnVisibility::Visible, 20)); } static const FVector2D SceneOutlinerWindowSize( 350.0f, 200.0f ); TSharedRef< SWidget > MenuWidget = SNew( SBox ) .WidthOverride( SceneOutlinerWindowSize.X ) .HeightOverride( SceneOutlinerWindowSize.Y ) [ SNew( SBorder ) .BorderImage( FEditorStyle::GetBrush( "Menu.Background" ) ) [ SceneOutlinerModule.CreateSceneOutliner( InitOptions, FOnActorPicked::CreateUObject( &InParam, &UHoudiniAssetInput::OnActorSelected ) ) ] ]; MenuBuilder.AddWidget(MenuWidget, FText::GetEmpty(), true ); } MenuBuilder.EndSection(); return MenuBuilder; } /** Create a single geometry widget for the given input object */ void FHoudiniParameterDetails::Helper_CreateGeometryWidget( UHoudiniAssetInput& InParam, int32 AtIndex, UObject* InputObject, TSharedPtr< FAssetThumbnailPool > AssetThumbnailPool, TSharedRef< SVerticalBox > VerticalBox ) { TWeakObjectPtr MyParam( &InParam ); // Create thumbnail for this static mesh. TSharedPtr< FAssetThumbnail > StaticMeshThumbnail = MakeShareable( new FAssetThumbnail( InputObject, 64, 64, AssetThumbnailPool ) ); TSharedPtr< SHorizontalBox > HorizontalBox = NULL; // Drop Target: Static Mesh VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SAssetDropTarget ) .OnIsAssetAcceptableForDrop( SAssetDropTarget::FIsAssetAcceptableForDrop::CreateLambda( []( const UObject* InObject ) { return InObject && ( InObject->IsA< UStaticMesh >() || InObject->IsA< USkeletalMesh >() ); } ) ) .OnAssetDropped( SAssetDropTarget::FOnAssetDropped::CreateUObject( &InParam, &UHoudiniAssetInput::OnStaticMeshDropped, AtIndex ) ) [ SAssignNew( HorizontalBox, SHorizontalBox ) ] ]; // Thumbnail : Static Mesh FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); //FText ParameterTooltip = GetParameterTooltip( &InParam, true ); TSharedPtr< SBorder > StaticMeshThumbnailBorder; HorizontalBox->AddSlot().Padding( 0.0f, 0.0f, 2.0f, 0.0f ).AutoWidth() [ SAssignNew( StaticMeshThumbnailBorder, SBorder ) .Padding( 5.0f ) .OnMouseDoubleClick( FPointerEventHandler::CreateUObject( &InParam, &UHoudiniAssetInput::OnThumbnailDoubleClick, AtIndex ) ) [ SNew( SBox ) .WidthOverride( 64 ) .HeightOverride( 64 ) .ToolTipText( ParameterLabelText ) [ StaticMeshThumbnail->MakeThumbnailWidget() ] ] ]; StaticMeshThumbnailBorder->SetBorderImage( TAttribute< const FSlateBrush * >::Create( TAttribute< const FSlateBrush * >::FGetter::CreateLambda( [StaticMeshThumbnailBorder]() { if ( StaticMeshThumbnailBorder.IsValid() && StaticMeshThumbnailBorder->IsHovered() ) return FEditorStyle::GetBrush( "PropertyEditor.AssetThumbnailLight" ); else return FEditorStyle::GetBrush( "PropertyEditor.AssetThumbnailShadow" ); } ) ) ); FText MeshNameText = FText::GetEmpty(); if ( InputObject ) MeshNameText = FText::FromString( InputObject->GetName() ); // ComboBox : Static Mesh TSharedPtr< SComboButton > StaticMeshComboButton; TSharedPtr< SHorizontalBox > ButtonBox; HorizontalBox->AddSlot() .FillWidth( 1.0f ) .Padding( 0.0f, 4.0f, 4.0f, 4.0f ) .VAlign( VAlign_Center ) [ SNew( SVerticalBox ) + SVerticalBox::Slot() .HAlign( HAlign_Fill ) [ SAssignNew( ButtonBox, SHorizontalBox ) + SHorizontalBox::Slot() [ SAssignNew( StaticMeshComboButton, SComboButton ) .ButtonStyle( FEditorStyle::Get(), "PropertyEditor.AssetComboStyle" ) .ForegroundColor( FEditorStyle::GetColor( "PropertyEditor.AssetName.ColorAndOpacity" ) ) .ContentPadding( 2.0f ) .ButtonContent() [ SNew( STextBlock ) .TextStyle( FEditorStyle::Get(), "PropertyEditor.AssetClass" ) .Font( FEditorStyle::GetFontStyle( FName( TEXT( "PropertyWindow.NormalFont" ) ) ) ) .Text( MeshNameText ) ] ] ] ]; StaticMeshComboButton->SetOnGetMenuContent( FOnGetContent::CreateLambda( [ MyParam, AtIndex, StaticMeshComboButton ]() { TArray< const UClass * > AllowedClasses; AllowedClasses.Add( UStaticMesh::StaticClass() ); AllowedClasses.Add( USkeletalMesh::StaticClass() ); TArray< UFactory * > NewAssetFactories; return PropertyCustomizationHelpers::MakeAssetPickerWithMenu( FAssetData( MyParam->GetInputObject( AtIndex ) ), true, AllowedClasses, NewAssetFactories, FOnShouldFilterAsset(), FOnAssetSelected::CreateLambda( [MyParam, AtIndex, StaticMeshComboButton]( const FAssetData & AssetData ) { if ( StaticMeshComboButton.IsValid() ) { StaticMeshComboButton->SetIsOpen( false ); UObject * Object = AssetData.GetAsset(); MyParam->OnStaticMeshDropped( Object, AtIndex ); } } ), FSimpleDelegate::CreateLambda( []() {} ) ); } ) ); // Create tooltip. FFormatNamedArguments Args; Args.Add( TEXT( "Asset" ), MeshNameText ); FText StaticMeshTooltip = FText::Format( LOCTEXT( "BrowseToSpecificAssetInContentBrowser", "Browse to '{Asset}' in Content Browser" ), Args ); // Button : Browse Static Mesh ButtonBox->AddSlot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign( VAlign_Center ) [ PropertyCustomizationHelpers::MakeBrowseButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInput::OnStaticMeshBrowse, AtIndex ), TAttribute< FText >( StaticMeshTooltip ) ) ]; // ButtonBox : Reset ButtonBox->AddSlot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign( VAlign_Center ) [ SNew( SButton ) .ToolTipText( LOCTEXT( "ResetToBase", "Reset to default static mesh" ) ) .ButtonStyle( FEditorStyle::Get(), "NoBorder" ) .ContentPadding( 0 ) .Visibility( EVisibility::Visible ) .OnClicked( FOnClicked::CreateUObject( &InParam, &UHoudiniAssetInput::OnResetStaticMeshClicked, AtIndex ) ) [ SNew( SImage ) .Image( FEditorStyle::GetBrush( "PropertyWindow.DiffersFromDefault" ) ) ] ]; ButtonBox->AddSlot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ PropertyCustomizationHelpers::MakeInsertDeleteDuplicateButton( FExecuteAction::CreateLambda( [ MyParam, AtIndex ]() { MyParam->OnInsertGeo( AtIndex ); } ), FExecuteAction::CreateLambda( [MyParam, AtIndex ]() { MyParam->OnDeleteGeo( AtIndex ); } ), FExecuteAction::CreateLambda( [ MyParam, AtIndex ]() { MyParam->OnDuplicateGeo( AtIndex ); } ) ) ]; { TSharedPtr ExpanderArrow; TSharedPtr ExpanderImage; VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SHorizontalBox ) +SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SAssignNew( ExpanderArrow, SButton ) .ButtonStyle( FEditorStyle::Get(), "NoBorder" ) .ClickMethod( EButtonClickMethod::MouseDown ) .Visibility( EVisibility::Visible ) .OnClicked( FOnClicked::CreateUObject(&InParam, &UHoudiniAssetInput::OnExpandInputTransform, AtIndex ) ) [ SAssignNew( ExpanderImage, SImage ) .ColorAndOpacity( FSlateColor::UseForeground() ) ] ] +SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew( STextBlock ) .Text( LOCTEXT("GeoInputTransform", "Transform Offset") ) .ToolTipText( LOCTEXT( "GeoInputTransformTooltip", "Transform offset used for correction before sending the asset to Houdini" ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] ]; // Set delegate for image ExpanderImage->SetImage( TAttribute::Create( TAttribute::FGetter::CreateLambda( [=]() { FName ResourceName; if ( MyParam->TransformUIExpanded[ AtIndex ] ) { if ( ExpanderArrow->IsHovered() ) ResourceName = "TreeArrow_Expanded_Hovered"; else ResourceName = "TreeArrow_Expanded"; } else { if ( ExpanderArrow->IsHovered() ) ResourceName = "TreeArrow_Collapsed_Hovered"; else ResourceName = "TreeArrow_Collapsed"; } return FEditorStyle::GetBrush( ResourceName ); } ) ) ); } // TRANSFORM if ( InParam.TransformUIExpanded[ AtIndex ] ) { // Position VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SHorizontalBox ) +SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew(STextBlock) .Text( LOCTEXT("GeoInputTranslate", "T") ) .ToolTipText( LOCTEXT( "GeoInputTranslateTooltip", "Translate" ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] + SHorizontalBox::Slot().MaxWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ) [ SNew( SVectorInputBox ) .bColorAxisLabels( true ) .X( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetPositionX, AtIndex ) ) ) .Y( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float> >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetPositionY, AtIndex ) ) ) .Z( TAttribute< TOptional< float> >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetPositionZ, AtIndex ) ) ) .OnXChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetPositionX, AtIndex ) ) .OnYChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetPositionY, AtIndex ) ) .OnZChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetPositionZ, AtIndex ) ) ] ]; // Rotation VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SHorizontalBox ) +SHorizontalBox::Slot() .Padding(1.0f) .VAlign(VAlign_Center) .AutoWidth() [ SNew(STextBlock) .Text( LOCTEXT("GeoInputRotate", "R") ) .ToolTipText( LOCTEXT( "GeoInputRotateTooltip", "Rotate" ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] + SHorizontalBox::Slot().MaxWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ) [ SNew( SRotatorInputBox ) .AllowSpin( true ) .bColorAxisLabels( true ) .Roll( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetRotationRoll, AtIndex ) ) ) .Pitch( TAttribute< TOptional< float> >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetRotationPitch, AtIndex) ) ) .Yaw( TAttribute >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetRotationYaw, AtIndex) ) ) .OnRollChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetRotationRoll, AtIndex) ) .OnPitchChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetRotationPitch, AtIndex) ) .OnYawChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetRotationYaw, AtIndex) ) ] ]; // Scale VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SHorizontalBox ) +SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew( STextBlock ) .Text( LOCTEXT( "GeoInputScale", "S" ) ) .ToolTipText( LOCTEXT( "GeoInputScaleTooltip", "Scale" ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] + SHorizontalBox::Slot().MaxWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ) [ SNew( SVectorInputBox ) .bColorAxisLabels( true ) .X( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetScaleX, AtIndex ) ) ) .Y( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float> >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetScaleY, AtIndex ) ) ) .Z( TAttribute< TOptional< float> >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetScaleZ, AtIndex ) ) ) .OnXChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetScaleX, AtIndex ) ) .OnYChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetScaleY, AtIndex ) ) .OnZChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetScaleZ, AtIndex ) ) ] /* + SHorizontalBox::Slot().AutoWidth() [ // Add a checkbox to toggle between preserving the ratio of x,y,z components of scale when a value is entered SNew( SCheckBox ) .Style( FEditorStyle::Get(), "TransparentCheckBox" ) .ToolTipText( LOCTEXT( "PreserveScaleToolTip", "When locked, scales uniformly based on the current xyz scale values so the object maintains its shape in each direction when scaled" ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( this, &UHoudiniAssetInput::CheckStateChanged, AtIndex ) ) .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute::FGetter::CreateUObject( this, &UHoudiniAssetInput::IsChecked, AtIndex ) ) ) [ SNew( SImage ) .Image( TAttribute::Create( TAttribute::FGetter::CreateUObject( this, &UHoudiniAssetInput::GetPreserveScaleRatioImage, AtIndex ) ) ) .ColorAndOpacity( FSlateColor::UseForeground() ) ] ] */ ]; } } /** Create a single skeleton widget for the given input object */ void FHoudiniParameterDetails::Helper_CreateSkeletonWidget( UHoudiniAssetInput& InParam, int32 AtIndex, UObject* InputObject, TSharedPtr< FAssetThumbnailPool > AssetThumbnailPool, TSharedRef< SVerticalBox > VerticalBox ) { TWeakObjectPtr MyParam( &InParam ); // Create thumbnail for this skeleton mesh. TSharedPtr< FAssetThumbnail > SkeletalMeshThumbnail = MakeShareable( new FAssetThumbnail( InputObject, 64, 64, AssetThumbnailPool ) ); TSharedPtr< SHorizontalBox > HorizontalBox = NULL; // Drop Target: Skeletal Mesh VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SAssetDropTarget ) .OnIsAssetAcceptableForDrop( SAssetDropTarget::FIsAssetAcceptableForDrop::CreateLambda( [] ( const UObject* InObject ) { return InObject && InObject->IsA< USkeletalMesh >(); } ) ) .OnAssetDropped( SAssetDropTarget::FOnAssetDropped::CreateUObject( &InParam, &UHoudiniAssetInput::OnSkeletalMeshDropped, AtIndex ) ) [ SAssignNew(HorizontalBox, SHorizontalBox) ] ]; // Thumbnail : Skeletal Mesh FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); //FText ParameterTooltip = GetParameterTooltip( &InParam, true ); TSharedPtr< SBorder > SkeletalMeshThumbnailBorder; HorizontalBox->AddSlot().Padding(0.0f, 0.0f, 2.0f, 0.0f).AutoWidth() [ SAssignNew( SkeletalMeshThumbnailBorder, SBorder ) .Padding(5.0f) .OnMouseDoubleClick(FPointerEventHandler::CreateUObject(&InParam, &UHoudiniAssetInput::OnThumbnailDoubleClick, AtIndex)) [ SNew(SBox) .WidthOverride(64) .HeightOverride(64) .ToolTipText(ParameterLabelText) [ SkeletalMeshThumbnail->MakeThumbnailWidget() ] ] ]; SkeletalMeshThumbnailBorder->SetBorderImage( TAttribute< const FSlateBrush * >::Create( TAttribute< const FSlateBrush * >::FGetter::CreateLambda( [ SkeletalMeshThumbnailBorder ]() { if ( SkeletalMeshThumbnailBorder.IsValid() && SkeletalMeshThumbnailBorder->IsHovered() ) return FEditorStyle::GetBrush( "PropertyEditor.AssetThumbnailLight" ); else return FEditorStyle::GetBrush( "PropertyEditor.AssetThumbnailShadow" ); } ) ) ); FText MeshNameText = FText::GetEmpty(); if ( InputObject ) MeshNameText = FText::FromString( InputObject->GetName() ); // ComboBox : Skeletal Mesh TSharedPtr< SComboButton > SkeletalMeshComboButton; TSharedPtr< SHorizontalBox > ButtonBox; HorizontalBox->AddSlot() .FillWidth( 1.0f ) .Padding( 0.0f, 4.0f, 4.0f, 4.0f ) .VAlign( VAlign_Center ) [ SNew( SVerticalBox ) + SVerticalBox::Slot() .HAlign( HAlign_Fill ) [ SAssignNew( ButtonBox, SHorizontalBox ) + SHorizontalBox::Slot() [ SAssignNew(SkeletalMeshComboButton, SComboButton ) .ButtonStyle( FEditorStyle::Get(), "PropertyEditor.AssetComboStyle" ) .ForegroundColor( FEditorStyle::GetColor( "PropertyEditor.AssetName.ColorAndOpacity" ) ) .ContentPadding( 2.0f ) .ButtonContent() [ SNew( STextBlock ) .TextStyle( FEditorStyle::Get(), "PropertyEditor.AssetClass" ) .Font( FEditorStyle::GetFontStyle( FName( TEXT( "PropertyWindow.NormalFont" ) ) ) ) .Text( MeshNameText ) ] ] ] ]; SkeletalMeshComboButton->SetOnGetMenuContent( FOnGetContent::CreateLambda( [ MyParam, AtIndex, SkeletalMeshComboButton ]() { TArray< const UClass * > AllowedClasses; AllowedClasses.Add(USkeletalMesh::StaticClass()); TArray< UFactory * > NewAssetFactories; return PropertyCustomizationHelpers::MakeAssetPickerWithMenu( FAssetData( MyParam->GetInputObject( AtIndex ) ), true, AllowedClasses, NewAssetFactories, FOnShouldFilterAsset(), FOnAssetSelected::CreateLambda( [ MyParam, AtIndex, SkeletalMeshComboButton]( const FAssetData & AssetData ) { if (SkeletalMeshComboButton.IsValid()) { SkeletalMeshComboButton->SetIsOpen(false); UObject * Object = AssetData.GetAsset(); MyParam->OnSkeletalMeshDropped(Object, AtIndex); } } ), FSimpleDelegate::CreateLambda( [](){}) ); } ) ); // Create tooltip. FFormatNamedArguments Args; Args.Add( TEXT("Asset"), MeshNameText ); FText SkeletalMeshTooltip = FText::Format( LOCTEXT("BrowseToSpecificAssetInContentBrowser", "Browse to '{Asset}' in Content Browser"), Args); // Button : Browse Skeletal Mesh ButtonBox->AddSlot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign( VAlign_Center ) [ PropertyCustomizationHelpers::MakeBrowseButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInput::OnStaticMeshBrowse, AtIndex ), TAttribute< FText >( SkeletalMeshTooltip ) ) ]; // ButtonBox : Reset ButtonBox->AddSlot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign( VAlign_Center ) [ SNew( SButton ) .ToolTipText( LOCTEXT( "ResetToBase", "Reset to default skeletal mesh" ) ) .ButtonStyle( FEditorStyle::Get(), "NoBorder" ) .ContentPadding( 0 ) .Visibility( EVisibility::Visible ) .OnClicked( FOnClicked::CreateUObject( &InParam, &UHoudiniAssetInput::OnResetSkeletalMeshClicked, AtIndex ) ) [ SNew( SImage ) .Image( FEditorStyle::GetBrush( "PropertyWindow.DiffersFromDefault" ) ) ] ]; ButtonBox->AddSlot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ PropertyCustomizationHelpers::MakeInsertDeleteDuplicateButton( FExecuteAction::CreateLambda( [ MyParam, AtIndex ](){ MyParam->OnInsertGeo( AtIndex ); } ), FExecuteAction::CreateLambda( [ MyParam, AtIndex ](){ MyParam->OnDeleteGeo( AtIndex ); } ), FExecuteAction::CreateLambda( [ MyParam, AtIndex ](){ MyParam->OnDuplicateGeo( AtIndex ); } ) ) ]; /* // TRANSFORM { TSharedPtr ExpanderArrow; TSharedPtr ExpanderImage; VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SAssignNew( ExpanderArrow, SButton ) .ButtonStyle( FEditorStyle::Get(), "NoBorder" ) .ClickMethod( EButtonClickMethod::MouseDown ) .Visibility( EVisibility::Visible ) .OnClicked( FOnClicked::CreateUObject( &InParam, &UHoudiniAssetInput::OnExpandInputTransform, AtIndex ) ) [ SAssignNew( ExpanderImage, SImage ) .ColorAndOpacity( FSlateColor::UseForeground() ) ] ] + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew( STextBlock ) .Text( LOCTEXT("GeoInputTransform", "Transform Offset" ) ) .ToolTipText( LOCTEXT( "GeoInputTransformTooltip", "Transform offset used for correction before sending the asset to Houdini" ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] ]; // Set delegate for image ExpanderImage->SetImage( TAttribute::Create( TAttribute::FGetter::CreateLambda( [=]() { FName ResourceName; if ( MyParam->TransformUIExpanded[ AtIndex ] ) { if ( ExpanderArrow->IsHovered() ) ResourceName = "TreeArrow_Expanded_Hovered"; else ResourceName = "TreeArrow_Expanded"; } else { if ( ExpanderArrow->IsHovered() ) ResourceName = "TreeArrow_Collapsed_Hovered"; else ResourceName = "TreeArrow_Collapsed"; } return FEditorStyle::GetBrush( ResourceName ); } ) ) ); } if ( InParam.TransformUIExpanded[ AtIndex ] ) { // Position VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew( STextBlock ) .Text( LOCTEXT( "GeoInputTranslate", "T" ) ) .ToolTipText( LOCTEXT( "GeoInputTranslateTooltip", "Translate" ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] + SHorizontalBox::Slot().MaxWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ) [ SNew( SVectorInputBox ) .bColorAxisLabels( true ) .X( TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetPositionX, AtIndex ) ) ) .Y(TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetPositionY, AtIndex ) ) ) .Z(TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetPositionZ, AtIndex ) ) ) .OnXChanged( FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetPositionX, AtIndex ) ) .OnYChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetPositionY, AtIndex ) ) .OnZChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetPositionZ, AtIndex ) ) ] ]; // Rotation VerticalBox->AddSlot().Padding( 0, 2 ).AutoHeight() [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew( STextBlock ) .Text( LOCTEXT( "GeoInputRotate", "R" ) ) .ToolTipText(LOCTEXT("GeoInputRotateTooltip", "Rotate") ) .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))) ] + SHorizontalBox::Slot().MaxWidth(HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH) [ SNew(SRotatorInputBox) .AllowSpin(true) .bColorAxisLabels(true) .Roll(TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetRotationRoll, AtIndex))) .Pitch(TAttribute< TOptional< float> >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetRotationPitch, AtIndex))) .Yaw(TAttribute >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetRotationYaw, AtIndex))) .OnRollChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetRotationRoll, AtIndex)) .OnPitchChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetRotationPitch, AtIndex)) .OnYawChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetRotationYaw, AtIndex)) ] ]; // Scale VerticalBox->AddSlot().Padding(0, 2).AutoHeight() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(1.0f) .VAlign(VAlign_Center) .AutoWidth() [ SNew(STextBlock) .Text(LOCTEXT("GeoInputScale", "S")) .ToolTipText(LOCTEXT("GeoInputScaleTooltip", "Scale")) .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))) ] + SHorizontalBox::Slot().MaxWidth(HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH) [ SNew(SVectorInputBox) .bColorAxisLabels(true) .X(TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetScaleX, AtIndex))) .Y(TAttribute< TOptional< float > >::Create( TAttribute< TOptional< float> >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetScaleY, AtIndex))) .Z(TAttribute< TOptional< float> >::Create( TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetScaleZ, AtIndex))) .OnXChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetScaleX, AtIndex)) .OnYChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetScaleY, AtIndex)) .OnZChanged(FOnFloatValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetScaleZ, AtIndex)) ] ]; } */ } FReply FHoudiniParameterDetails::Helper_OnButtonClickSelectActors( TWeakObjectPtr InParam, FName DetailsPanelName) { return Helper_OnButtonClickSelectActors( InParam, DetailsPanelName, false ); } FReply FHoudiniParameterDetails::Helper_OnButtonClickUseSelectionAsBoundSelector( TWeakObjectPtr InParam, FName DetailsPanelName ) { return Helper_OnButtonClickSelectActors(InParam, DetailsPanelName, true); } FReply FHoudiniParameterDetails::Helper_OnButtonClickSelectActors( TWeakObjectPtr InParam, FName DetailsPanelName, const bool& bUseWorldInAsWorldSelector ) { if ( !InParam.IsValid() ) return FReply::Handled(); // There's no undo operation for button. FPropertyEditorModule & PropertyModule = FModuleManager::Get().GetModuleChecked< FPropertyEditorModule >( "PropertyEditor" ); // Locate the details panel. TSharedPtr< IDetailsView > DetailsView = PropertyModule.FindDetailView( DetailsPanelName ); if ( !DetailsView.IsValid() ) return FReply::Handled(); class SLocalDetailsView : public SDetailsViewBase { public: void LockDetailsView() { SDetailsViewBase::bIsLocked = true; } void UnlockDetailsView() { SDetailsViewBase::bIsLocked = false; } }; auto * LocalDetailsView = static_cast< SLocalDetailsView * >( DetailsView.Get() ); if ( !DetailsView->IsLocked() ) { LocalDetailsView->LockDetailsView(); check( DetailsView->IsLocked() ); // Force refresh of details view. InParam->OnParamStateChanged(); // Select the previously chosen input Actors from the World Outliner. GEditor->SelectNone( false, true ); for ( auto & OutlinerMesh : InParam->InputOutlinerMeshArray ) { if ( OutlinerMesh.ActorPtr.IsValid() ) GEditor->SelectActor( OutlinerMesh.ActorPtr.Get(), true, true ); } return FReply::Handled(); } if ( !GEditor || !GEditor->GetSelectedObjects() ) return FReply::Handled(); // If details panel is locked, locate selected actors and check if this component belongs to one of them. FScopedTransaction Transaction( TEXT( HOUDINI_MODULE_RUNTIME ), LOCTEXT( "HoudiniInputChange", "Houdini World Outliner Input Change" ), InParam->PrimaryObject ); InParam->Modify(); InParam->bStaticMeshChanged = true; // Delete all assets and reset the array. // TODO: Make this process a little more efficient. InParam->DisconnectAndDestroyInputAsset(); InParam->InputOutlinerMeshArray.Empty(); USelection * SelectedActors = GEditor->GetSelectedActors(); if ( !SelectedActors ) return FReply::Handled(); if ( bUseWorldInAsWorldSelector ) { // Build an array of the current selection's bounds TArray AllBBox; for (FSelectionIterator It(*SelectedActors); It; ++It) { AActor * CurrentActor = Cast< AActor >(*It); if (!CurrentActor || CurrentActor->IsPendingKill()) continue; AllBBox.Add(CurrentActor->GetComponentsBoundingBox(true)); } UWorld* editorWorld = GEditor->GetEditorWorldContext().World(); for (TActorIterator ActorItr(editorWorld); ActorItr; ++ActorItr) { AActor *CurrentActor = *ActorItr; if (!CurrentActor || CurrentActor->IsPendingKill()) continue; // Check that actor is currently not selected if (CurrentActor->IsSelected()) continue; // Ignore the SkySpheres? FString ClassName = CurrentActor->GetClass() ? CurrentActor->GetClass()->GetName() : FString(); if (ClassName.Contains("BP_Sky_Sphere")) continue; // Don't allow selection of ourselves. Bad things happen if we do. if ( InParam->GetHoudiniAssetComponent() && (CurrentActor == InParam->GetHoudiniAssetComponent()->GetOwner()) ) continue; FBox ActorBounds = CurrentActor->GetComponentsBoundingBox(true); for (auto InBounds : AllBBox) { // Check if both actor's bounds intersects if (!ActorBounds.Intersect(InBounds)) continue; // Current actors intersect with our selection's bounds, update the input with it InParam->UpdateInputOulinerArrayFromActor(CurrentActor, false); break; } } } else { // If the builder brush is selected, first deselect it. for ( FSelectionIterator It(*SelectedActors); It; ++It ) { AActor * Actor = Cast< AActor >(*It); if (!Actor) continue; // Don't allow selection of ourselves. Bad things happen if we do. if (InParam->GetHoudiniAssetComponent() && (Actor == InParam->GetHoudiniAssetComponent()->GetOwner())) continue; InParam->UpdateInputOulinerArrayFromActor(Actor, false); } } InParam->MarkChanged(); AActor* HoudiniAssetActor = InParam->GetHoudiniAssetComponent()->GetOwner(); if ( DetailsView->IsLocked() ) { LocalDetailsView->UnlockDetailsView(); check( !DetailsView->IsLocked() ); TArray< UObject * > DummySelectedActors; DummySelectedActors.Add( HoudiniAssetActor ); // Reset selected actor to itself, force refresh and override the lock. DetailsView->SetObjects( DummySelectedActors, true, true ); } // Reselect the Asset Actor. If we don't do this, our Asset parameters will stop // refreshing and the user will be very confused. It is also resetting the state // of the selection before the input actor selection process was started. GEditor->SelectNone( false, true ); GEditor->SelectActor( HoudiniAssetActor, true, true ); // Update parameter layout. InParam->OnParamStateChanged(); // Start or stop the tick timer to check if the selected Actors have been transformed. if ( InParam->InputOutlinerMeshArray.Num() > 0 ) InParam->StartWorldOutlinerTicking(); else if ( InParam->InputOutlinerMeshArray.Num() <= 0 ) InParam->StopWorldOutlinerTicking(); return FReply::Handled(); } void FHoudiniParameterDetails::CreateWidgetInput( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetInput& InParam ) { TWeakObjectPtr MyParam( &InParam ); // Get thumbnail pool for this builder. IDetailLayoutBuilder & DetailLayoutBuilder = LocalDetailCategoryBuilder.GetParentLayout(); TSharedPtr< FAssetThumbnailPool > AssetThumbnailPool = DetailLayoutBuilder.GetThumbnailPool(); FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); FText ParameterTooltip = GetParameterTooltip( &InParam ); Row.NameWidget.Widget = SNew( STextBlock ) .Text( ParameterLabelText ) .ToolTipText( ParameterTooltip ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ); TSharedRef< SVerticalBox > VerticalBox = SNew( SVerticalBox ); if ( InParam.StringChoiceLabels.Num() > 0 ) { // ComboBox : Input Type TSharedPtr< SComboBox< TSharedPtr< FString > > > ComboBoxInputType; VerticalBox->AddSlot().Padding(2, 2, 5, 2) [ //SNew(SComboBox >) SAssignNew( ComboBoxInputType, SComboBox< TSharedPtr< FString > > ) .OptionsSource( &InParam.StringChoiceLabels ) .InitiallySelectedItem( InParam.StringChoiceLabels[ InParam.ChoiceIndex ] ) .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() || !MyParam.IsValid() ) return; MyParam->OnChoiceChange( NewChoice ); })) [ SNew( STextBlock ) .Text( TAttribute< FText >::Create( TAttribute< FText >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::HandleChoiceContentText ) ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] ]; //ComboBoxInputType->SetSelectedItem( InParam.StringChoiceLabels[ InParam.ChoiceIndex ] ); } // Checkbox : Keep World Transform { TSharedPtr< SCheckBox > CheckBoxTranformType; VerticalBox->AddSlot().Padding(2, 2, 5, 2).AutoHeight() [ SAssignNew(CheckBoxTranformType, SCheckBox) .Content() [ SNew(STextBlock) .Text(LOCTEXT("KeepWorldTransformCheckbox", "Keep World Transform")) .ToolTipText(LOCTEXT("KeepWorldTransformCheckboxTip", "Set this Input's object_merge Transform Type to INTO_THIS_OBJECT. If unchecked, it will be set to NONE.")) .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))) ] .IsChecked(TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedKeepWorldTransform))) .OnCheckStateChanged(FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedKeepWorldTransform)) ]; // the checkbox is read only for geo inputs if ( InParam.ChoiceIndex == EHoudiniAssetInputType::GeometryInput ) CheckBoxTranformType->SetEnabled( false ); // Checkbox is read only if the input is an object-path parameter //if ( bIsObjectPathParameter ) // CheckBoxTranformType->SetEnabled(false); } // Checkbox Pack before merging if ( InParam.ChoiceIndex == EHoudiniAssetInputType::GeometryInput || InParam.ChoiceIndex == EHoudiniAssetInputType::WorldInput ) { TSharedPtr< SCheckBox > CheckBoxPackBeforeMerge; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( CheckBoxPackBeforeMerge, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "PackBeforeMergeCheckbox", "Pack Geometry before merging" ) ) .ToolTipText( LOCTEXT( "PackBeforeMergeCheckboxTip", "Pack each separate piece of geometry before merging them into the input." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedPackBeforeMerge ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedPackBeforeMerge ) ) ]; } // Checkboxes Export LODs and Export Sockets if ( InParam.ChoiceIndex == EHoudiniAssetInputType::GeometryInput || InParam.ChoiceIndex == EHoudiniAssetInputType::WorldInput ) { TSharedPtr< SCheckBox > CheckBoxExportAllLODs; TSharedPtr< SCheckBox > CheckBoxExportSockets; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SAssignNew( CheckBoxExportAllLODs, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "ExportAllLOD", "Export LODs" ) ) .ToolTipText( LOCTEXT( "ExportAllLODCheckboxTip", "If enabled, all LOD Meshes in this static mesh will be sent to Houdini." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportAllLODs ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportAllLODs ) ) ] + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SAssignNew( CheckBoxExportSockets, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "ExportSockets", "Export Sockets" ) ) .ToolTipText( LOCTEXT( "ExportSocketsTip", "If enabled, all Mesh Sockets in this static mesh will be sent to Houdini." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportSockets ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportSockets ) ) ] ]; } if ( InParam.ChoiceIndex == EHoudiniAssetInputType::GeometryInput ) { const int32 NumInputs = InParam.InputObjects.Num(); VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew( STextBlock ) .Text( FText::Format( LOCTEXT( "NumArrayItemsFmt", "{0} elements" ), FText::AsNumber( NumInputs ) ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ PropertyCustomizationHelpers::MakeAddButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInput::OnAddToInputObjects ), LOCTEXT( "AddInput", "Adds a Geometry Input" ), true ) ] + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ PropertyCustomizationHelpers::MakeEmptyButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInput::OnEmptyInputObjects ), LOCTEXT( "EmptyInputs", "Removes All Inputs" ), true ) ] ]; for ( int32 Ix = 0; Ix < NumInputs; Ix++ ) { UObject* InputObject = InParam.GetInputObject( Ix ); Helper_CreateGeometryWidget( InParam, Ix, InputObject, AssetThumbnailPool, VerticalBox ); } } else if ( InParam.ChoiceIndex == EHoudiniAssetInputType::AssetInput ) { // ActorPicker : Houdini Asset FMenuBuilder MenuBuilder = Helper_CreateCustomActorPickerWidget( InParam, LOCTEXT( "AssetInputSelectableActors", "Houdini Asset Actors" ), true ); VerticalBox->AddSlot().Padding(2, 2, 5, 2).AutoHeight() [ MenuBuilder.MakeWidget() ]; } else if ( InParam.ChoiceIndex == EHoudiniAssetInputType::CurveInput ) { // Go through all input curve parameters and build their widgets recursively. for ( TMap< FString, UHoudiniAssetParameter * >::TIterator IterParams( InParam.InputCurveParameters ); IterParams; ++IterParams ) { // Note: Only choice and toggle supported atm if ( UHoudiniAssetParameter * HoudiniAssetParameter = IterParams.Value() ) FHoudiniParameterDetails::CreateWidget( VerticalBox, HoudiniAssetParameter ); } } else if ( InParam.ChoiceIndex == EHoudiniAssetInputType::LandscapeInput ) { // CheckBox : Update Input Landscape Data { TSharedPtr< SCheckBox > CheckBoxUpdateInput; VerticalBox->AddSlot().Padding(2, 2, 5, 2).AutoHeight() [ SAssignNew( CheckBoxUpdateInput, SCheckBox ) .Content() [ SNew(STextBlock) .Text(LOCTEXT("LandscapeUpdateInputCheckbox", "Update Input Landscape Data")) .ToolTipText(LOCTEXT("LandscapeSelectedTooltip", "If enabled, the input landscape's data will be updated instead of creating a new Landscape Actor.")) .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))) ] .IsChecked(TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedUpdateInputLandscape ) ) ) .OnCheckStateChanged(FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedUpdateInputLandscape ) ) ]; // Only enable if we're exporting as HF CheckBoxUpdateInput->SetEnabled(static_cast(InParam.bLandscapeExportAsHeightfield)); } // ActorPicker : Landscape FMenuBuilder MenuBuilder = Helper_CreateCustomActorPickerWidget( InParam, LOCTEXT( "LandscapeInputSelectableActors", "Landscapes" ), true ); VerticalBox->AddSlot().Padding(2, 2, 5, 2).AutoHeight() [ MenuBuilder.MakeWidget() ]; // Checkboxes : Export Landscape As // Heightfield Mesh Points { // Label VerticalBox->AddSlot().Padding(2, 2, 5, 2).AutoHeight() [ SNew(STextBlock) .Text(LOCTEXT("LandscapeExportAs", "Export Landscape As")) .ToolTipText(LOCTEXT("LandscapeExportAsTooltip", "Choose the type of data you want the landscape to be exported to:\n * Heightfield\n * Mesh\n * Points")) .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))) ]; // TSharedPtr ButtonOptionsPanel; VerticalBox->AddSlot().Padding(5, 2, 5, 2).AutoHeight() [ SAssignNew( ButtonOptionsPanel, SUniformGridPanel ) ]; // Heightfield FText HeightfieldTooltip = LOCTEXT("LandscapeExportAsHeightfieldTooltip", "If enabled, the landscape will be exported to Houdini as a heighfield."); ButtonOptionsPanel->AddSlot(0, 0) [ SNew(SCheckBox) .Style(FEditorStyle::Get(), "Property.ToggleButton.Start") .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportAsHeightfield ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportAsHeightfield ) ) .ToolTipText( HeightfieldTooltip ) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2, 2) [ SNew(SImage) .Image(FEditorStyle::GetBrush("ClassIcon.LandscapeComponent")) ] + SHorizontalBox::Slot() .FillWidth(1.0f) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .Padding(2, 2) [ SNew(STextBlock) .Text( LOCTEXT( "LandscapeExportAsHeightfieldCheckbox", "Heightfield" ) ) .ToolTipText( HeightfieldTooltip ) .Font( IDetailLayoutBuilder::GetDetailFont() ) ] ] ]; // Mesh FText MeshTooltip = LOCTEXT("LandscapeExportAsHeightfieldTooltip", "If enabled, the landscape will be exported to Houdini as a heighfield."); ButtonOptionsPanel->AddSlot(1, 0) [ SNew(SCheckBox) .Style(FEditorStyle::Get(), "Property.ToggleButton.Middle") .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportAsMesh ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportAsMesh ) ) .ToolTipText( MeshTooltip ) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2, 2) [ SNew(SImage) .Image(FEditorStyle::GetBrush( "ClassIcon.StaticMeshComponent" ) ) ] + SHorizontalBox::Slot() .FillWidth(1.0f) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .Padding(2, 2) [ SNew( STextBlock ) .Text( LOCTEXT( "LandscapeExportAsMeshCheckbox", "Mesh" ) ) .ToolTipText( MeshTooltip ) .Font( IDetailLayoutBuilder::GetDetailFont() ) ] ] ]; // Points FText PointsTooltip = LOCTEXT( "LandscapeExportAsPointsTooltip", "If enabled, the landscape will be exported to Houdini as points." ); ButtonOptionsPanel->AddSlot(2, 0) [ SNew(SCheckBox) .Style(FEditorStyle::Get(), "Property.ToggleButton.End") .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportAsPoints ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportAsPoints ) ) .ToolTipText( PointsTooltip ) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2, 2) [ SNew(SImage) .Image(FEditorStyle::GetBrush("Mobility.Static")) ] + SHorizontalBox::Slot() .FillWidth(1.0f) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .Padding(2, 2) [ SNew(STextBlock) .Text( LOCTEXT( "LandscapeExportAsPointsCheckbox", "Points" ) ) .ToolTipText( PointsTooltip ) .Font( IDetailLayoutBuilder::GetDetailFont() ) ] ] ]; } // CheckBox : Export selected components only { TSharedPtr< SCheckBox > CheckBoxExportSelected; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( CheckBoxExportSelected, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "LandscapeSelectedCheckbox", "Export Selected Landscape Components Only" ) ) .ToolTipText( LOCTEXT( "LandscapeSelectedTooltip", "If enabled, only the selected Landscape Components will be exported." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont") ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportOnlySelected) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportOnlySelected ) ) ]; } // Checkboxes auto select components { TSharedPtr< SCheckBox > CheckBoxAutoSelectComponents; VerticalBox->AddSlot().Padding(10, 2, 5, 2).AutoHeight() [ SAssignNew( CheckBoxAutoSelectComponents, SCheckBox ) .Content() [ SNew(STextBlock) .Text( LOCTEXT( "AutoSelectComponentCheckbox", "Auto-select component in asset bounds" ) ) .ToolTipText( LOCTEXT( "AutoSelectComponentCheckboxTooltip", "If enabled, when no Landscape components are curremtly selected, the one within the asset's bounding box will be exported." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont") ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedAutoSelectLandscape ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedAutoSelectLandscape) ) ]; // Enable only when exporting selection // or when exporting heighfield (for now) if ( !InParam.bLandscapeExportSelectionOnly ) CheckBoxAutoSelectComponents->SetEnabled( false ); } // The following checkbox are only added when not in heightfield mode if ( !InParam.bLandscapeExportAsHeightfield ) { // Checkbox : Export materials { TSharedPtr< SCheckBox > CheckBoxExportMaterials; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( CheckBoxExportMaterials, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "LandscapeMaterialsCheckbox", "Export Landscape Materials" ) ) .ToolTipText( LOCTEXT( "LandscapeMaterialsTooltip", "If enabled, the landscape materials will be exported with it." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportMaterials ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportMaterials ) ) ]; // Disable when exporting heightfields if ( InParam.bLandscapeExportAsHeightfield ) CheckBoxExportMaterials->SetEnabled( false ); } // Checkbox : Export Tile UVs { TSharedPtr< SCheckBox > CheckBoxExportTileUVs; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( CheckBoxExportTileUVs, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "LandscapeTileUVsCheckbox", "Export Landscape Tile UVs" ) ) .ToolTipText( LOCTEXT( "LandscapeTileUVsTooltip", "If enabled, UVs will be exported separately for each Landscape tile." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked(TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportTileUVs ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportTileUVs ) ) ]; // Disable when exporting heightfields if ( InParam.bLandscapeExportAsHeightfield ) CheckBoxExportTileUVs->SetEnabled( false ); } // Checkbox : Export normalized UVs { TSharedPtr< SCheckBox > CheckBoxExportNormalizedUVs; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( CheckBoxExportNormalizedUVs, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "LandscapeNormalizedUVsCheckbox", "Export Landscape Normalized UVs" ) ) .ToolTipText( LOCTEXT( "LandscapeNormalizedUVsTooltip", "If enabled, landscape UVs will be exported in [0, 1]." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportNormalizedUVs ) ) ) .OnCheckStateChanged(FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportNormalizedUVs ) ) ]; // Disable when exporting heightfields if ( InParam.bLandscapeExportAsHeightfield ) CheckBoxExportNormalizedUVs->SetEnabled( false ); } // Checkbox : Export lighting { TSharedPtr< SCheckBox > CheckBoxExportLighting; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( CheckBoxExportLighting, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "LandscapeLightingCheckbox", "Export Landscape Lighting" ) ) .ToolTipText( LOCTEXT( "LandscapeLightingTooltip", "If enabled, lightmap information will be exported with the landscape." ) ) .Font(FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportLighting ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportLighting ) ) ]; // Disable when exporting heightfields if ( InParam.bLandscapeExportAsHeightfield ) CheckBoxExportLighting->SetEnabled( false ); } /* // UNUSED // Checkbox : Export landscape curves { TSharedPtr< SCheckBox > CheckBoxExportCurves; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( CheckBoxExportCurves, SCheckBox ) .Content() [ SNew( STextBlock ) .Text( LOCTEXT( "LandscapeCurvesCheckbox", "Export Landscape Curves" ) ) .ToolTipText( LOCTEXT( "LandscapeCurvesTooltip", "If enabled, Landscape curves will be exported." ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] .IsChecked( TAttribute< ECheckBoxState >::Create( TAttribute< ECheckBoxState >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsCheckedExportCurves ) ) ) .OnCheckStateChanged( FOnCheckStateChanged::CreateUObject( &InParam, &UHoudiniAssetInput::CheckStateChangedExportCurves ) ) ]; // Disable curves until we have them implemented. if ( CheckBoxExportCurves.IsValid() ) CheckBoxExportCurves->SetEnabled( false ); } */ } // Button : Recommit VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SNew( SHorizontalBox ) +SHorizontalBox::Slot() .Padding( 1, 2, 4, 2 ) [ SNew( SButton ) .VAlign( VAlign_Center ) .HAlign( HAlign_Center ) .Text( LOCTEXT( "LandscapeInputRecommit", "Recommit Landscape" ) ) .ToolTipText( LOCTEXT( "LandscapeInputRecommitTooltip", "Recommits the Landscape to Houdini." ) ) .OnClicked( FOnClicked::CreateUObject( &InParam, &UHoudiniAssetInput::OnButtonClickRecommit ) ) ] ]; } else if ( InParam.ChoiceIndex == EHoudiniAssetInputType::WorldInput ) { // Button : Start Selection / Use current selection + refresh { TSharedPtr< SHorizontalBox > HorizontalBox = NULL; FPropertyEditorModule & PropertyModule = FModuleManager::Get().GetModuleChecked< FPropertyEditorModule >( "PropertyEditor" ); // Get the details view bool bDetailsLocked = false; FName DetailsPanelName = "LevelEditorSelectionDetails"; const IDetailsView* DetailsView = DetailLayoutBuilder.GetDetailsView(); if ( DetailsView ) { DetailsPanelName = DetailsView->GetIdentifier(); if ( DetailsView->IsLocked() ) bDetailsLocked = true; } auto ButtonLabel = LOCTEXT( "WorldInputStartSelection", "Start Selection (Locks Details Panel)" ); if ( bDetailsLocked ) ButtonLabel = LOCTEXT( "WorldInputUseCurrentSelection", "Use Current Selection (Unlocks Details Panel)" ); auto ButtonTooltip = LOCTEXT("WorldInputStartSelectionTooltip", "Locks the Details Panel, and allow you to select object in the world that you can commit to the input afterwards."); if (bDetailsLocked) ButtonTooltip = LOCTEXT("WorldInputUseCurrentSelectionTooltip", "Fill the asset's input with the current selection and unlocks the Details Panel."); FOnClicked OnSelectActors = FOnClicked::CreateStatic( &FHoudiniParameterDetails::Helper_OnButtonClickSelectActors, MyParam, DetailsPanelName ); VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( HorizontalBox, SHorizontalBox ) + SHorizontalBox::Slot() [ SNew( SButton ) .VAlign( VAlign_Center ) .HAlign( HAlign_Center ) .Text( ButtonLabel ) .ToolTipText( ButtonTooltip ) .OnClicked( OnSelectActors ) ] ]; // Button : Use current selection as Bound selector FOnClicked OnSelectBounds = FOnClicked::CreateStatic(&FHoudiniParameterDetails::Helper_OnButtonClickUseSelectionAsBoundSelector, MyParam, DetailsPanelName ); if ( bDetailsLocked ) { ButtonLabel = LOCTEXT("WorldInputUseSelectionAsBoundSelector", "Use Selection as Bound Selector (Unlocks Details Panel)"); ButtonTooltip = LOCTEXT("WorldInputUseSelectionAsBoundSelectorTooltip", "Fill the asset's input with all the actors contains in the current's selection bound, and unlocks the Details Panel."); VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SAssignNew( HorizontalBox, SHorizontalBox ) + SHorizontalBox::Slot() [ SNew( SButton ) .VAlign( VAlign_Center ) .HAlign( HAlign_Center ) .Text( ButtonLabel ) .ToolTipText( ButtonTooltip ) .OnClicked( OnSelectBounds ) ] ]; } } // ActorPicker : World Outliner { FMenuBuilder MenuBuilder = Helper_CreateCustomActorPickerWidget( InParam, LOCTEXT( "WorldInputSelectedActors", "Currently Selected Actors" ), false ); VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ MenuBuilder.MakeWidget() ]; } { // Spline Resolution TSharedPtr< SNumericEntryBox< float > > NumericEntryBox; int32 Idx = 0; VerticalBox->AddSlot().Padding(2, 2, 5, 2).AutoHeight() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() [ SNew(STextBlock) .Text(LOCTEXT("SplineRes", "Unreal Spline Resolution")) .ToolTipText(LOCTEXT("SplineResTooltip", "Resolution used when marshalling the Unreal Splines to HoudiniEngine.\n(step in cm betweem control points)\nSet this to 0 to only export the control points.")) .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))) ] + SHorizontalBox::Slot() .Padding(2.0f, 0.0f) .VAlign(VAlign_Center) [ SNew(SNumericEntryBox< float >) .AllowSpin(true) .Font(FEditorStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))) .MinValue(-1.0f) .MaxValue(1000.0f) .MinSliderValue(0.0f) .MaxSliderValue(1000.0f) .Value(TAttribute< TOptional< float > >::Create(TAttribute< TOptional< float > >::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::GetSplineResolutionValue) ) ) .OnValueChanged(SNumericEntryBox< float >::FOnValueChanged::CreateUObject( &InParam, &UHoudiniAssetInput::SetSplineResolutionValue) ) .IsEnabled(TAttribute::Create(TAttribute::FGetter::CreateUObject( &InParam, &UHoudiniAssetInput::IsSplineResolutionEnabled))) .SliderExponent(1.0f) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(2.0f, 0.0f) .VAlign(VAlign_Center) [ SNew(SButton) .ToolTipText(LOCTEXT("SplineResToDefault", "Reset to default value.")) .ButtonStyle(FEditorStyle::Get(), "NoBorder") .ContentPadding(0) .Visibility(EVisibility::Visible) .OnClicked(FOnClicked::CreateUObject(&InParam, &UHoudiniAssetInput::OnResetSplineResolutionClicked)) [ SNew(SImage) .Image(FEditorStyle::GetBrush("PropertyWindow.DiffersFromDefault")) ] ] ]; } } if ( InParam.ChoiceIndex == EHoudiniAssetInputType::SkeletonInput ) { const int32 NumInputs = InParam.SkeletonInputObjects.Num(); VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ).AutoHeight() [ SNew( SHorizontalBox ) + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ SNew( STextBlock ) .Text( FText::Format( LOCTEXT( "NumArrayItemsFmt", "{0} elements" ), FText::AsNumber( NumInputs ) ) ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) ] + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ PropertyCustomizationHelpers::MakeAddButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInput::OnAddToSkeletonInputObjects ), LOCTEXT( "AddSkeletonInput", "Adds a Skeleton Input" ), true ) ] + SHorizontalBox::Slot() .Padding( 1.0f ) .VAlign( VAlign_Center ) .AutoWidth() [ PropertyCustomizationHelpers::MakeEmptyButton( FSimpleDelegate::CreateUObject( &InParam, &UHoudiniAssetInput::OnEmptySkeletonInputObjects ), LOCTEXT( "EmptySkeletonInputs", "Removes All Inputs" ), true ) ] ]; for ( int32 Ix = 0; Ix < NumInputs; Ix++ ) { UObject* InputObject = InParam.GetSkeletonInputObject( Ix ); Helper_CreateSkeletonWidget( InParam, Ix, InputObject, AssetThumbnailPool, VerticalBox ); } } Row.ValueWidget.Widget = VerticalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); } void FHoudiniParameterDetails::CreateWidgetLabel( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterLabel& InParam ) { TSharedPtr< STextBlock > TextBlock; FText ParameterLabelText = FText::FromString( InParam.GetParameterLabel() ); FText ParameterTooltip = GetParameterTooltip( &InParam ); LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ) [ SAssignNew( TextBlock, STextBlock ) .Text( ParameterLabelText ) .ToolTipText( ParameterTooltip ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) .WrapTextAt( HAPI_UNREAL_DESIRED_ROW_FULL_WIDGET_WIDTH ) .Justification( ETextJustify::Center ) ]; if ( TextBlock.IsValid() ) TextBlock->SetEnabled( !InParam.bIsDisabled ); } void FHoudiniParameterDetails::CreateWidgetString( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterString& InParam ) { FDetailWidgetRow & Row = LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ); // Create the standard parameter name widget. CreateNameWidget( &InParam, Row, true ); TSharedRef< SVerticalBox > VerticalBox = SNew( SVerticalBox ); for ( int32 Idx = 0; Idx < InParam.GetTupleSize(); ++Idx ) { TSharedPtr< SEditableTextBox > EditableTextBox; VerticalBox->AddSlot().Padding( 2, 2, 5, 2 ) [ SAssignNew( EditableTextBox, SEditableTextBox ) .Font( FEditorStyle::GetFontStyle( TEXT( "PropertyWindow.NormalFont" ) ) ) .Text( FText::FromString( InParam.Values[Idx] ) ) .OnTextCommitted( FOnTextCommitted::CreateUObject( &InParam, &UHoudiniAssetParameterString::SetValueCommitted, Idx ) ) ]; if ( EditableTextBox.IsValid() ) EditableTextBox->SetEnabled( !InParam.bIsDisabled ); } Row.ValueWidget.Widget = VerticalBox; Row.ValueWidget.MinDesiredWidth( HAPI_UNREAL_DESIRED_ROW_VALUE_WIDGET_WIDTH ); } void FHoudiniParameterDetails::CreateWidgetSeparator( IDetailCategoryBuilder & LocalDetailCategoryBuilder, class UHoudiniAssetParameterSeparator& InParam ) { TSharedPtr< SSeparator > Separator; LocalDetailCategoryBuilder.AddCustomRow( FText::GetEmpty() ) [ SNew( SVerticalBox ) +SVerticalBox::Slot() .Padding( 0, 0, 5, 0 ) [ SAssignNew( Separator, SSeparator ) .Thickness( 2.0f ) ] ]; if ( Separator.IsValid() ) Separator->SetEnabled( !InParam.bIsDisabled ); } #undef LOCTEXT_NAMESPACE