593 lines
19 KiB
C++
593 lines
19 KiB
C++
/*
|
|
* Copyright (c) <2021> Side Effects Software Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. The name of Side Effects Software may not be used to endorse or
|
|
* promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE "AS IS" AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
|
* NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "HoudiniAssetComponentDetails.h"
|
|
|
|
#include "HoudiniAssetComponent.h"
|
|
#include "HoudiniAsset.h"
|
|
#include "HoudiniEngine.h"
|
|
#include "HoudiniEngineUtils.h"
|
|
#include "HoudiniParameter.h"
|
|
#include "HoudiniHandleComponent.h"
|
|
#include "HoudiniParameterDetails.h"
|
|
#include "HoudiniInput.h"
|
|
#include "HoudiniInputDetails.h"
|
|
#include "HoudiniHandleDetails.h"
|
|
#include "HoudiniOutput.h"
|
|
#include "HoudiniOutputDetails.h"
|
|
#include "Widgets/Input/SButton.h"
|
|
#include "Widgets/Input/SCheckBox.h"
|
|
#include "Widgets/Input/SEditableTextBox.h"
|
|
#include "Widgets/Layout/SSeparator.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Widgets/Images/SImage.h"
|
|
|
|
#include "PropertyCustomizationHelpers.h"
|
|
#include "DetailLayoutBuilder.h"
|
|
#include "DetailCategoryBuilder.h"
|
|
|
|
#define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE
|
|
|
|
TSharedRef< IDetailCustomization >
|
|
FHoudiniAssetComponentDetails::MakeInstance()
|
|
{
|
|
return MakeShareable(new FHoudiniAssetComponentDetails);
|
|
}
|
|
|
|
FHoudiniAssetComponentDetails::FHoudiniAssetComponentDetails()
|
|
{
|
|
OutputDetails = MakeShared<FHoudiniOutputDetails, ESPMode::NotThreadSafe>();
|
|
ParameterDetails = MakeShared<FHoudiniParameterDetails, ESPMode::NotThreadSafe>();
|
|
PDGDetails = MakeShared<FHoudiniPDGDetails, ESPMode::NotThreadSafe>();
|
|
HoudiniEngineDetails = MakeShared<FHoudiniEngineDetails, ESPMode::NotThreadSafe>();
|
|
}
|
|
|
|
|
|
FHoudiniAssetComponentDetails::~FHoudiniAssetComponentDetails()
|
|
{
|
|
// The ramp param's curves are added to root to avoid garbage collection
|
|
// We need to remove those curves from the root when the details classes are destroyed.
|
|
if (ParameterDetails.IsValid())
|
|
{
|
|
FHoudiniParameterDetails* ParamDetailsPtr = ParameterDetails.Get();
|
|
|
|
for (auto& CurFloatRampCurveEditor : ParamDetailsPtr->CreatedFloatCurveEditors)
|
|
{
|
|
if (CurFloatRampCurveEditor.IsValid())
|
|
{
|
|
CurFloatRampCurveEditor->HoudiniFloatRampCurve = nullptr;
|
|
CurFloatRampCurveEditor->SetCurveOwner(nullptr);
|
|
}
|
|
}
|
|
for (auto& CurFloatRampCurve : ParamDetailsPtr->CreatedFloatRampCurves)
|
|
{
|
|
if (!CurFloatRampCurve || CurFloatRampCurve->IsPendingKill())
|
|
continue;
|
|
|
|
CurFloatRampCurve->RemoveFromRoot();
|
|
}
|
|
|
|
for (auto& CurColorRampCurveEditor : ParamDetailsPtr->CreatedColorGradientEditors)
|
|
{
|
|
if (CurColorRampCurveEditor.IsValid())
|
|
{
|
|
CurColorRampCurveEditor->HoudiniColorRampCurve = nullptr;
|
|
CurColorRampCurveEditor->SetCurveOwner(nullptr);
|
|
}
|
|
}
|
|
for (auto& CurColorRampCurve : ParamDetailsPtr->CreatedColorRampCurves)
|
|
{
|
|
if (!CurColorRampCurve || CurColorRampCurve->IsPendingKill())
|
|
continue;
|
|
|
|
CurColorRampCurve->RemoveFromRoot();
|
|
}
|
|
|
|
ParamDetailsPtr->CreatedFloatCurveEditors.Empty();
|
|
ParamDetailsPtr->CreatedColorGradientEditors.Empty();
|
|
ParamDetailsPtr->CreatedFloatRampCurves.Empty();
|
|
ParamDetailsPtr->CreatedColorRampCurves.Empty();
|
|
}
|
|
}
|
|
|
|
void
|
|
FHoudiniAssetComponentDetails::AddIndieLicenseRow(IDetailCategoryBuilder& InCategory)
|
|
{
|
|
FText IndieText =
|
|
FText::FromString(TEXT("Houdini Engine Indie - For Limited Commercial Use Only"));
|
|
|
|
FSlateFontInfo LargeDetailsFont = IDetailLayoutBuilder::GetDetailFontBold();
|
|
LargeDetailsFont.Size += 2;
|
|
|
|
FSlateColor LabelColor = FLinearColor(1.0f, 1.0f, 0.0f, 1.0f);
|
|
|
|
InCategory.AddCustomRow(FText::GetEmpty())
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(IndieText)
|
|
.ToolTipText(IndieText)
|
|
.Font(LargeDetailsFont)
|
|
.Justification(ETextJustify::Center)
|
|
.ColorAndOpacity(LabelColor)
|
|
];
|
|
|
|
InCategory.AddCustomRow(FText::GetEmpty())
|
|
[
|
|
SNew(SVerticalBox)
|
|
+ SVerticalBox::Slot()
|
|
.Padding(0, 0, 5, 0)
|
|
[
|
|
SNew(SSeparator)
|
|
.Thickness(2.0f)
|
|
]
|
|
];
|
|
}
|
|
|
|
|
|
void
|
|
FHoudiniAssetComponentDetails::AddSessionStatusRow(IDetailCategoryBuilder& InCategory)
|
|
{
|
|
FDetailWidgetRow& PDGStatusRow = InCategory.AddCustomRow(FText::GetEmpty())
|
|
.WholeRowContent()
|
|
[
|
|
SNew(SHorizontalBox)
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.Padding(2.0f, 0.0f)
|
|
.VAlign(VAlign_Center)
|
|
.HAlign(HAlign_Center)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text_Lambda([]()
|
|
{
|
|
FString StatusString;
|
|
FLinearColor StatusColor;
|
|
GetSessionStatusAndColor(StatusString, StatusColor);
|
|
return FText::FromString(StatusString);
|
|
})
|
|
.ColorAndOpacity_Lambda([]()
|
|
{
|
|
FString StatusString;
|
|
FLinearColor StatusColor;
|
|
GetSessionStatusAndColor(StatusString, StatusColor);
|
|
return FSlateColor(StatusColor);
|
|
})
|
|
]
|
|
];
|
|
}
|
|
|
|
bool
|
|
FHoudiniAssetComponentDetails::GetSessionStatusAndColor(
|
|
FString& OutStatusString, FLinearColor& OutStatusColor)
|
|
{
|
|
OutStatusString = FString();
|
|
OutStatusColor = FLinearColor::White;
|
|
|
|
const EHoudiniSessionStatus& SessionStatus = FHoudiniEngine::Get().GetSessionStatus();
|
|
|
|
switch (SessionStatus)
|
|
{
|
|
case EHoudiniSessionStatus::NotStarted:
|
|
// Session not initialized yet
|
|
OutStatusString = TEXT("Houdini Engine Session - Not Started");
|
|
OutStatusColor = FLinearColor::White;
|
|
break;
|
|
|
|
case EHoudiniSessionStatus::Connected:
|
|
// Session successfully started
|
|
OutStatusString = TEXT("Houdini Engine Session READY");
|
|
OutStatusColor = FLinearColor::Green;
|
|
break;
|
|
case EHoudiniSessionStatus::Stopped:
|
|
// Session stopped
|
|
OutStatusString = TEXT("Houdini Engine Session STOPPED");
|
|
OutStatusColor = FLinearColor(1.0f, 0.5f, 0.0f);
|
|
break;
|
|
case EHoudiniSessionStatus::Failed:
|
|
// Session failed to be created/connected
|
|
OutStatusString = TEXT("Houdini Engine Session FAILED");
|
|
OutStatusColor = FLinearColor::Red;
|
|
break;
|
|
case EHoudiniSessionStatus::Lost:
|
|
// Session Lost (HARS/Houdini Crash?)
|
|
OutStatusString = TEXT("Houdini Engine Session LOST");
|
|
OutStatusColor = FLinearColor::Red;
|
|
break;
|
|
case EHoudiniSessionStatus::NoLicense:
|
|
// Failed to acquire a license
|
|
OutStatusString = TEXT("Houdini Engine Session FAILED - No License");
|
|
OutStatusColor = FLinearColor::Red;
|
|
break;
|
|
case EHoudiniSessionStatus::None:
|
|
// Session type set to None
|
|
OutStatusString = TEXT("Houdini Engine Session DISABLED");
|
|
OutStatusColor = FLinearColor::White;
|
|
break;
|
|
default:
|
|
case EHoudiniSessionStatus::Invalid:
|
|
OutStatusString = TEXT("Houdini Engine Session INVALID");
|
|
OutStatusColor = FLinearColor::Red;
|
|
break;
|
|
}
|
|
|
|
// Handle a few specific case for active session
|
|
if (SessionStatus == EHoudiniSessionStatus::Connected)
|
|
{
|
|
bool bPaused = !FHoudiniEngine::Get().IsCookingEnabled();
|
|
bool bSSync = FHoudiniEngine::Get().IsSessionSyncEnabled();
|
|
if (bPaused)
|
|
{
|
|
OutStatusString = TEXT("Houdini Engine Session PAUSED");
|
|
OutStatusColor = FLinearColor::Yellow;
|
|
}
|
|
/*
|
|
else if (bSSync)
|
|
{
|
|
OutStatusString = TEXT("Houdini Engine Session Sync READY");
|
|
OutStatusColor = FLinearColor::Blue;
|
|
}
|
|
*/
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
FHoudiniAssetComponentDetails::AddBakeMenu(IDetailCategoryBuilder& InCategory, UHoudiniAssetComponent* HAC)
|
|
{
|
|
FString CategoryName = "Bake";
|
|
InCategory.AddGroup(FName(*CategoryName), FText::FromString(CategoryName), false, false);
|
|
|
|
}
|
|
|
|
void
|
|
FHoudiniAssetComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
{
|
|
// Get all components which are being customized.
|
|
TArray< TWeakObjectPtr< UObject > > ObjectsCustomized;
|
|
DetailBuilder.GetObjectsBeingCustomized(ObjectsCustomized);
|
|
|
|
// Extract the Houdini Asset Component to detail
|
|
for (int32 i = 0; i < ObjectsCustomized.Num(); ++i)
|
|
{
|
|
if (ObjectsCustomized[i].IsValid())
|
|
{
|
|
UObject * Object = ObjectsCustomized[i].Get();
|
|
if (Object)
|
|
{
|
|
UHoudiniAssetComponent * HAC = Cast< UHoudiniAssetComponent >(Object);
|
|
if (HAC && !HAC->IsPendingKill())
|
|
HoudiniAssetComponents.Add(HAC);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if we'll need to add indie license labels
|
|
bool bIsIndieLicense = FHoudiniEngine::Get().IsLicenseIndie();
|
|
|
|
// To handle multiselection parameter edit, we try to group the selected components by their houdini assets
|
|
// TODO? ignore multiselection if all are not the same HDA?
|
|
// TODO do the same for inputs
|
|
TMap<TWeakObjectPtr<UHoudiniAsset>, TArray<TWeakObjectPtr<UHoudiniAssetComponent>>> HoudiniAssetToHACs;
|
|
for (auto HAC : HoudiniAssetComponents)
|
|
{
|
|
TWeakObjectPtr<UHoudiniAsset> HoudiniAsset = HAC->GetHoudiniAsset();
|
|
if (!HoudiniAsset.IsValid())
|
|
continue;
|
|
|
|
TArray<TWeakObjectPtr<UHoudiniAssetComponent>>& ValueRef = HoudiniAssetToHACs.FindOrAdd(HoudiniAsset);
|
|
ValueRef.Add(HAC);
|
|
}
|
|
|
|
for (auto Iter : HoudiniAssetToHACs)
|
|
{
|
|
TArray<TWeakObjectPtr<UHoudiniAssetComponent>> HACs = Iter.Value;
|
|
if (HACs.Num() < 1)
|
|
continue;
|
|
|
|
TWeakObjectPtr<UHoudiniAssetComponent> MainComponent = HACs[0];
|
|
if (!MainComponent.IsValid())
|
|
continue;
|
|
|
|
// If we have selected more than one component that have different HDAs,
|
|
// we'll want to separate the param/input/output category for each HDA
|
|
FString MultiSelectionIdentifier = FString();
|
|
if (HoudiniAssetToHACs.Num() > 1)
|
|
{
|
|
MultiSelectionIdentifier = TEXT("(");
|
|
if (MainComponent->GetHoudiniAsset())
|
|
MultiSelectionIdentifier += MainComponent->GetHoudiniAsset()->GetName();
|
|
MultiSelectionIdentifier += TEXT(")");
|
|
}
|
|
|
|
/*
|
|
// Handled by the UPROPERTIES on the component in v2!
|
|
// Edit the Houdini details category
|
|
IDetailCategoryBuilder & HoudiniAssetCategory =
|
|
DetailBuilder.EditCategory("HoudiniAsset", FText::GetEmpty(), ECategoryPriority::Important);
|
|
*/
|
|
|
|
//
|
|
// 0. HOUDINI ASSET DETAILS
|
|
//
|
|
|
|
{
|
|
FString HoudiniEngineCategoryName = "Houdini Engine";
|
|
HoudiniEngineCategoryName += MultiSelectionIdentifier;
|
|
|
|
// Create Houdini Engine details category
|
|
IDetailCategoryBuilder & HouEngineCategory =
|
|
DetailBuilder.EditCategory(*HoudiniEngineCategoryName, FText::FromString("Houdini Engine"), ECategoryPriority::Important);
|
|
|
|
// If we are running Houdini Engine Indie license, we need to display a special label.
|
|
if (bIsIndieLicense)
|
|
AddIndieLicenseRow(HouEngineCategory);
|
|
|
|
TArray<UHoudiniAssetComponent*> MultiSelectedHACs;
|
|
for (auto& NextHACWeakPtr : HACs)
|
|
{
|
|
if (NextHACWeakPtr.IsValid())
|
|
MultiSelectedHACs.Add(NextHACWeakPtr.Get());
|
|
}
|
|
|
|
HoudiniEngineDetails->CreateWidget(HouEngineCategory, MultiSelectedHACs);
|
|
}
|
|
|
|
//
|
|
// 1. PDG ASSET LINK (if available)
|
|
//
|
|
if (MainComponent->GetPDGAssetLink())
|
|
{
|
|
FString PDGCatName = "HoudiniPDGAssetLink";
|
|
PDGCatName += MultiSelectionIdentifier;
|
|
|
|
// Create the PDG Asset Link details category
|
|
IDetailCategoryBuilder & HouPDGCategory =
|
|
DetailBuilder.EditCategory(*PDGCatName, FText::FromString("Houdini - PDG Asset Link"), ECategoryPriority::Important);
|
|
|
|
// If we are running Houdini Engine Indie license, we need to display a special label.
|
|
if (bIsIndieLicense)
|
|
AddIndieLicenseRow(HouPDGCategory);
|
|
|
|
// TODO: Handle multi selection of outputs like params/inputs?
|
|
|
|
|
|
PDGDetails->CreateWidget(HouPDGCategory, MainComponent->GetPDGAssetLink()/*, MainComponent*/);
|
|
}
|
|
|
|
|
|
//
|
|
// 2. PARAMETER DETAILS
|
|
//
|
|
|
|
// If we have selected more than one component that have different HDAs,
|
|
// we need to create multiple categories one for each different HDA
|
|
FString ParamCatName = "HoudiniParameters";
|
|
ParamCatName += MultiSelectionIdentifier;
|
|
|
|
// Create the Parameters details category
|
|
IDetailCategoryBuilder & HouParameterCategory =
|
|
DetailBuilder.EditCategory(*ParamCatName, FText::GetEmpty(), ECategoryPriority::Important);
|
|
|
|
// If we are running Houdini Engine Indie license, we need to display a special label.
|
|
if(bIsIndieLicense)
|
|
AddIndieLicenseRow(HouParameterCategory);
|
|
|
|
// Iterate through the component's parameters
|
|
for (int32 ParamIdx = 0; ParamIdx < MainComponent->GetNumParameters(); ParamIdx++)
|
|
{
|
|
// We only want to create root parameters here, they will recursively create child parameters.
|
|
UHoudiniParameter* CurrentParam = MainComponent->GetParameterAt(ParamIdx);
|
|
if (!CurrentParam || CurrentParam->IsPendingKill())
|
|
continue;
|
|
|
|
// TODO: remove ? unneeded?
|
|
// ensure the parameter is actually owned by a HAC
|
|
/*const TWeakObjectPtr<UHoudiniAssetComponent> Owner = Cast<UHoudiniAssetComponent>(CurrentParam->GetOuter());
|
|
if (!Owner.IsValid())
|
|
continue;*/
|
|
|
|
// Build an array of edited parameter for multi edit
|
|
TArray<UHoudiniParameter*> EditedParams;
|
|
EditedParams.Add(CurrentParam);
|
|
|
|
// Add the corresponding params in the other HAC
|
|
for (int LinkedIdx = 1; LinkedIdx < HACs.Num(); LinkedIdx++)
|
|
{
|
|
UHoudiniParameter* LinkedParam = HACs[LinkedIdx]->GetParameterAt(ParamIdx);
|
|
if (!LinkedParam || LinkedParam->IsPendingKill())
|
|
continue;
|
|
|
|
// Linked params should match the main param! If not try to find one that matches
|
|
if ( !LinkedParam->Matches(*CurrentParam) )
|
|
{
|
|
LinkedParam = MainComponent->FindMatchingParameter(CurrentParam);
|
|
if (!LinkedParam || LinkedParam->IsPendingKill() || LinkedParam->IsChildParameter())
|
|
continue;
|
|
}
|
|
|
|
EditedParams.Add(LinkedParam);
|
|
}
|
|
|
|
ParameterDetails->CreateWidget(HouParameterCategory, EditedParams);
|
|
}
|
|
|
|
/*** HOUDINI HANDLE DETAILS ***/
|
|
|
|
// If we have selected more than one component that have different HDAs,
|
|
// we need to create multiple categories one for each different HDA
|
|
FString HandleCatName = "HoudiniHandles";
|
|
HandleCatName += MultiSelectionIdentifier;
|
|
|
|
// Create the Parameters details category
|
|
IDetailCategoryBuilder & HouHandleCategory =
|
|
DetailBuilder.EditCategory(*HandleCatName, FText::GetEmpty(), ECategoryPriority::Important);
|
|
|
|
// If we are running Houdini Engine Indie license, we need to display a special label.
|
|
if (bIsIndieLicense)
|
|
AddIndieLicenseRow(HouHandleCategory);
|
|
|
|
// Iterate through the component's Houdini handles
|
|
for (int32 HandleIdx = 0; HandleIdx < MainComponent->GetNumHandles(); ++HandleIdx)
|
|
{
|
|
UHoudiniHandleComponent* CurrentHandleComponent = MainComponent->GetHandleComponentAt(HandleIdx);
|
|
|
|
if (!CurrentHandleComponent || CurrentHandleComponent->IsPendingKill())
|
|
continue;
|
|
|
|
TArray<UHoudiniHandleComponent*> EditedHandles;
|
|
EditedHandles.Add(CurrentHandleComponent);
|
|
|
|
// Add the corresponding params in the other HAC
|
|
for (int LinkedIdx = 1; LinkedIdx < HACs.Num(); ++LinkedIdx)
|
|
{
|
|
UHoudiniHandleComponent* LinkedHandle = HACs[LinkedIdx]->GetHandleComponentAt(HandleIdx);
|
|
if (!LinkedHandle || LinkedHandle->IsPendingKill())
|
|
continue;
|
|
|
|
// Linked handles should match the main param, if not try to find one that matches
|
|
if (!LinkedHandle->Matches(*CurrentHandleComponent))
|
|
{
|
|
LinkedHandle = MainComponent->FindMatchingHandle(CurrentHandleComponent);
|
|
if (!LinkedHandle || LinkedHandle->IsPendingKill())
|
|
continue;
|
|
}
|
|
|
|
EditedHandles.Add(LinkedHandle);
|
|
}
|
|
|
|
FHoudiniHandleDetails::CreateWidget(HouHandleCategory, EditedHandles);
|
|
}
|
|
|
|
|
|
//
|
|
// 3. INPUT DETAILS
|
|
//
|
|
|
|
// If we have selected more than one component that have different HDAs,
|
|
// we need to create multiple categories one for each different HDA
|
|
FString InputCatName = "HoudiniInputs";
|
|
InputCatName += MultiSelectionIdentifier;
|
|
|
|
// Create the input details category
|
|
IDetailCategoryBuilder & HouInputCategory =
|
|
DetailBuilder.EditCategory(*InputCatName, FText::GetEmpty(), ECategoryPriority::Important);
|
|
|
|
// If we are running Houdini Engine Indie license, we need to display a special label.
|
|
if (bIsIndieLicense)
|
|
AddIndieLicenseRow(HouInputCategory);
|
|
|
|
// Iterate through the component's inputs
|
|
for (int32 InputIdx = 0; InputIdx < MainComponent->GetNumInputs(); InputIdx++)
|
|
{
|
|
UHoudiniInput* CurrentInput = MainComponent->GetInputAt(InputIdx);
|
|
if (!CurrentInput || CurrentInput->IsPendingKill())
|
|
continue;
|
|
|
|
if (!MainComponent->IsInputTypeSupported(CurrentInput->GetInputType()))
|
|
continue;
|
|
|
|
// Object path parameter inputs are displayed by the ParameterDetails - skip them
|
|
if (CurrentInput->IsObjectPathParameter())
|
|
continue;
|
|
|
|
// Build an array of edited inputs for multi edit
|
|
TArray<UHoudiniInput*> EditedInputs;
|
|
EditedInputs.Add(CurrentInput);
|
|
|
|
// Add the corresponding inputs in the other HAC
|
|
for (int LinkedIdx = 1; LinkedIdx < HACs.Num(); LinkedIdx++)
|
|
{
|
|
UHoudiniInput* LinkedInput = HACs[LinkedIdx]->GetInputAt(InputIdx);
|
|
if (!LinkedInput || LinkedInput->IsPendingKill())
|
|
continue;
|
|
|
|
// Linked params should match the main param! If not try to find one that matches
|
|
if (!LinkedInput->Matches(*CurrentInput))
|
|
{
|
|
LinkedInput = MainComponent->FindMatchingInput(CurrentInput);
|
|
if (!LinkedInput || LinkedInput->IsPendingKill())
|
|
continue;
|
|
}
|
|
|
|
EditedInputs.Add(LinkedInput);
|
|
}
|
|
|
|
FHoudiniInputDetails::CreateWidget(HouInputCategory, EditedInputs);
|
|
}
|
|
|
|
//
|
|
// 4. OUTPUT DETAILS
|
|
//
|
|
|
|
// If we have selected more than one component that have different HDAs,
|
|
// we need to create multiple categories one for each different HDA
|
|
FString OutputCatName = "HoudiniOutputs";
|
|
OutputCatName += MultiSelectionIdentifier;
|
|
|
|
// Create the output details category
|
|
IDetailCategoryBuilder & HouOutputCategory =
|
|
DetailBuilder.EditCategory(*OutputCatName, FText::GetEmpty(), ECategoryPriority::Important);
|
|
|
|
// Iterate through the component's outputs
|
|
for (int32 OutputIdx = 0; OutputIdx < MainComponent->GetNumOutputs(); OutputIdx++)
|
|
{
|
|
UHoudiniOutput* CurrentOutput = MainComponent->GetOutputAt(OutputIdx);
|
|
if (!CurrentOutput || CurrentOutput->IsPendingKill())
|
|
continue;
|
|
|
|
// Build an array of edited inpoutputs for multi edit
|
|
TArray<UHoudiniOutput*> EditedOutputs;
|
|
EditedOutputs.Add(CurrentOutput);
|
|
|
|
// Add the corresponding outputs in the other HAC
|
|
for (int LinkedIdx = 1; LinkedIdx < HACs.Num(); LinkedIdx++)
|
|
{
|
|
UHoudiniOutput* LinkedOutput = HACs[LinkedIdx]->GetOutputAt(OutputIdx);
|
|
if (!LinkedOutput || LinkedOutput->IsPendingKill())
|
|
continue;
|
|
|
|
/*
|
|
// Linked output should match the main output! If not try to find one that matches
|
|
if (!LinkedOutput->Matches(*CurrentOutput))
|
|
{
|
|
LinkedOutput = MainComponent->FindMatchingInput(CurrentOutput);
|
|
if (!LinkedOutput || LinkedOutput->IsPendingKill())
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
EditedOutputs.Add(LinkedOutput);
|
|
}
|
|
|
|
// TODO: Handle multi selection of outputs like params/inputs?
|
|
OutputDetails->CreateWidget(HouOutputCategory, EditedOutputs);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE |