2606 lines
71 KiB
C++
2606 lines
71 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 "HoudiniInput.h"
|
|
|
|
#include "HoudiniEngineRuntime.h"
|
|
#include "HoudiniAssetComponent.h"
|
|
#include "HoudiniOutput.h"
|
|
#include "HoudiniSplineComponent.h"
|
|
#include "HoudiniAsset.h"
|
|
#include "HoudiniGeoPartObject.h"
|
|
#include "HoudiniAssetComponent.h"
|
|
#include "HoudiniAssetBlueprintComponent.h"
|
|
|
|
#include "EngineUtils.h"
|
|
#include "Engine/Brush.h"
|
|
#include "Engine/Engine.h"
|
|
#include "Engine/DataTable.h"
|
|
#include "Model.h"
|
|
#include "Engine/StaticMesh.h"
|
|
#include "Engine/SkeletalMesh.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "FoliageType_InstancedStaticMesh.h"
|
|
|
|
#include "Components/SplineComponent.h"
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "Components/InstancedStaticMeshComponent.h"
|
|
#include "Landscape.h"
|
|
|
|
#if WITH_EDITOR
|
|
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
|
|
#endif
|
|
|
|
//
|
|
UHoudiniInput::UHoudiniInput()
|
|
: Type(EHoudiniInputType::Invalid)
|
|
, PreviousType(EHoudiniInputType::Invalid)
|
|
, AssetNodeId(-1)
|
|
, InputNodeId(-1)
|
|
, InputIndex(0)
|
|
, ParmId(-1)
|
|
, bIsObjectPathParameter(false)
|
|
, bHasChanged(false)
|
|
, bPackBeforeMerge(false)
|
|
, bExportLODs(false)
|
|
, bExportSockets(false)
|
|
, bExportColliders(false)
|
|
, bCookOnCurveChanged(true)
|
|
, bStaticMeshChanged(false)
|
|
, bInputAssetConnectedInHoudini(false)
|
|
, DefaultCurveOffset(0.f)
|
|
, bAddRotAndScaleAttributesOnCurves(false)
|
|
, bIsWorldInputBoundSelector(false)
|
|
, bWorldInputBoundSelectorAutoUpdate(false)
|
|
, UnrealSplineResolution(50.0f)
|
|
, bUpdateInputLandscape(false)
|
|
, LandscapeExportType(EHoudiniLandscapeExportType::Heightfield)
|
|
, bLandscapeExportSelectionOnly(false)
|
|
, bLandscapeAutoSelectComponent(false)
|
|
, bLandscapeExportMaterials(false)
|
|
, bLandscapeExportLighting(false)
|
|
, bLandscapeExportNormalizedUVs(false)
|
|
, bLandscapeExportTileUVs(false)
|
|
{
|
|
Name = TEXT("");
|
|
Label = TEXT("");
|
|
SetFlags(RF_Transactional);
|
|
|
|
// Geometry inputs always have one null default object
|
|
GeometryInputObjects.Add(nullptr);
|
|
|
|
KeepWorldTransform = GetDefaultXTransformType();
|
|
|
|
const UHoudiniRuntimeSettings * HoudiniRuntimeSettings = GetDefault<UHoudiniRuntimeSettings>();
|
|
UnrealSplineResolution = HoudiniRuntimeSettings ? HoudiniRuntimeSettings->MarshallingSplineResolution : 50.0f;
|
|
|
|
bAddRotAndScaleAttributesOnCurves = HoudiniRuntimeSettings ? HoudiniRuntimeSettings->bAddRotAndScaleAttributesOnCurves : false;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::BeginDestroy()
|
|
{
|
|
InvalidateData();
|
|
|
|
// DO NOT MANUALLY DESTROY OUR INPUT OBJECTS!
|
|
// This messes up unreal's Garbage collection and would cause crashes on duplication
|
|
|
|
// Mark all our input objects for destruction
|
|
ForAllHoudiniInputObjectArrays([](TArray<UHoudiniInputObject*>& ObjectArray) {
|
|
ObjectArray.Empty();
|
|
});
|
|
|
|
Super::BeginDestroy();
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void UHoudiniInput::PostEditUndo()
|
|
{
|
|
Super::PostEditUndo();
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(Type);
|
|
if (!InputObjectsPtr)
|
|
return;
|
|
|
|
MarkChanged(true);
|
|
bool bBlueprintStructureChanged = false;
|
|
|
|
if (HasInputTypeChanged())
|
|
{
|
|
// If the input type has changed on undo, previousType becomes new type
|
|
/* This does not work properly, see the corresponding part in FHoudiniInputDetails::AddInputTypeComboBox(...), after Transaction(...
|
|
)
|
|
{
|
|
EHoudiniInputType NewType = PreviousType;
|
|
SetInputType(NewType);
|
|
}
|
|
*/
|
|
EHoudiniInputType Temp = Type;
|
|
Type = PreviousType;
|
|
PreviousType = EHoudiniInputType::Invalid;
|
|
|
|
// If the undo action caused input type changing, treat it as a regular type changing
|
|
// after set up the new and prev types properly
|
|
SetInputType(Temp, bBlueprintStructureChanged);
|
|
}
|
|
else
|
|
{
|
|
if (Type == EHoudiniInputType::Asset)
|
|
{
|
|
// Mark the input asset object as changed, since only undo changing asset will get into here.
|
|
// The input array will be empty when undo adding asset (only support single asset input object in an input now)
|
|
for (auto & NextAssetInputObj : *InputObjectsPtr)
|
|
{
|
|
if (!NextAssetInputObj || NextAssetInputObj->IsPendingKill())
|
|
continue;
|
|
|
|
NextAssetInputObj->MarkChanged(true);
|
|
}
|
|
}
|
|
|
|
|
|
if (Type == EHoudiniInputType::World)
|
|
{
|
|
if (WorldInputObjects.Num() == 0 && InputNodeId >= 0)
|
|
{
|
|
for (auto & NextNodeId : CreatedDataNodeIds)
|
|
{
|
|
if (bCanDeleteHoudiniNodes)
|
|
FHoudiniEngineRuntime::Get().MarkNodeIdAsPendingDelete(NextNodeId, true);
|
|
}
|
|
|
|
CreatedDataNodeIds.Empty();
|
|
|
|
if (bCanDeleteHoudiniNodes)
|
|
FHoudiniEngineRuntime::Get().MarkNodeIdAsPendingDelete(InputNodeId, true);
|
|
InputNodeId = -1;
|
|
}
|
|
}
|
|
|
|
if (Type == EHoudiniInputType::Curve)
|
|
{
|
|
if (PreviousType != EHoudiniInputType::Curve)
|
|
{
|
|
for (auto& NextInput : *GetHoudiniInputObjectArray(Type))
|
|
{
|
|
UHoudiniInputHoudiniSplineComponent* SplineInput = Cast< UHoudiniInputHoudiniSplineComponent>(NextInput);
|
|
if (!SplineInput || SplineInput->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniSplineComponent * HoudiniSplineComponent = SplineInput->GetCurveComponent();
|
|
if (!HoudiniSplineComponent || HoudiniSplineComponent->IsPendingKill())
|
|
continue;
|
|
|
|
USceneComponent* OuterComponent = Cast<USceneComponent>(GetOuter());
|
|
|
|
// Attach the new Houdini spline component to it's owner
|
|
HoudiniSplineComponent->RegisterComponent();
|
|
HoudiniSplineComponent->AttachToComponent(OuterComponent, FAttachmentTransformRules::KeepRelativeTransform);
|
|
HoudiniSplineComponent->SetVisibility(true, true);
|
|
HoudiniSplineComponent->SetHoudiniSplineVisible(true);
|
|
HoudiniSplineComponent->SetHiddenInGame(false, true);
|
|
HoudiniSplineComponent->MarkChanged(true);
|
|
}
|
|
return;
|
|
}
|
|
bool bUndoDelete = false;
|
|
bool bUndoInsert = false;
|
|
bool bUndoDeletedObjArrayEmptied = false;
|
|
|
|
TArray< USceneComponent* > childActor;
|
|
UHoudiniAssetComponent* OuterHAC = Cast<UHoudiniAssetComponent>(GetOuter());
|
|
if (OuterHAC && !OuterHAC->IsPendingKill())
|
|
childActor = OuterHAC->GetAttachChildren();
|
|
|
|
// Undo delete input objects action
|
|
for (int Index = 0; Index < GetNumberOfInputObjects(); ++Index)
|
|
{
|
|
UHoudiniInputObject* InputObject = (*InputObjectsPtr)[Index];
|
|
if (!InputObject || InputObject->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniInputHoudiniSplineComponent * HoudiniSplineInputObject = Cast<UHoudiniInputHoudiniSplineComponent>(InputObject);
|
|
|
|
if (!HoudiniSplineInputObject || HoudiniSplineInputObject->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniSplineComponent* SplineComponent = HoudiniSplineInputObject->GetCurveComponent();
|
|
|
|
if (!SplineComponent || SplineComponent->IsPendingKill())
|
|
continue;
|
|
|
|
// If the last change deleted this curve input, recreate this Houdini Spline input.
|
|
if (!SplineComponent->GetAttachParent())
|
|
{
|
|
bUndoDelete = true;
|
|
|
|
if (!bUndoDeletedObjArrayEmptied)
|
|
LastUndoDeletedInputs.Empty();
|
|
|
|
bUndoDeletedObjArrayEmptied = true;
|
|
|
|
UHoudiniSplineComponent * ReconstructedSpline = NewObject<UHoudiniSplineComponent>(
|
|
GetOuter(), UHoudiniSplineComponent::StaticClass());
|
|
|
|
if (!ReconstructedSpline || ReconstructedSpline->IsPendingKill())
|
|
continue;
|
|
|
|
ReconstructedSpline->SetFlags(RF_Transactional);
|
|
ReconstructedSpline->CopyHoudiniData(SplineComponent);
|
|
|
|
UHoudiniInputObject * ReconstructedInputObject = UHoudiniInputHoudiniSplineComponent::Create(
|
|
ReconstructedSpline, GetOuter(), ReconstructedSpline->GetHoudiniSplineName());
|
|
UHoudiniInputHoudiniSplineComponent *ReconstructedHoudiniSplineInput = (UHoudiniInputHoudiniSplineComponent*)ReconstructedInputObject;
|
|
(*InputObjectsPtr)[Index] = ReconstructedHoudiniSplineInput;
|
|
|
|
ReconstructedSpline->RegisterComponent();
|
|
ReconstructedSpline->SetFlags(RF_Transactional);
|
|
|
|
CreateHoudiniSplineInput(ReconstructedHoudiniSplineInput, true, true, bBlueprintStructureChanged);
|
|
|
|
// Cast the reconstructed Houdini Spline Input to a generic HoudiniInput object.
|
|
UHoudiniInputObject * ReconstructedHoudiniInput = Cast<UHoudiniInputObject>(ReconstructedHoudiniSplineInput);
|
|
|
|
LastUndoDeletedInputs.Add(ReconstructedHoudiniInput);
|
|
// Reset the LastInsertedInputsArray for redoing this undo action.
|
|
}
|
|
}
|
|
|
|
if (bUndoDelete)
|
|
return;
|
|
|
|
// Undo insert input objects action
|
|
for (int Index = 0; Index < LastInsertedInputs.Num(); ++Index)
|
|
{
|
|
bUndoInsert = true;
|
|
UHoudiniInputHoudiniSplineComponent* SplineInputComponent = LastInsertedInputs[Index];
|
|
if (!SplineInputComponent || SplineInputComponent->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniSplineComponent* HoudiniSplineComponent = SplineInputComponent->GetCurveComponent();
|
|
if (!HoudiniSplineComponent || HoudiniSplineComponent->IsPendingKill())
|
|
continue;
|
|
|
|
HoudiniSplineComponent->DestroyComponent();
|
|
}
|
|
|
|
if (bUndoInsert)
|
|
return;
|
|
|
|
for (int Index = 0; Index < LastUndoDeletedInputs.Num(); ++Index)
|
|
{
|
|
UHoudiniInputObject* NextInputObject = LastUndoDeletedInputs[Index];
|
|
|
|
UHoudiniInputHoudiniSplineComponent* SplineInputComponent = Cast<UHoudiniInputHoudiniSplineComponent>(NextInputObject);
|
|
if (!SplineInputComponent || SplineInputComponent->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniSplineComponent* HoudiniSplineComponent = SplineInputComponent->GetCurveComponent();
|
|
if (!HoudiniSplineComponent || SplineInputComponent->IsPendingKill())
|
|
continue;
|
|
|
|
FDetachmentTransformRules DetachTransRules(EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, false);
|
|
HoudiniSplineComponent->DetachFromComponent(DetachTransRules);
|
|
|
|
HoudiniSplineComponent->DestroyComponent();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bBlueprintStructureChanged)
|
|
{
|
|
UHoudiniAssetComponent* OuterHAC = Cast<UHoudiniAssetComponent>(GetOuter());
|
|
FHoudiniEngineRuntimeUtils::MarkBlueprintAsStructurallyModified(OuterHAC);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
FBox
|
|
UHoudiniInput::GetBounds() const
|
|
{
|
|
FBox BoxBounds(ForceInitToZero);
|
|
|
|
switch (Type)
|
|
{
|
|
case EHoudiniInputType::Curve:
|
|
{
|
|
for (int32 Idx = 0; Idx < CurveInputObjects.Num(); ++Idx)
|
|
{
|
|
const UHoudiniInputHoudiniSplineComponent* CurInCurve = Cast<UHoudiniInputHoudiniSplineComponent>(CurveInputObjects[Idx]);
|
|
if (!CurInCurve || CurInCurve->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniSplineComponent* CurCurve = CurInCurve->GetCurveComponent();
|
|
if (!CurCurve || CurCurve->IsPendingKill())
|
|
continue;
|
|
|
|
FBox CurCurveBound(ForceInitToZero);
|
|
for (auto & Trans : CurCurve->CurvePoints)
|
|
{
|
|
CurCurveBound += Trans.GetLocation();
|
|
}
|
|
|
|
UHoudiniAssetComponent* OuterHAC = Cast<UHoudiniAssetComponent>(GetOuter());
|
|
|
|
if (OuterHAC && !OuterHAC->IsPendingKill())
|
|
BoxBounds += CurCurveBound.MoveTo(OuterHAC->GetComponentLocation());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Asset:
|
|
{
|
|
for (int32 Idx = 0; Idx < AssetInputObjects.Num(); ++Idx)
|
|
{
|
|
UHoudiniInputHoudiniAsset* CurInAsset = Cast<UHoudiniInputHoudiniAsset>(AssetInputObjects[Idx]);
|
|
if (!CurInAsset || CurInAsset->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniAssetComponent* CurInHAC = CurInAsset->GetHoudiniAssetComponent();
|
|
if (!CurInHAC || CurInHAC->IsPendingKill())
|
|
continue;
|
|
|
|
BoxBounds += CurInHAC->GetAssetBounds(nullptr, false);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::World:
|
|
{
|
|
for (int32 Idx = 0; Idx < WorldInputObjects.Num(); ++Idx)
|
|
{
|
|
UHoudiniInputActor* CurInActor = Cast<UHoudiniInputActor>(WorldInputObjects[Idx]);
|
|
if (CurInActor && !CurInActor->IsPendingKill())
|
|
{
|
|
AActor* Actor = CurInActor->GetActor();
|
|
if (!Actor || Actor->IsPendingKill())
|
|
continue;
|
|
|
|
FVector Origin, Extent;
|
|
Actor->GetActorBounds(false, Origin, Extent);
|
|
|
|
BoxBounds += FBox::BuildAABB(Origin, Extent);
|
|
}
|
|
else
|
|
{
|
|
// World Input now also support HoudiniAssets
|
|
UHoudiniInputHoudiniAsset* CurInAsset = Cast<UHoudiniInputHoudiniAsset>(WorldInputObjects[Idx]);
|
|
if (CurInAsset && !CurInAsset->IsPendingKill())
|
|
{
|
|
UHoudiniAssetComponent* CurInHAC = CurInAsset->GetHoudiniAssetComponent();
|
|
if (!CurInHAC || CurInHAC->IsPendingKill())
|
|
continue;
|
|
|
|
BoxBounds += CurInHAC->GetAssetBounds(nullptr, false);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Landscape:
|
|
{
|
|
for (int32 Idx = 0; Idx < LandscapeInputObjects.Num(); ++Idx)
|
|
{
|
|
UHoudiniInputLandscape* CurInLandscape = Cast<UHoudiniInputLandscape>(LandscapeInputObjects[Idx]);
|
|
if (!CurInLandscape || CurInLandscape->IsPendingKill())
|
|
continue;
|
|
|
|
ALandscapeProxy* CurLandscape = CurInLandscape->GetLandscapeProxy();
|
|
if (!CurLandscape || CurLandscape->IsPendingKill())
|
|
continue;
|
|
|
|
FVector Origin, Extent;
|
|
CurLandscape->GetActorBounds(false, Origin, Extent);
|
|
|
|
BoxBounds += FBox::BuildAABB(Origin, Extent);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Skeletal:
|
|
case EHoudiniInputType::Invalid:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return BoxBounds;
|
|
}
|
|
|
|
FString
|
|
UHoudiniInput::InputTypeToString(const EHoudiniInputType& InInputType)
|
|
{
|
|
FString InputTypeStr;
|
|
switch (InInputType)
|
|
{
|
|
case EHoudiniInputType::Geometry:
|
|
{
|
|
InputTypeStr = TEXT("Geometry Input");
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Asset:
|
|
{
|
|
InputTypeStr = TEXT("Asset Input");
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Curve:
|
|
{
|
|
InputTypeStr = TEXT("Curve Input");
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Landscape:
|
|
{
|
|
InputTypeStr = TEXT("Landscape Input");
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::World:
|
|
{
|
|
InputTypeStr = TEXT("World Outliner Input");
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Skeletal:
|
|
{
|
|
InputTypeStr = TEXT("Skeletal Mesh Input");
|
|
}
|
|
break;
|
|
}
|
|
|
|
return InputTypeStr;
|
|
}
|
|
|
|
|
|
EHoudiniInputType
|
|
UHoudiniInput::StringToInputType(const FString& InInputTypeString)
|
|
{
|
|
if (InInputTypeString.StartsWith(TEXT("Geometry"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniInputType::Geometry;
|
|
}
|
|
else if (InInputTypeString.StartsWith(TEXT("Asset"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniInputType::Asset;
|
|
}
|
|
else if (InInputTypeString.StartsWith(TEXT("Curve"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniInputType::Curve;
|
|
}
|
|
else if (InInputTypeString.StartsWith(TEXT("Landscape"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniInputType::Landscape;
|
|
}
|
|
else if (InInputTypeString.StartsWith(TEXT("World"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniInputType::World;
|
|
}
|
|
else if (InInputTypeString.StartsWith(TEXT("Skeletal"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniInputType::Skeletal;
|
|
}
|
|
|
|
return EHoudiniInputType::Invalid;
|
|
}
|
|
|
|
|
|
EHoudiniCurveType UHoudiniInput::StringToHoudiniCurveType(const FString& HoudiniCurveTypeString)
|
|
{
|
|
if (HoudiniCurveTypeString.StartsWith(TEXT("Polygon"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniCurveType::Polygon;
|
|
}
|
|
else if (HoudiniCurveTypeString.StartsWith(TEXT("Nurbs"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniCurveType::Nurbs;
|
|
}
|
|
else if (HoudiniCurveTypeString.StartsWith(TEXT("Bezier"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniCurveType::Bezier;
|
|
}
|
|
else if (HoudiniCurveTypeString.StartsWith(TEXT("Points"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniCurveType::Points;
|
|
}
|
|
|
|
return EHoudiniCurveType::Invalid;
|
|
}
|
|
|
|
EHoudiniCurveMethod UHoudiniInput::StringToHoudiniCurveMethod(const FString& HoudiniCurveMethodString)
|
|
{
|
|
if (HoudiniCurveMethodString.StartsWith(TEXT("CVs"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniCurveMethod::CVs;
|
|
}
|
|
else if (HoudiniCurveMethodString.StartsWith(TEXT("Breakpoints"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniCurveMethod::Breakpoints;
|
|
}
|
|
|
|
else if (HoudiniCurveMethodString.StartsWith(TEXT("Freehand"), ESearchCase::IgnoreCase))
|
|
{
|
|
return EHoudiniCurveMethod::Freehand;
|
|
}
|
|
|
|
return EHoudiniCurveMethod::Invalid;
|
|
|
|
}
|
|
|
|
//
|
|
void
|
|
UHoudiniInput::SetSOPInput(const int32& InInputIndex)
|
|
{
|
|
// Set the input index
|
|
InputIndex = InInputIndex;
|
|
|
|
// Invalidate objpath parameter
|
|
ParmId = -1;
|
|
bIsObjectPathParameter = false;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetObjectPathParameter(const int32& InParmId)
|
|
{
|
|
// Set as objpath parameter
|
|
ParmId = InParmId;
|
|
bIsObjectPathParameter = true;
|
|
|
|
// Invalidate the geo input
|
|
InputIndex = -1;
|
|
}
|
|
|
|
EHoudiniXformType
|
|
UHoudiniInput::GetDefaultXTransformType()
|
|
{
|
|
switch (Type)
|
|
{
|
|
case EHoudiniInputType::Curve:
|
|
case EHoudiniInputType::Geometry:
|
|
case EHoudiniInputType::Skeletal:
|
|
return EHoudiniXformType::None;
|
|
case EHoudiniInputType::Asset:
|
|
case EHoudiniInputType::Landscape:
|
|
case EHoudiniInputType::World:
|
|
return EHoudiniXformType::IntoThisObject;
|
|
}
|
|
|
|
return EHoudiniXformType::Auto;
|
|
}
|
|
|
|
bool
|
|
UHoudiniInput::GetKeepWorldTransform() const
|
|
{
|
|
bool bReturn = false;
|
|
switch (KeepWorldTransform)
|
|
{
|
|
case EHoudiniXformType::Auto:
|
|
{
|
|
// Return default values corresponding to the input type:
|
|
if (Type == EHoudiniInputType::Curve
|
|
|| Type == EHoudiniInputType::Geometry
|
|
|| Type == EHoudiniInputType::Skeletal )
|
|
{
|
|
// NONE for Geo, Curve and skeletal mesh IN
|
|
bReturn = false;
|
|
}
|
|
else
|
|
{
|
|
// INTO THIS OBJECT for Asset, Landscape and World IN
|
|
bReturn = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EHoudiniXformType::None:
|
|
{
|
|
bReturn = false;
|
|
break;
|
|
}
|
|
|
|
case EHoudiniXformType::IntoThisObject:
|
|
{
|
|
bReturn = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetKeepWorldTransform(const bool& bInKeepWorldTransform)
|
|
{
|
|
if (bInKeepWorldTransform)
|
|
{
|
|
KeepWorldTransform = EHoudiniXformType::IntoThisObject;
|
|
}
|
|
else
|
|
{
|
|
KeepWorldTransform = EHoudiniXformType::None;
|
|
}
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetInputType(const EHoudiniInputType& InInputType, bool& bOutBlueprintStructureModified)
|
|
{
|
|
if (InInputType == Type)
|
|
return;
|
|
|
|
SetPreviousInputType(Type);
|
|
|
|
// Mark this input as changed
|
|
MarkChanged(true);
|
|
bOutBlueprintStructureModified = true;
|
|
|
|
// Check previous input type
|
|
switch (PreviousType)
|
|
{
|
|
case EHoudiniInputType::Asset:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case EHoudiniInputType::Curve:
|
|
{
|
|
// detach the input curves from the asset component
|
|
if (GetNumberOfInputObjects() > 0)
|
|
{
|
|
for (UHoudiniInputObject * CurrentInput : *GetHoudiniInputObjectArray(Type))
|
|
{
|
|
UHoudiniInputHoudiniSplineComponent * CurrentInputHoudiniSpline = Cast<UHoudiniInputHoudiniSplineComponent>(CurrentInput);
|
|
|
|
if (!CurrentInputHoudiniSpline || CurrentInputHoudiniSpline->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniSplineComponent * HoudiniSplineComponent = CurrentInputHoudiniSpline->GetCurveComponent();
|
|
|
|
|
|
if (!HoudiniSplineComponent || HoudiniSplineComponent->IsPendingKill())
|
|
continue;
|
|
|
|
HoudiniSplineComponent->Modify();
|
|
|
|
const bool bIsArchetype = HoudiniSplineComponent->HasAnyFlags(RF_ArchetypeObject|RF_ClassDefaultObject);
|
|
|
|
if (bIsArchetype)
|
|
{
|
|
#if WITH_EDITOR
|
|
check(HoudiniSplineComponent->IsTemplate());
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bVisible", false);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bHiddenInGame", true);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bIsHoudiniSplineVisible", false);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bHasChanged", true);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bNeedsToTriggerUpdate", true);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
AActor* OwningActor = HoudiniSplineComponent->GetOwner();
|
|
check(OwningActor);
|
|
|
|
FDetachmentTransformRules DetachTransRules(EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, false);
|
|
HoudiniSplineComponent->DetachFromComponent(DetachTransRules);
|
|
HoudiniSplineComponent->SetVisibility(false, true);
|
|
HoudiniSplineComponent->SetHoudiniSplineVisible(false);
|
|
HoudiniSplineComponent->SetHiddenInGame(true, true);
|
|
|
|
// This NodeId shouldn't be invalidated like this. If a spline component
|
|
// or curve input is no longer valid, the input object should be removed from the HoudinInput
|
|
// to get cleaned up properly.
|
|
// HoudiniSplineComponent->SetNodeId(-1);
|
|
HoudiniSplineComponent->MarkChanged(true);
|
|
}
|
|
|
|
bOutBlueprintStructureModified = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EHoudiniInputType::Geometry:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case EHoudiniInputType::Landscape:
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsArray = GetHoudiniInputObjectArray(PreviousType);
|
|
|
|
if (!InputObjectsArray)
|
|
break;
|
|
|
|
for (int32 Idx = 0; Idx < InputObjectsArray->Num(); ++Idx)
|
|
{
|
|
UHoudiniInputObject* InputObj = (*InputObjectsArray)[Idx];
|
|
|
|
if (!InputObj || InputObj->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniInputLandscape* InputLandscape = Cast<UHoudiniInputLandscape>(InputObj);
|
|
|
|
if (!InputLandscape || InputLandscape->IsPendingKill())
|
|
continue;
|
|
|
|
// do something?
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case EHoudiniInputType::Skeletal:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case EHoudiniInputType::World:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
Type = InInputType;
|
|
|
|
// TODO: NOPE, not needed
|
|
// Set keep world transform to default w.r.t to new input type.
|
|
//KeepWorldTransform = GetDefaultXTransformType();
|
|
|
|
// Check current input type
|
|
switch (InInputType)
|
|
{
|
|
case EHoudiniInputType::World:
|
|
case EHoudiniInputType::Asset:
|
|
{
|
|
UHoudiniAssetComponent* OuterHAC = Cast<UHoudiniAssetComponent>(GetOuter());
|
|
if (OuterHAC && !bImportAsReference)
|
|
{
|
|
for (auto& CurrentInput : *GetHoudiniInputObjectArray(Type))
|
|
{
|
|
UHoudiniInputHoudiniAsset* HoudiniAssetInput = Cast<UHoudiniInputHoudiniAsset>(CurrentInput);
|
|
if (!HoudiniAssetInput || HoudiniAssetInput->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniAssetComponent* CurrentHAC = HoudiniAssetInput->GetHoudiniAssetComponent();
|
|
if (!CurrentHAC || CurrentHAC->IsPendingKill())
|
|
continue;
|
|
|
|
CurrentHAC->AddDownstreamHoudiniAsset(OuterHAC);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Curve:
|
|
{
|
|
if (GetNumberOfInputObjects() == 0)
|
|
{
|
|
CreateNewCurveInputObject(bOutBlueprintStructureModified);
|
|
MarkChanged(true);
|
|
}
|
|
else
|
|
{
|
|
for (auto& CurrentInput : *GetHoudiniInputObjectArray(Type))
|
|
{
|
|
UHoudiniInputHoudiniSplineComponent* SplineInput = Cast< UHoudiniInputHoudiniSplineComponent>(CurrentInput);
|
|
if (!IsValid(SplineInput))
|
|
continue;
|
|
|
|
UHoudiniSplineComponent * HoudiniSplineComponent = SplineInput->GetCurveComponent();
|
|
if (!IsValid(HoudiniSplineComponent))
|
|
continue;
|
|
|
|
HoudiniSplineComponent->Modify();
|
|
|
|
const bool bIsArchetype = HoudiniSplineComponent->HasAnyFlags(RF_ArchetypeObject|RF_ClassDefaultObject);
|
|
|
|
if (bIsArchetype)
|
|
{
|
|
#if WITH_EDITOR
|
|
check(HoudiniSplineComponent->IsTemplate());
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bVisible", true);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bHiddenInGame", false);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bIsHoudiniSplineVisible", true);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bHasChanged", true);
|
|
FHoudiniEngineRuntimeUtils::SetTemplatePropertyValue(HoudiniSplineComponent, "bNeedsToTriggerUpdate", true);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Attach the new Houdini spline component to it's owner
|
|
AActor* OwningActor = HoudiniSplineComponent->GetOwner();
|
|
check(OwningActor);
|
|
USceneComponent* OuterComponent = Cast<USceneComponent>(GetOuter());
|
|
HoudiniSplineComponent->RegisterComponent();
|
|
HoudiniSplineComponent->AttachToComponent(OuterComponent, FAttachmentTransformRules::KeepRelativeTransform);
|
|
HoudiniSplineComponent->SetHoudiniSplineVisible(true);
|
|
HoudiniSplineComponent->SetHiddenInGame(false, true);
|
|
HoudiniSplineComponent->SetVisibility(true, true);
|
|
HoudiniSplineComponent->MarkChanged(true);
|
|
|
|
}
|
|
|
|
bOutBlueprintStructureModified = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Geometry:
|
|
{
|
|
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Landscape:
|
|
{
|
|
// Need to do anything on select?
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Skeletal:
|
|
{
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
UHoudiniInputObject*
|
|
UHoudiniInput::CreateNewCurveInputObject(bool& bOutBlueprintStructureModified)
|
|
{
|
|
if (CurveInputObjects.Num() > 0)
|
|
return nullptr;
|
|
|
|
UHoudiniInputHoudiniSplineComponent* NewCurveInputObject = CreateHoudiniSplineInput(nullptr, true, false, bOutBlueprintStructureModified);
|
|
if (!NewCurveInputObject || NewCurveInputObject->IsPendingKill())
|
|
return nullptr;
|
|
|
|
UHoudiniSplineComponent * HoudiniSplineComponent = NewCurveInputObject->GetCurveComponent();
|
|
if (!HoudiniSplineComponent || HoudiniSplineComponent->IsPendingKill())
|
|
return nullptr;
|
|
|
|
// Default Houdini spline component input should not be visible at initialization
|
|
HoudiniSplineComponent->SetVisibility(true, true);
|
|
HoudiniSplineComponent->SetHoudiniSplineVisible(true);
|
|
HoudiniSplineComponent->SetHiddenInGame(false, true);
|
|
|
|
CurveInputObjects.Add(NewCurveInputObject);
|
|
SetInputObjectsNumber(EHoudiniInputType::Curve, 1);
|
|
CurveInputObjects.SetNum(1);
|
|
|
|
return NewCurveInputObject;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::MarkAllInputObjectsChanged(const bool& bInChanged)
|
|
{
|
|
MarkDataUploadNeeded(bInChanged);
|
|
|
|
// Mark all the objects from this input has changed so they upload themselves
|
|
|
|
TSet<EHoudiniInputType> InputTypes;
|
|
InputTypes.Add(Type);
|
|
InputTypes.Add(EHoudiniInputType::Curve);
|
|
|
|
TArray<UHoudiniInputObject*>* NewInputObjects = GetHoudiniInputObjectArray(Type);
|
|
if (NewInputObjects)
|
|
{
|
|
for (auto CurInputObject : *NewInputObjects)
|
|
{
|
|
if (CurInputObject && !CurInputObject->IsPendingKill())
|
|
CurInputObject->MarkChanged(bInChanged);
|
|
}
|
|
}
|
|
}
|
|
|
|
UHoudiniInput * UHoudiniInput::DuplicateAndCopyState(UObject * DestOuter, bool bInCanDeleteHoudiniNodes)
|
|
{
|
|
UHoudiniInput* NewInput = Cast<UHoudiniInput>(StaticDuplicateObject(this, DestOuter));
|
|
|
|
NewInput->CopyStateFrom(this, false, bInCanDeleteHoudiniNodes);
|
|
|
|
return NewInput;
|
|
}
|
|
|
|
void UHoudiniInput::CopyStateFrom(UHoudiniInput* InInput, bool bCopyAllProperties, bool bInCanDeleteHoudiniNodes)
|
|
{
|
|
|
|
// Preserve the current input objects before the copy to ensure we don't lose
|
|
// access to input objects and have them end up in the garbage.
|
|
|
|
TMap<EHoudiniInputType, TArray<UHoudiniInputObject*>*> PrevInputObjectsMap;
|
|
|
|
for(EHoudiniInputType InputType : HoudiniInputTypeList)
|
|
{
|
|
PrevInputObjectsMap.Add(InputType, GetHoudiniInputObjectArray(InputType));
|
|
}
|
|
|
|
// TArray<UHoudiniInputObject*> PrevInputObjects;
|
|
// TArray<UHoudiniInputObject*>* OldToInputObjects = GetHoudiniInputObjectArray(Type);
|
|
// if (OldToInputObjects)
|
|
// PrevInputObjects = *OldToInputObjects;
|
|
|
|
// Copy the state of this UHoudiniInput object.
|
|
if (bCopyAllProperties)
|
|
{
|
|
UEngine::FCopyPropertiesForUnrelatedObjectsParams Params;
|
|
Params.bDoDelta = false; // Perform a deep copy
|
|
Params.bClearReferences = false; // References will be replaced afterwards.
|
|
UEngine::CopyPropertiesForUnrelatedObjects(InInput, this, Params);
|
|
}
|
|
|
|
AssetNodeId = InInput->AssetNodeId;
|
|
InputNodeId = InInput->InputNodeId;
|
|
ParmId = InInput->ParmId;
|
|
bCanDeleteHoudiniNodes = bInCanDeleteHoudiniNodes;
|
|
|
|
//if (bInCanDeleteHoudiniNodes)
|
|
//{
|
|
// // Delete stale data nodes before they get overwritten.
|
|
// TSet<int32> NewNodeIds(InInput->CreatedDataNodeIds);
|
|
// for (int32 NodeId : CreatedDataNodeIds)
|
|
// {
|
|
// if (!NewNodeIds.Contains(NodeId))
|
|
// {
|
|
// FHoudiniEngineRuntime::Get().MarkNodeIdAsPendingDelete(NodeId);
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
CreatedDataNodeIds = InInput->CreatedDataNodeIds;
|
|
|
|
// Important note: At this point the new object may still share objects with InInput.
|
|
// The CopyInputs() will properly duplicate inputs where necessary.
|
|
|
|
// Copy states of Input Objects that correspond to the current type.
|
|
|
|
for(auto& Entry : PrevInputObjectsMap)
|
|
{
|
|
EHoudiniInputType InputType = Entry.Key;
|
|
TArray<UHoudiniInputObject*>* PrevInputObjects = Entry.Value;
|
|
TArray<UHoudiniInputObject*>* ToInputObjects = GetHoudiniInputObjectArray(InputType);
|
|
TArray<UHoudiniInputObject*>* FromInputObjects = InInput->GetHoudiniInputObjectArray(InputType);
|
|
|
|
if (ToInputObjects && FromInputObjects)
|
|
{
|
|
*ToInputObjects = *PrevInputObjects;
|
|
CopyInputs(*ToInputObjects, *FromInputObjects, bInCanDeleteHoudiniNodes);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void UHoudiniInput::SetCanDeleteHoudiniNodes(bool bInCanDeleteNodes)
|
|
{
|
|
bCanDeleteHoudiniNodes = bInCanDeleteNodes;
|
|
for(UHoudiniInputObject* InputObject : GeometryInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->SetCanDeleteHoudiniNodes(bInCanDeleteNodes);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : AssetInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->SetCanDeleteHoudiniNodes(bInCanDeleteNodes);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : CurveInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->SetCanDeleteHoudiniNodes(bInCanDeleteNodes);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : LandscapeInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->SetCanDeleteHoudiniNodes(bInCanDeleteNodes);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : WorldInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->SetCanDeleteHoudiniNodes(bInCanDeleteNodes);
|
|
}
|
|
}
|
|
|
|
void UHoudiniInput::InvalidateData()
|
|
{
|
|
// If valid, mark our input node for deletion
|
|
if (InputNodeId >= 0)
|
|
{
|
|
// .. but if we're an asset input, don't delete the node as InputNodeId
|
|
// is set to the input HDA's node ID!
|
|
if (Type != EHoudiniInputType::Asset)
|
|
{
|
|
if (bCanDeleteHoudiniNodes)
|
|
FHoudiniEngineRuntime::Get().MarkNodeIdAsPendingDelete(InputNodeId, true);
|
|
}
|
|
|
|
InputNodeId = -1;
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : GeometryInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->InvalidateData();
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : AssetInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->InvalidateData();
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : CurveInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->InvalidateData();
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : LandscapeInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
InputObject->InvalidateData();
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : WorldInputObjects)
|
|
{
|
|
if (!InputObject)
|
|
continue;
|
|
|
|
if (InputObject->IsA<UHoudiniInputHoudiniAsset>())
|
|
{
|
|
// When the input object is a HoudiniAssetComponent,
|
|
// we need to be sure that this HDA node id is not in CreatedDataNodeIds
|
|
// We dont want to delete the input HDA node!
|
|
CreatedDataNodeIds.Remove(InputObject->InputNodeId);
|
|
}
|
|
|
|
InputObject->InvalidateData();
|
|
}
|
|
|
|
if (bCanDeleteHoudiniNodes)
|
|
{
|
|
auto& HoudiniEngineRuntime = FHoudiniEngineRuntime::Get();
|
|
for(int32 NodeId : CreatedDataNodeIds)
|
|
{
|
|
HoudiniEngineRuntime.MarkNodeIdAsPendingDelete(NodeId, true);
|
|
}
|
|
}
|
|
|
|
CreatedDataNodeIds.Empty();
|
|
}
|
|
|
|
void UHoudiniInput::CopyInputs(TArray<UHoudiniInputObject*>& ToInputObjects, TArray<UHoudiniInputObject*>& FromInputObjects, bool bInCanDeleteHoudiniNodes)
|
|
{
|
|
TSet<UHoudiniInputObject*> StaleObjects(ToInputObjects);
|
|
|
|
const int32 NumInputs = FromInputObjects.Num();
|
|
UObject* TargetOuter = GetOuter();
|
|
|
|
ToInputObjects.SetNum(NumInputs);
|
|
|
|
|
|
for (int i = 0; i < NumInputs; i++)
|
|
{
|
|
UHoudiniInputObject* FromObject = FromInputObjects[i];
|
|
UHoudiniInputObject* ToObject = ToInputObjects[i];
|
|
|
|
if (!FromObject)
|
|
{
|
|
ToInputObjects[i] = nullptr;
|
|
continue;
|
|
}
|
|
|
|
if (ToObject)
|
|
{
|
|
bool IsValid = true;
|
|
// Is ToInput and FromInput the same or do we have to create a input object?
|
|
IsValid = IsValid && ToObject->Matches(*FromObject);
|
|
IsValid = IsValid && ToObject->GetOuter() == TargetOuter;
|
|
|
|
if (!IsValid)
|
|
{
|
|
ToObject = nullptr;
|
|
}
|
|
}
|
|
|
|
if (ToObject)
|
|
{
|
|
// We have an existing (matching) object. Copy the
|
|
// state from the incoming input.
|
|
StaleObjects.Remove(ToObject);
|
|
ToObject->CopyStateFrom(FromObject, true);
|
|
}
|
|
else
|
|
{
|
|
// We need to create a new input here.
|
|
ToObject = FromObject->DuplicateAndCopyState(TargetOuter);
|
|
ToInputObjects[i] = ToObject;
|
|
}
|
|
|
|
ToObject->SetCanDeleteHoudiniNodes(bInCanDeleteHoudiniNodes);
|
|
}
|
|
|
|
|
|
for (UHoudiniInputObject* StaleInputObject : StaleObjects)
|
|
{
|
|
if (!StaleInputObject)
|
|
continue;
|
|
if (StaleInputObject->GetOuter() == this)
|
|
{
|
|
StaleInputObject->SetCanDeleteHoudiniNodes(bInCanDeleteHoudiniNodes);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UHoudiniInputHoudiniSplineComponent*
|
|
UHoudiniInput::CreateHoudiniSplineInput(UHoudiniInputHoudiniSplineComponent * FromHoudiniSplineInputComponent, const bool & bAttachToparent, const bool & bAppendToInputArray, bool& bOutBlueprintStructureModified)
|
|
{
|
|
UHoudiniInputHoudiniSplineComponent* HoudiniSplineInput = nullptr;
|
|
UHoudiniSplineComponent* HoudiniSplineComponent = nullptr;
|
|
|
|
UObject* OuterObj = GetOuter();
|
|
USceneComponent* OuterComp = Cast<USceneComponent>(GetOuter());
|
|
bool bOuterIsTemplate = (OuterObj && OuterObj->IsTemplate());
|
|
|
|
if (!FromHoudiniSplineInputComponent)
|
|
{
|
|
// NOTE: If we're inside the Blueprint editor, the outer here is going to the be HAC component template.
|
|
check(OuterObj)
|
|
|
|
// Create a default Houdini spline input if a null pointer is passed in.
|
|
FName HoudiniSplineName = MakeUniqueObjectName(OuterComp, UHoudiniSplineComponent::StaticClass(), TEXT("Houdini Spline"));
|
|
|
|
// Create a Houdini Input Object.
|
|
UHoudiniInputObject * NewInputObject = UHoudiniInputHoudiniSplineComponent::Create(
|
|
nullptr, OuterObj, HoudiniSplineName.ToString());
|
|
|
|
if (!NewInputObject || NewInputObject->IsPendingKill())
|
|
return nullptr;
|
|
|
|
HoudiniSplineInput = Cast<UHoudiniInputHoudiniSplineComponent>(NewInputObject);
|
|
if (!HoudiniSplineInput)
|
|
return nullptr;
|
|
|
|
HoudiniSplineComponent = NewObject<UHoudiniSplineComponent>(
|
|
HoudiniSplineInput, UHoudiniSplineComponent::StaticClass());
|
|
|
|
if (!HoudiniSplineComponent || HoudiniSplineComponent->IsPendingKill())
|
|
return nullptr;
|
|
|
|
HoudiniSplineInput->Update(HoudiniSplineComponent);
|
|
|
|
HoudiniSplineComponent->SetHoudiniSplineName(HoudiniSplineName.ToString());
|
|
HoudiniSplineComponent->SetFlags(RF_Transactional);
|
|
|
|
// Set the default position of curve to avoid overlapping.
|
|
HoudiniSplineComponent->SetOffset(DefaultCurveOffset);
|
|
DefaultCurveOffset += 100.f;
|
|
|
|
if (!bOuterIsTemplate)
|
|
{
|
|
HoudiniSplineComponent->RegisterComponent();
|
|
|
|
// Attach the new Houdini spline component to it's owner.
|
|
if (bAttachToparent)
|
|
HoudiniSplineComponent->AttachToComponent(OuterComp, FAttachmentTransformRules::KeepRelativeTransform);
|
|
}
|
|
|
|
//push the new input object to the array for new type.
|
|
if (bAppendToInputArray && Type == EHoudiniInputType::Curve)
|
|
GetHoudiniInputObjectArray(Type)->Add(NewInputObject);
|
|
|
|
#if WITH_EDITOR
|
|
if (bOuterIsTemplate)
|
|
{
|
|
UHoudiniAssetBlueprintComponent* HAB = Cast<UHoudiniAssetBlueprintComponent>(OuterObj);
|
|
if (HAB)
|
|
{
|
|
UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(HAB->GetOuter());
|
|
UBlueprint* Blueprint = Cast<UBlueprint>(BPGC->ClassGeneratedBy);
|
|
if (Blueprint)
|
|
{
|
|
TArray<UActorComponent*> Components;
|
|
Components.Add(HoudiniSplineComponent);
|
|
|
|
USCS_Node* HABNode = HAB->FindSCSNodeForTemplateComponentInClassHierarchy(HAB);
|
|
|
|
// NOTE: FAddComponentsToBlueprintParams was introduced in 4.26 so for the sake of
|
|
// backwards compatibility, manually determine which SCSNode was added instead of
|
|
// relying on Params.OutNodes.
|
|
//FKismetEditorUtilities::FAddComponentsToBlueprintParams Params;
|
|
//Params.OptionalNewRootNode = HABNode;
|
|
const TSet<USCS_Node*> PreviousSCSNodes(Blueprint->SimpleConstructionScript->GetAllNodes());
|
|
FKismetEditorUtilities::AddComponentsToBlueprint(Blueprint, Components, FKismetEditorUtilities::EAddComponentToBPHarvestMode::None, HABNode, false);
|
|
USCS_Node* NewNode = nullptr;
|
|
const TSet<USCS_Node*> CurrentSCSNodes(Blueprint->SimpleConstructionScript->GetAllNodes());
|
|
const TSet<USCS_Node*> AddedNodes = CurrentSCSNodes.Difference(PreviousSCSNodes);
|
|
|
|
if (AddedNodes.Num() > 0)
|
|
{
|
|
// Record Input / SCS node mapping
|
|
USCS_Node* SCSNode = AddedNodes.Array()[0];
|
|
HAB->AddInputObjectMapping(NewInputObject->GetInputGuid(), SCSNode->VariableGuid);
|
|
SCSNode->ComponentTemplate->SetFlags(RF_Public | RF_ArchetypeObject | RF_DefaultSubObject);
|
|
}
|
|
|
|
Blueprint->Modify();
|
|
bOutBlueprintStructureModified = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, get the Houdini spline, and Houdini spline input from the argument.
|
|
HoudiniSplineInput = FromHoudiniSplineInputComponent;
|
|
HoudiniSplineComponent = FromHoudiniSplineInputComponent->GetCurveComponent();
|
|
|
|
if (!HoudiniSplineComponent || HoudiniSplineComponent->IsPendingKill())
|
|
return nullptr;
|
|
|
|
// Attach the new Houdini spline component to it's owner.
|
|
HoudiniSplineComponent->AttachToComponent(OuterComp, FAttachmentTransformRules::KeepRelativeTransform);
|
|
}
|
|
|
|
// Mark the created UHoudiniSplineComponent as an input, and set its InputObject.
|
|
HoudiniSplineComponent->SetIsInputCurve(true);
|
|
|
|
// HoudiniSplineComponent->SetInputObject(HoudiniSplineInput);
|
|
|
|
// Set Houdini Spline Component bHasChanged and bNeedsToTrigerUpdate to true.
|
|
HoudiniSplineComponent->MarkChanged(true);
|
|
|
|
|
|
|
|
return HoudiniSplineInput;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::RemoveSplineFromInputObject(
|
|
UHoudiniInputHoudiniSplineComponent* InHoudiniSplineInputObject,
|
|
bool& bOutBlueprintStructureModified) const
|
|
{
|
|
if (!InHoudiniSplineInputObject)
|
|
return;
|
|
|
|
UObject* OuterObj = GetOuter();
|
|
const bool bOuterIsTemplate = OuterObj && OuterObj->IsTemplate();
|
|
|
|
if (bOuterIsTemplate)
|
|
{
|
|
#if WITH_EDITOR
|
|
// Find the SCS node that corresponds to this input and remove it.
|
|
UHoudiniAssetBlueprintComponent* HAB = Cast<UHoudiniAssetBlueprintComponent>(OuterObj);
|
|
if (HAB)
|
|
{
|
|
const UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(HAB->GetOuter());
|
|
UBlueprint* Blueprint = Cast<UBlueprint>(BPGC->ClassGeneratedBy);
|
|
if (Blueprint)
|
|
{
|
|
USimpleConstructionScript* SCS = Blueprint->SimpleConstructionScript;
|
|
check(SCS);
|
|
FGuid SCSGuid;
|
|
if (HAB->GetInputObjectSCSVariableGuid(InHoudiniSplineInputObject->Guid, SCSGuid))
|
|
{
|
|
// TODO: Move this SCS variable removal code to a reusable utility function. We're
|
|
// going to need to reuse this in a few other places too.
|
|
USCS_Node* SCSNode = SCS->FindSCSNodeByGuid(SCSGuid);
|
|
if (SCSNode)
|
|
{
|
|
SCS->RemoveNodeAndPromoteChildren(SCSNode);
|
|
SCSNode->SetOnNameChanged(FSCSNodeNameChanged());
|
|
bOutBlueprintStructureModified = true;
|
|
HAB->RemoveInputObjectSCSVariableGuid(InHoudiniSplineInputObject->Guid);
|
|
|
|
if (SCSNode->ComponentTemplate != nullptr)
|
|
{
|
|
const FName TemplateName = SCSNode->ComponentTemplate->GetFName();
|
|
const FString RemovedName = SCSNode->GetVariableName().ToString() + TEXT("_REMOVED_") + FGuid::NewGuid().ToString();
|
|
|
|
SCSNode->ComponentTemplate->Modify();
|
|
SCSNode->ComponentTemplate->Rename(*RemovedName, /*NewOuter =*/nullptr, REN_DontCreateRedirectors);
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
auto DestroyArchetypeInstances = [&ArchetypeInstances, &RemovedName](UActorComponent* ComponentTemplate)
|
|
{
|
|
ComponentTemplate->GetArchetypeInstances(ArchetypeInstances);
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
if (!ArchetypeInstance->HasAllFlags(RF_ArchetypeObject | RF_InheritableComponentTemplate))
|
|
{
|
|
CastChecked<UActorComponent>(ArchetypeInstance)->DestroyComponent();
|
|
ArchetypeInstance->Rename(*RemovedName, nullptr, REN_DontCreateRedirectors);
|
|
}
|
|
}
|
|
};
|
|
|
|
DestroyArchetypeInstances(SCSNode->ComponentTemplate);
|
|
|
|
if (Blueprint)
|
|
{
|
|
// Children need to have their inherited component template instance of the component renamed out of the way as well
|
|
TArray<UClass*> ChildrenOfClass;
|
|
GetDerivedClasses(Blueprint->GeneratedClass, ChildrenOfClass);
|
|
|
|
for (UClass* ChildClass : ChildrenOfClass)
|
|
{
|
|
UBlueprintGeneratedClass* BPChildClass = CastChecked<UBlueprintGeneratedClass>(ChildClass);
|
|
|
|
if (UActorComponent* Component = (UActorComponent*)FindObjectWithOuter(BPChildClass, UActorComponent::StaticClass(), TemplateName))
|
|
{
|
|
Component->Modify();
|
|
Component->Rename(*RemovedName, /*NewOuter =*/nullptr, REN_DontCreateRedirectors);
|
|
|
|
DestroyArchetypeInstances(Component);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // if (HAB->GetInputObjectSCSVariableGuid(InHoudiniSplineInputObject->Guid, SCSGuid))
|
|
} // if (Blueprint)
|
|
}
|
|
#endif
|
|
} // if (bIsOuterTemplate)
|
|
else
|
|
{
|
|
UHoudiniSplineComponent* HoudiniSplineComponent = InHoudiniSplineInputObject->GetCurveComponent();
|
|
if (HoudiniSplineComponent)
|
|
{
|
|
// detach the input curves from the asset component
|
|
//FDetachmentTransformRules DetachTransRules(EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, EDetachmentRule::KeepRelative, false);
|
|
//HoudiniSplineComponent->DetachFromComponent(DetachTransRules);
|
|
// Destroy the Houdini Spline Component
|
|
//InputObjectsPtr->RemoveAt(AtIndex);
|
|
HoudiniSplineComponent->DestroyComponent();
|
|
}
|
|
}
|
|
InHoudiniSplineInputObject->Update(nullptr);
|
|
}
|
|
|
|
|
|
TArray<UHoudiniInputObject*>*
|
|
UHoudiniInput::GetHoudiniInputObjectArray(const EHoudiniInputType& InType)
|
|
{
|
|
switch (InType)
|
|
{
|
|
case EHoudiniInputType::Geometry:
|
|
return &GeometryInputObjects;
|
|
|
|
case EHoudiniInputType::Curve:
|
|
return &CurveInputObjects;
|
|
|
|
case EHoudiniInputType::Asset:
|
|
return &AssetInputObjects;
|
|
|
|
case EHoudiniInputType::Landscape:
|
|
return &LandscapeInputObjects;
|
|
|
|
case EHoudiniInputType::World:
|
|
return &WorldInputObjects;
|
|
|
|
case EHoudiniInputType::Skeletal:
|
|
return &SkeletalInputObjects;
|
|
|
|
default:
|
|
case EHoudiniInputType::Invalid:
|
|
return nullptr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TArray<AActor*>*
|
|
UHoudiniInput::GetBoundSelectorObjectArray()
|
|
{
|
|
return &WorldInputBoundSelectorObjects;
|
|
}
|
|
|
|
const TArray<AActor*>*
|
|
UHoudiniInput::GetBoundSelectorObjectArray() const
|
|
{
|
|
return &WorldInputBoundSelectorObjects;
|
|
}
|
|
|
|
const TArray<UHoudiniInputObject*>*
|
|
UHoudiniInput::GetHoudiniInputObjectArray(const EHoudiniInputType& InType) const
|
|
{
|
|
switch (InType)
|
|
{
|
|
case EHoudiniInputType::Geometry:
|
|
return &GeometryInputObjects;
|
|
|
|
case EHoudiniInputType::Curve:
|
|
return &CurveInputObjects;
|
|
|
|
case EHoudiniInputType::Asset:
|
|
return &AssetInputObjects;
|
|
|
|
case EHoudiniInputType::Landscape:
|
|
return &LandscapeInputObjects;
|
|
|
|
case EHoudiniInputType::World:
|
|
return &WorldInputObjects;
|
|
|
|
case EHoudiniInputType::Skeletal:
|
|
return &SkeletalInputObjects;
|
|
|
|
default:
|
|
case EHoudiniInputType::Invalid:
|
|
return nullptr;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UHoudiniInputObject*
|
|
UHoudiniInput::GetHoudiniInputObjectAt(const int32& AtIndex)
|
|
{
|
|
return GetHoudiniInputObjectAt(Type, AtIndex);
|
|
}
|
|
|
|
const UHoudiniInputObject*
|
|
UHoudiniInput::GetHoudiniInputObjectAt(const int32& AtIndex) const
|
|
{
|
|
const TArray<UHoudiniInputObject*>* InputObjectsArray = GetHoudiniInputObjectArray(Type);
|
|
if (!InputObjectsArray || !InputObjectsArray->IsValidIndex(AtIndex))
|
|
return nullptr;
|
|
|
|
return (*InputObjectsArray)[AtIndex];
|
|
}
|
|
|
|
UHoudiniInputObject*
|
|
UHoudiniInput::GetHoudiniInputObjectAt(const EHoudiniInputType& InType, const int32& AtIndex)
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsArray = GetHoudiniInputObjectArray(InType);
|
|
if (!InputObjectsArray || !InputObjectsArray->IsValidIndex(AtIndex))
|
|
return nullptr;
|
|
|
|
return (*InputObjectsArray)[AtIndex];
|
|
}
|
|
|
|
UObject*
|
|
UHoudiniInput::GetInputObjectAt(const int32& AtIndex)
|
|
{
|
|
return GetInputObjectAt(Type, AtIndex);
|
|
}
|
|
|
|
AActor*
|
|
UHoudiniInput::GetBoundSelectorObjectAt(const int32& AtIndex)
|
|
{
|
|
if (!WorldInputBoundSelectorObjects.IsValidIndex(AtIndex))
|
|
return nullptr;
|
|
|
|
return WorldInputBoundSelectorObjects[AtIndex];
|
|
}
|
|
|
|
UObject*
|
|
UHoudiniInput::GetInputObjectAt(const EHoudiniInputType& InType, const int32& AtIndex)
|
|
{
|
|
UHoudiniInputObject* HoudiniInputObject = GetHoudiniInputObjectAt(InType, AtIndex);
|
|
if (!HoudiniInputObject || HoudiniInputObject->IsPendingKill())
|
|
return nullptr;
|
|
|
|
return HoudiniInputObject->GetObject();
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::InsertInputObjectAt(const int32& AtIndex)
|
|
{
|
|
InsertInputObjectAt(Type, AtIndex);
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::InsertInputObjectAt(const EHoudiniInputType& InType, const int32& AtIndex)
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!InputObjectsPtr)
|
|
return;
|
|
|
|
InputObjectsPtr->InsertDefaulted(AtIndex, 1);
|
|
MarkChanged(true);
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::DeleteInputObjectAt(const int32& AtIndex)
|
|
{
|
|
DeleteInputObjectAt(Type, AtIndex);
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::DeleteInputObjectAt(const EHoudiniInputType& InType, const int32& AtIndex)
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!InputObjectsPtr)
|
|
return;
|
|
|
|
if (!InputObjectsPtr->IsValidIndex(AtIndex))
|
|
return;
|
|
|
|
bool bBlueprintStructureModified = false;
|
|
|
|
if (Type == EHoudiniInputType::Asset)
|
|
{
|
|
// ... TODO operations for removing asset input type
|
|
}
|
|
else if (Type == EHoudiniInputType::Curve)
|
|
{
|
|
UHoudiniInputHoudiniSplineComponent* HoudiniSplineInputObject = Cast<UHoudiniInputHoudiniSplineComponent>((*InputObjectsPtr)[AtIndex]);
|
|
if (HoudiniSplineInputObject)
|
|
{
|
|
RemoveSplineFromInputObject(HoudiniSplineInputObject, bBlueprintStructureModified);
|
|
}
|
|
}
|
|
else if (Type == EHoudiniInputType::Geometry)
|
|
{
|
|
// ... TODO operations for removing geometry input type
|
|
}
|
|
else if (Type == EHoudiniInputType::Landscape)
|
|
{
|
|
// ... TODO operations for removing landscape input type
|
|
}
|
|
else if (Type == EHoudiniInputType::Skeletal)
|
|
{
|
|
// ... TODO operations for removing skeletal input type
|
|
}
|
|
else if (Type == EHoudiniInputType::World)
|
|
{
|
|
// ... TODO operations for removing world input type
|
|
}
|
|
else
|
|
{
|
|
// ... invalid input type
|
|
}
|
|
|
|
MarkChanged(true);
|
|
|
|
UHoudiniInputObject* InputObjectToDelete = (*InputObjectsPtr)[AtIndex];
|
|
if (InputObjectToDelete && !InputObjectToDelete->IsPendingKill())
|
|
{
|
|
// Mark the input object's nodes for deletion
|
|
InputObjectToDelete->InvalidateData();
|
|
|
|
// If the deleted object wasnt null, trigger a re upload of the input data
|
|
MarkDataUploadNeeded(true);
|
|
}
|
|
|
|
InputObjectsPtr->RemoveAt(AtIndex);
|
|
|
|
// Delete the merge node when all the input objects are deleted.
|
|
if (InputObjectsPtr->Num() == 0 && InputNodeId >= 0)
|
|
{
|
|
if (bCanDeleteHoudiniNodes)
|
|
FHoudiniEngineRuntime::Get().MarkNodeIdAsPendingDelete(InputNodeId);
|
|
InputNodeId = -1;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
if (bBlueprintStructureModified)
|
|
{
|
|
UActorComponent* Component = Cast<UActorComponent>(GetOuter());
|
|
FHoudiniEngineRuntimeUtils::MarkBlueprintAsStructurallyModified(Component);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::DuplicateInputObjectAt(const int32& AtIndex)
|
|
{
|
|
DuplicateInputObjectAt(Type, AtIndex);
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::DuplicateInputObjectAt(const EHoudiniInputType& InType, const int32& AtIndex)
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!InputObjectsPtr)
|
|
return;
|
|
|
|
if (!InputObjectsPtr->IsValidIndex(AtIndex))
|
|
return;
|
|
|
|
// If the duplicated object is not null, trigger a re upload of the input data
|
|
bool bTriggerUpload = (*InputObjectsPtr)[AtIndex] != nullptr;
|
|
|
|
// TODO: Duplicate the UHoudiniInputObject!!
|
|
UHoudiniInputObject* DuplicateInput = (*InputObjectsPtr)[AtIndex];
|
|
InputObjectsPtr->Insert(DuplicateInput, AtIndex);
|
|
|
|
MarkChanged(true);
|
|
|
|
if (bTriggerUpload)
|
|
MarkDataUploadNeeded(true);
|
|
}
|
|
|
|
int32
|
|
UHoudiniInput::GetNumberOfInputObjects()
|
|
{
|
|
return GetNumberOfInputObjects(Type);
|
|
}
|
|
|
|
int32
|
|
UHoudiniInput::GetNumberOfInputObjects(const EHoudiniInputType& InType)
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!InputObjectsPtr)
|
|
return 0;
|
|
|
|
return InputObjectsPtr->Num();
|
|
}
|
|
|
|
int32
|
|
UHoudiniInput::GetNumberOfInputMeshes()
|
|
{
|
|
return GetNumberOfInputMeshes(Type);
|
|
}
|
|
|
|
int32
|
|
UHoudiniInput::GetNumberOfInputMeshes(const EHoudiniInputType& InType)
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!InputObjectsPtr)
|
|
return 0;
|
|
|
|
// TODO?
|
|
// If geometry input, and we only have one null object, return 0
|
|
int32 Num = InputObjectsPtr->Num();
|
|
|
|
// TODO: Fix BP properly!
|
|
// Special case for SM in BP:
|
|
// we need to add extra input objects store in BlueprintStaticMeshes
|
|
// Same thing for Actor InputObjects!
|
|
for (auto InputObj : *InputObjectsPtr)
|
|
{
|
|
if (!InputObj || InputObj->IsPendingKill())
|
|
continue;
|
|
|
|
UHoudiniInputStaticMesh* InputSM = Cast<UHoudiniInputStaticMesh>(InputObj);
|
|
if (InputSM && !InputSM->IsPendingKill())
|
|
{
|
|
if (InputSM->BlueprintStaticMeshes.Num() > 0)
|
|
{
|
|
Num += (InputSM->BlueprintStaticMeshes.Num() - 1);
|
|
}
|
|
}
|
|
|
|
UHoudiniInputActor* InputActor = Cast<UHoudiniInputActor>(InputObj);
|
|
if (InputActor && !InputActor->IsPendingKill())
|
|
{
|
|
if (InputActor->GetActorComponents().Num() > 0)
|
|
{
|
|
Num += (InputActor->GetActorComponents().Num() - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Num;
|
|
}
|
|
|
|
|
|
int32
|
|
UHoudiniInput::GetNumberOfBoundSelectorObjects() const
|
|
{
|
|
return WorldInputBoundSelectorObjects.Num();
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetInputObjectAt(const int32& AtIndex, UObject* InObject)
|
|
{
|
|
return SetInputObjectAt(Type, AtIndex, InObject);
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetInputObjectAt(const EHoudiniInputType& InType, const int32& AtIndex, UObject* InObject)
|
|
{
|
|
// Start by making sure we have the proper number of input objects
|
|
int32 NumIntObject = GetNumberOfInputObjects(InType);
|
|
if (NumIntObject <= AtIndex)
|
|
{
|
|
// We need to resize the array
|
|
SetInputObjectsNumber(InType, AtIndex + 1);
|
|
}
|
|
|
|
UObject* CurrentInputObject = GetInputObjectAt(InType, AtIndex);
|
|
if (CurrentInputObject == InObject)
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
UHoudiniInputObject* CurrentInputObjectWrapper = GetHoudiniInputObjectAt(InType, AtIndex);
|
|
if (!InObject)
|
|
{
|
|
// We want to set the input object to null
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!ensure(InputObjectsPtr != nullptr && InputObjectsPtr->IsValidIndex(AtIndex)))
|
|
return;
|
|
|
|
if (CurrentInputObjectWrapper)
|
|
{
|
|
// TODO: Check this case
|
|
// Do not destroy the input object manually! this messes up GC
|
|
//CurrentInputObjectWrapper->ConditionalBeginDestroy();
|
|
MarkDataUploadNeeded(true);
|
|
}
|
|
|
|
(*InputObjectsPtr)[AtIndex] = nullptr;
|
|
return;
|
|
}
|
|
|
|
// Get the type of the previous and new input objects
|
|
EHoudiniInputObjectType NewObjectType = InObject ? UHoudiniInputObject::GetInputObjectTypeFromObject(InObject) : EHoudiniInputObjectType::Invalid;
|
|
EHoudiniInputObjectType CurrentObjectType = CurrentInputObjectWrapper ? CurrentInputObjectWrapper->Type : EHoudiniInputObjectType::Invalid;
|
|
|
|
// See if we can reuse the existing InputObject
|
|
if (CurrentObjectType == NewObjectType && NewObjectType != EHoudiniInputObjectType::Invalid)
|
|
{
|
|
// The InputObjectTypes match, we can just update the existing object
|
|
CurrentInputObjectWrapper->Update(InObject);
|
|
CurrentInputObjectWrapper->MarkChanged(true);
|
|
return;
|
|
}
|
|
|
|
// Destroy the existing input object
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!ensure(InputObjectsPtr))
|
|
return;
|
|
|
|
UHoudiniInputObject* NewInputObject = UHoudiniInputObject::CreateTypedInputObject(InObject, this, FString::FromInt(AtIndex + 1));
|
|
if (!ensure(NewInputObject))
|
|
return;
|
|
|
|
// Mark that input object as changed so we know we need to update it
|
|
NewInputObject->MarkChanged(true);
|
|
if (CurrentInputObjectWrapper && !CurrentInputObjectWrapper->IsPendingKill())
|
|
{
|
|
// TODO:
|
|
// For some input type, we may have to copy some of the previous object's property before deleting it
|
|
|
|
// Delete the previous object
|
|
CurrentInputObjectWrapper->MarkPendingKill();
|
|
(*InputObjectsPtr)[AtIndex] = nullptr;
|
|
}
|
|
|
|
// Update the input object array with the newly created input object
|
|
(*InputObjectsPtr)[AtIndex] = NewInputObject;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetInputObjectsNumber(const EHoudiniInputType& InType, const int32& InNewCount)
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(InType);
|
|
if (!InputObjectsPtr)
|
|
return;
|
|
|
|
if (InputObjectsPtr->Num() == InNewCount)
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
if (InNewCount > InputObjectsPtr->Num())
|
|
{
|
|
// Simply add new default InputObjects
|
|
InputObjectsPtr->SetNum(InNewCount);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Check this case!
|
|
// Do not destroy the input object themselves manually,
|
|
// destroy the input object's nodes and reduce the array's size
|
|
for (int32 InObjIdx = InputObjectsPtr->Num() - 1; InObjIdx >= InNewCount; InObjIdx--)
|
|
{
|
|
UHoudiniInputObject* CurrentInputObject = (*InputObjectsPtr)[InObjIdx];
|
|
if (!CurrentInputObject || CurrentInputObject->IsPendingKill())
|
|
continue;
|
|
|
|
if (bCanDeleteHoudiniNodes)
|
|
CurrentInputObject->InvalidateData();
|
|
|
|
/*/
|
|
//FHoudiniInputTranslator::DestroyInput(Inputs[InputIdx]);
|
|
CurrentObject->ConditionalBeginDestroy();
|
|
(*InputObjectsPtr)[InObjIdx] = nullptr;
|
|
*/
|
|
}
|
|
|
|
// Decrease the input object array size
|
|
InputObjectsPtr->SetNum(InNewCount);
|
|
}
|
|
|
|
// Also delete the input's merge node when all the input objects are deleted.
|
|
if (InNewCount == 0 && InputNodeId >= 0)
|
|
{
|
|
if (bCanDeleteHoudiniNodes)
|
|
FHoudiniEngineRuntime::Get().MarkNodeIdAsPendingDelete(InputNodeId, true);
|
|
InputNodeId = -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetBoundSelectorObjectsNumber(const int32& InNewCount)
|
|
{
|
|
if (WorldInputBoundSelectorObjects.Num() == InNewCount)
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
if (InNewCount > WorldInputBoundSelectorObjects.Num())
|
|
{
|
|
// Simply add new default InputObjects
|
|
WorldInputBoundSelectorObjects.SetNum(InNewCount);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
// TODO: Not Needed?
|
|
// Do not destroy the input object themselves manually,
|
|
// destroy the input object's nodes and reduce the array's size
|
|
for (int32 InObjIdx = WorldInputBoundSelectorObjects.Num() - 1; InObjIdx >= InNewCount; InObjIdx--)
|
|
{
|
|
UHoudiniInputObject* CurrentInputObject = WorldInputBoundSelectorObjects[InObjIdx];
|
|
if (!CurrentInputObject)
|
|
continue;
|
|
|
|
CurrentInputObject->MarkInputNodesForDeletion();
|
|
}
|
|
*/
|
|
|
|
// Decrease the input object array size
|
|
WorldInputBoundSelectorObjects.SetNum(InNewCount);
|
|
}
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetBoundSelectorObjectAt(const int32& AtIndex, AActor* InActor)
|
|
{
|
|
// Start by making sure we have the proper number of objects
|
|
int32 NumIntObject = GetNumberOfBoundSelectorObjects();
|
|
if (NumIntObject <= AtIndex)
|
|
{
|
|
// We need to resize the array
|
|
SetBoundSelectorObjectsNumber(AtIndex + 1);
|
|
}
|
|
|
|
AActor* CurrentActor = GetBoundSelectorObjectAt(AtIndex);
|
|
if (CurrentActor == InActor)
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
// Update the array with the new object
|
|
WorldInputBoundSelectorObjects[AtIndex] = InActor;
|
|
}
|
|
|
|
// Helper function indicating what classes are supported by an input type
|
|
TArray<const UClass*>
|
|
UHoudiniInput::GetAllowedClasses(const EHoudiniInputType& InInputType)
|
|
{
|
|
TArray<const UClass*> AllowedClasses;
|
|
switch (InInputType)
|
|
{
|
|
case EHoudiniInputType::Geometry:
|
|
AllowedClasses.Add(UStaticMesh::StaticClass());
|
|
AllowedClasses.Add(USkeletalMesh::StaticClass());
|
|
AllowedClasses.Add(UBlueprint::StaticClass());
|
|
AllowedClasses.Add(UDataTable::StaticClass());
|
|
AllowedClasses.Add(UFoliageType_InstancedStaticMesh::StaticClass());
|
|
break;
|
|
|
|
case EHoudiniInputType::Curve:
|
|
AllowedClasses.Add(USplineComponent::StaticClass());
|
|
AllowedClasses.Add(UHoudiniSplineComponent::StaticClass());
|
|
break;
|
|
|
|
case EHoudiniInputType::Asset:
|
|
AllowedClasses.Add(UHoudiniAssetComponent::StaticClass());
|
|
break;
|
|
|
|
case EHoudiniInputType::Landscape:
|
|
AllowedClasses.Add(ALandscapeProxy::StaticClass());
|
|
break;
|
|
|
|
case EHoudiniInputType::World:
|
|
AllowedClasses.Add(AActor::StaticClass());
|
|
break;
|
|
|
|
case EHoudiniInputType::Skeletal:
|
|
AllowedClasses.Add(USkeletalMesh::StaticClass());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return AllowedClasses;
|
|
}
|
|
|
|
// Helper function indicating if an object is supported by an input type
|
|
bool
|
|
UHoudiniInput::IsObjectAcceptable(const EHoudiniInputType& InInputType, const UObject* InObject)
|
|
{
|
|
TArray<const UClass*> AllowedClasses = GetAllowedClasses(InInputType);
|
|
for (auto CurClass : AllowedClasses)
|
|
{
|
|
if (InObject->IsA(CurClass))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
UHoudiniInput::IsDataUploadNeeded()
|
|
{
|
|
if (bDataUploadNeeded)
|
|
return true;
|
|
|
|
return HasChanged();
|
|
}
|
|
|
|
// Indicates if this input has changed and should be updated
|
|
bool
|
|
UHoudiniInput::HasChanged()
|
|
{
|
|
if (bHasChanged)
|
|
return true;
|
|
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(Type);
|
|
if (!ensure(InputObjectsPtr))
|
|
return false;
|
|
|
|
for (auto CurrentInputObject : (*InputObjectsPtr))
|
|
{
|
|
if (CurrentInputObject && CurrentInputObject->HasChanged())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
UHoudiniInput::IsTransformUploadNeeded()
|
|
{
|
|
TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(Type);
|
|
if (!ensure(InputObjectsPtr))
|
|
return false;
|
|
|
|
for (auto CurrentInputObject : (*InputObjectsPtr))
|
|
{
|
|
if (CurrentInputObject && CurrentInputObject->HasTransformChanged())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Indicates if this input needs to trigger an update
|
|
bool
|
|
UHoudiniInput::NeedsToTriggerUpdate()
|
|
{
|
|
if (bNeedsToTriggerUpdate)
|
|
return true;
|
|
|
|
const TArray<UHoudiniInputObject*>* InputObjectsPtr = GetHoudiniInputObjectArray(Type);
|
|
if (!ensure(InputObjectsPtr))
|
|
return false;
|
|
|
|
for (auto CurrentInputObject : (*InputObjectsPtr))
|
|
{
|
|
if (CurrentInputObject && CurrentInputObject->NeedsToTriggerUpdate())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FString
|
|
UHoudiniInput::GetNodeBaseName() const
|
|
{
|
|
UHoudiniAssetComponent* HAC = Cast<UHoudiniAssetComponent>(GetOuter());
|
|
FString NodeBaseName = HAC ? HAC->GetDisplayName() : TEXT("HoudiniAsset");
|
|
|
|
// Unfortunately CreateInputNode always prefix with input_...
|
|
if (IsObjectPathParameter())
|
|
NodeBaseName += TEXT("_") + GetName();
|
|
else
|
|
NodeBaseName += TEXT("_input") + FString::FromInt(GetInputIndex());
|
|
|
|
return NodeBaseName;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::OnTransformUIExpand(const int32& AtIndex)
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
if (TransformUIExpanded.IsValidIndex(AtIndex))
|
|
{
|
|
TransformUIExpanded[AtIndex] = !TransformUIExpanded[AtIndex];
|
|
}
|
|
else
|
|
{
|
|
// We need to append values to the expanded array
|
|
for (int32 Index = TransformUIExpanded.Num(); Index <= AtIndex; Index++)
|
|
{
|
|
TransformUIExpanded.Add(Index == AtIndex ? true : false);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
UHoudiniInput::IsTransformUIExpanded(const int32& AtIndex)
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
return TransformUIExpanded.IsValidIndex(AtIndex) ? TransformUIExpanded[AtIndex] : false;
|
|
#else
|
|
return false;
|
|
#endif
|
|
};
|
|
|
|
FTransform*
|
|
UHoudiniInput::GetTransformOffset(const int32& AtIndex)
|
|
{
|
|
UHoudiniInputObject* InObject = GetHoudiniInputObjectAt(AtIndex);
|
|
if (InObject)
|
|
return &(InObject->Transform);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const FTransform
|
|
UHoudiniInput::GetTransformOffset(const int32& AtIndex) const
|
|
{
|
|
const UHoudiniInputObject* InObject = GetHoudiniInputObjectAt(AtIndex);
|
|
if (InObject)
|
|
return InObject->Transform;
|
|
|
|
return FTransform::Identity;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetPositionOffsetX(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).GetLocation().X;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetPositionOffsetY(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).GetLocation().Y;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetPositionOffsetZ(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).GetLocation().Z;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetRotationOffsetRoll(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).Rotator().Roll;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetRotationOffsetPitch(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).Rotator().Pitch;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetRotationOffsetYaw(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).Rotator().Yaw;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetScaleOffsetX(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).GetScale3D().X;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetScaleOffsetY(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).GetScale3D().Y;
|
|
}
|
|
|
|
TOptional<float>
|
|
UHoudiniInput::GetScaleOffsetZ(int32 AtIndex) const
|
|
{
|
|
return GetTransformOffset(AtIndex).GetScale3D().Z;
|
|
}
|
|
|
|
bool
|
|
UHoudiniInput::SetTransformOffsetAt(const float& Value, const int32& AtIndex, const int32& PosRotScaleIndex, const int32& XYZIndex)
|
|
{
|
|
FTransform* Transform = GetTransformOffset(AtIndex);
|
|
if (!Transform)
|
|
return false;
|
|
|
|
if (PosRotScaleIndex == 0)
|
|
{
|
|
FVector Position = Transform->GetLocation();
|
|
if (Position[XYZIndex] == Value)
|
|
return false;
|
|
Position[XYZIndex] = Value;
|
|
Transform->SetLocation(Position);
|
|
}
|
|
else if (PosRotScaleIndex == 1)
|
|
{
|
|
FRotator Rotator = Transform->Rotator();
|
|
switch (XYZIndex)
|
|
{
|
|
case 0:
|
|
{
|
|
if (Rotator.Roll == Value)
|
|
return false;
|
|
Rotator.Roll = Value;
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
if (Rotator.Pitch == Value)
|
|
return false;
|
|
Rotator.Pitch = Value;
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
if (Rotator.Yaw == Value)
|
|
return false;
|
|
Rotator.Yaw = Value;
|
|
break;
|
|
}
|
|
}
|
|
Transform->SetRotation(Rotator.Quaternion());
|
|
}
|
|
else if (PosRotScaleIndex == 2)
|
|
{
|
|
FVector Scale = Transform->GetScale3D();
|
|
if (Scale[XYZIndex] == Value)
|
|
return false;
|
|
|
|
Scale[XYZIndex] = Value;
|
|
Transform->SetScale3D(Scale);
|
|
}
|
|
|
|
MarkChanged(true);
|
|
bStaticMeshChanged = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetAddRotAndScaleAttributes(const bool& InValue)
|
|
{
|
|
if (bAddRotAndScaleAttributesOnCurves == InValue)
|
|
return;
|
|
|
|
bAddRotAndScaleAttributesOnCurves = InValue;
|
|
|
|
// Mark all input obj as changed
|
|
MarkAllInputObjectsChanged(true);
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
FText
|
|
UHoudiniInput::GetCurrentSelectionText() const
|
|
{
|
|
FText CurrentSelectionText;
|
|
switch (Type)
|
|
{
|
|
case EHoudiniInputType::Landscape :
|
|
{
|
|
if (LandscapeInputObjects.Num() > 0)
|
|
{
|
|
UHoudiniInputObject* InputObject = LandscapeInputObjects[0];
|
|
|
|
UHoudiniInputLandscape* InputLandscape = Cast<UHoudiniInputLandscape>(InputObject);
|
|
if (!InputLandscape || InputLandscape->IsPendingKill())
|
|
return CurrentSelectionText;
|
|
|
|
ALandscapeProxy* LandscapeProxy = InputLandscape->GetLandscapeProxy();
|
|
if (!LandscapeProxy || LandscapeProxy->IsPendingKill())
|
|
return CurrentSelectionText;
|
|
|
|
CurrentSelectionText = FText::FromString(LandscapeProxy->GetActorLabel());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EHoudiniInputType::Asset :
|
|
{
|
|
if (AssetInputObjects.Num() > 0)
|
|
{
|
|
UHoudiniInputObject* InputObject = AssetInputObjects[0];
|
|
|
|
UHoudiniInputHoudiniAsset* HoudiniAssetInput = Cast<UHoudiniInputHoudiniAsset>(InputObject);
|
|
if (!HoudiniAssetInput || HoudiniAssetInput->IsPendingKill())
|
|
return CurrentSelectionText;
|
|
|
|
UHoudiniAssetComponent* HAC = HoudiniAssetInput->GetHoudiniAssetComponent();
|
|
if (!HAC || HAC->IsPendingKill())
|
|
return CurrentSelectionText;
|
|
|
|
UHoudiniAsset* HoudiniAsset = HAC->GetHoudiniAsset();
|
|
if (!HoudiniAsset || HoudiniAsset->IsPendingKill())
|
|
return CurrentSelectionText;
|
|
|
|
CurrentSelectionText = FText::FromString(HoudiniAsset->GetName());
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return CurrentSelectionText;
|
|
}
|
|
#endif
|
|
|
|
bool
|
|
UHoudiniInput::HasLandscapeExportTypeChanged () const
|
|
{
|
|
if (Type != EHoudiniInputType::Landscape)
|
|
return false;
|
|
|
|
return bLandscapeHasExportTypeChanged;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetHasLandscapeExportTypeChanged(const bool InChanged)
|
|
{
|
|
if (Type != EHoudiniInputType::Landscape)
|
|
return;
|
|
|
|
bLandscapeHasExportTypeChanged = InChanged;
|
|
}
|
|
|
|
bool
|
|
UHoudiniInput::GetUpdateInputLandscape() const
|
|
{
|
|
if (Type != EHoudiniInputType::Landscape)
|
|
return false;
|
|
|
|
return bUpdateInputLandscape;
|
|
}
|
|
|
|
void
|
|
UHoudiniInput::SetUpdateInputLandscape(const bool bInUpdateInputLandcape)
|
|
{
|
|
if (Type != EHoudiniInputType::Landscape)
|
|
return;
|
|
|
|
bUpdateInputLandscape = bInUpdateInputLandcape;
|
|
}
|
|
|
|
|
|
bool
|
|
UHoudiniInput::UpdateWorldSelectionFromBoundSelectors()
|
|
{
|
|
// Dont do anything if we're not a World Input
|
|
if (Type != EHoudiniInputType::World)
|
|
return false;
|
|
|
|
// Build an array of the current selection's bounds
|
|
TArray<FBox> AllBBox;
|
|
for (auto CurrentActor : WorldInputBoundSelectorObjects)
|
|
{
|
|
if (!CurrentActor || CurrentActor->IsPendingKill())
|
|
continue;
|
|
|
|
AllBBox.Add(CurrentActor->GetComponentsBoundingBox(true, true));
|
|
}
|
|
|
|
//
|
|
// Select all actors in our bound selectors bounding boxes
|
|
//
|
|
|
|
// Get our parent component/actor
|
|
USceneComponent* ParentComponent = Cast<USceneComponent>(GetOuter());
|
|
AActor* ParentActor = ParentComponent ? ParentComponent->GetOwner() : nullptr;
|
|
|
|
//UWorld* editorWorld = GEditor->GetEditorWorldContext().World();
|
|
UWorld* MyWorld = GetWorld();
|
|
TArray<AActor*> NewSelectedActors;
|
|
for (TActorIterator<AActor> ActorItr(MyWorld); ActorItr; ++ActorItr)
|
|
{
|
|
AActor *CurrentActor = *ActorItr;
|
|
if (!CurrentActor || CurrentActor->IsPendingKill())
|
|
continue;
|
|
|
|
// Check that actor is currently not selected
|
|
if (WorldInputBoundSelectorObjects.Contains(CurrentActor))
|
|
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 (ParentActor && (CurrentActor == ParentActor))
|
|
continue;
|
|
|
|
// For BrushActors, both the actor and its brush must be valid
|
|
ABrush* BrushActor = Cast<ABrush>(CurrentActor);
|
|
if (BrushActor)
|
|
{
|
|
if (!BrushActor->Brush || BrushActor->Brush->IsPendingKill())
|
|
continue;
|
|
}
|
|
|
|
FBox ActorBounds = CurrentActor->GetComponentsBoundingBox(true);
|
|
for (auto InBounds : AllBBox)
|
|
{
|
|
// Check if both actor's bounds intersects
|
|
if (!ActorBounds.Intersect(InBounds))
|
|
continue;
|
|
|
|
NewSelectedActors.Add(CurrentActor);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return UpdateWorldSelection(NewSelectedActors);
|
|
}
|
|
|
|
bool
|
|
UHoudiniInput::UpdateWorldSelection(const TArray<AActor*>& InNewSelection)
|
|
{
|
|
TArray<AActor*> NewSelectedActors = InNewSelection;
|
|
|
|
// Update our current selection with the new one
|
|
// Keep actors that are still selected, remove the one that are not selected anymore
|
|
bool bHasSelectionChanged = false;
|
|
for (int32 Idx = WorldInputObjects.Num() - 1; Idx >= 0; Idx--)
|
|
{
|
|
UHoudiniInputActor* InputActor = Cast<UHoudiniInputActor>(WorldInputObjects[Idx]);
|
|
AActor* CurActor = InputActor ? InputActor->GetActor() : nullptr;
|
|
|
|
if (CurActor && NewSelectedActors.Contains(CurActor))
|
|
{
|
|
// The actor is still selected, remove it from the new selection
|
|
NewSelectedActors.Remove(CurActor);
|
|
}
|
|
else
|
|
{
|
|
// That actor is no longer selected, remove itr from our current selection
|
|
DeleteInputObjectAt(EHoudiniInputType::World, Idx);
|
|
bHasSelectionChanged = true;
|
|
}
|
|
}
|
|
|
|
if (NewSelectedActors.Num() > 0)
|
|
bHasSelectionChanged = true;
|
|
|
|
// Then add the newly selected Actors
|
|
int32 InputObjectIdx = GetNumberOfInputObjects(EHoudiniInputType::World);
|
|
int32 NewInputObjectNumber = InputObjectIdx + NewSelectedActors.Num();
|
|
SetInputObjectsNumber(EHoudiniInputType::World, NewInputObjectNumber);
|
|
for (const auto& CurActor : NewSelectedActors)
|
|
{
|
|
// Update the input objects from the valid selected actors array
|
|
SetInputObjectAt(InputObjectIdx++, CurActor);
|
|
}
|
|
|
|
MarkChanged(bHasSelectionChanged);
|
|
|
|
return bHasSelectionChanged;
|
|
}
|
|
|
|
|
|
bool
|
|
UHoudiniInput::ContainsInputObject(const UObject* InObject, const EHoudiniInputType& InType) const
|
|
{
|
|
if (!InObject || InObject->IsPendingKill())
|
|
return false;
|
|
|
|
// Returns true if the object is one of our input object for the given type
|
|
const TArray<UHoudiniInputObject*>* ObjectArray = GetHoudiniInputObjectArray(InType);
|
|
if (!ObjectArray)
|
|
return false;
|
|
|
|
for (auto& CurrentInputObject : (*ObjectArray))
|
|
{
|
|
if (!CurrentInputObject || CurrentInputObject->IsPendingKill())
|
|
continue;
|
|
|
|
if (CurrentInputObject->GetObject() == InObject)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UHoudiniInput::ForAllHoudiniInputObjects(TFunctionRef<void(UHoudiniInputObject*)> Fn) const
|
|
{
|
|
for(UHoudiniInputObject* InputObject : GeometryInputObjects)
|
|
{
|
|
Fn(InputObject);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : AssetInputObjects)
|
|
{
|
|
Fn(InputObject);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : CurveInputObjects)
|
|
{
|
|
Fn(InputObject);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : LandscapeInputObjects)
|
|
{
|
|
Fn(InputObject);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : WorldInputObjects)
|
|
{
|
|
Fn(InputObject);
|
|
}
|
|
|
|
for(UHoudiniInputObject* InputObject : SkeletalInputObjects)
|
|
{
|
|
Fn(InputObject);
|
|
}
|
|
}
|
|
|
|
TArray<const TArray<UHoudiniInputObject*>*> UHoudiniInput::GetAllObjectArrays() const
|
|
{
|
|
return { &GeometryInputObjects, &CurveInputObjects, &WorldInputObjects, &SkeletalInputObjects, &LandscapeInputObjects, &AssetInputObjects };
|
|
}
|
|
|
|
TArray<TArray<UHoudiniInputObject*>*> UHoudiniInput::GetAllObjectArrays()
|
|
{
|
|
return { &GeometryInputObjects, &CurveInputObjects, &WorldInputObjects, &SkeletalInputObjects, &LandscapeInputObjects, &AssetInputObjects };
|
|
}
|
|
|
|
void UHoudiniInput::ForAllHoudiniInputObjectArrays(TFunctionRef<void(const TArray<UHoudiniInputObject*>&)> Fn) const
|
|
{
|
|
TArray<const TArray<UHoudiniInputObject*>*> ObjectArrays = GetAllObjectArrays();
|
|
for (const TArray<UHoudiniInputObject*>* ObjectArrayPtr : ObjectArrays)
|
|
{
|
|
if (!ObjectArrayPtr)
|
|
continue;
|
|
Fn(*ObjectArrayPtr);
|
|
}
|
|
}
|
|
|
|
void UHoudiniInput::ForAllHoudiniInputObjectArrays(TFunctionRef<void(TArray<UHoudiniInputObject*>&)> Fn)
|
|
{
|
|
TArray<TArray<UHoudiniInputObject*>*> ObjectArrays = GetAllObjectArrays();
|
|
for (TArray<UHoudiniInputObject*>* ObjectArrayPtr : ObjectArrays)
|
|
{
|
|
if (!ObjectArrayPtr)
|
|
continue;
|
|
Fn(*ObjectArrayPtr);
|
|
}
|
|
}
|
|
|
|
void UHoudiniInput::GetAllHoudiniInputObjects(TArray<UHoudiniInputObject*>& OutObjects) const
|
|
{
|
|
OutObjects.Empty();
|
|
auto AddInputObject = [&OutObjects](UHoudiniInputObject* InputObject)
|
|
{
|
|
if (InputObject)
|
|
OutObjects.Add(InputObject);
|
|
};
|
|
ForAllHoudiniInputObjects(AddInputObject);
|
|
}
|
|
|
|
void UHoudiniInput::ForAllHoudiniInputSceneComponents(TFunctionRef<void(UHoudiniInputSceneComponent*)> Fn) const
|
|
{
|
|
auto ProcessSceneComponent = [Fn](UHoudiniInputObject* InputObject)
|
|
{
|
|
if (!InputObject)
|
|
return;
|
|
UHoudiniInputSceneComponent* SceneComponentInput = Cast<UHoudiniInputSceneComponent>(InputObject);
|
|
if (!SceneComponentInput)
|
|
return;
|
|
Fn(SceneComponentInput);
|
|
};
|
|
ForAllHoudiniInputObjects(ProcessSceneComponent);
|
|
}
|
|
|
|
void UHoudiniInput::GetAllHoudiniInputSceneComponents(TArray<UHoudiniInputSceneComponent*>& OutObjects) const
|
|
{
|
|
OutObjects.Empty();
|
|
auto AddSceneComponent = [&OutObjects](UHoudiniInputObject* InputObject)
|
|
{
|
|
if (!InputObject)
|
|
return;
|
|
UHoudiniInputSceneComponent* SceneComponentInput = Cast<UHoudiniInputSceneComponent>(InputObject);
|
|
if (!SceneComponentInput)
|
|
return;
|
|
OutObjects.Add(SceneComponentInput);
|
|
};
|
|
ForAllHoudiniInputObjects(AddSceneComponent);
|
|
}
|
|
|
|
void UHoudiniInput::GetAllHoudiniInputSplineComponents(TArray<UHoudiniInputHoudiniSplineComponent*>& OutObjects) const
|
|
{
|
|
OutObjects.Empty();
|
|
auto AddSceneComponent = [&OutObjects](UHoudiniInputObject* InputObject)
|
|
{
|
|
if (!InputObject)
|
|
return;
|
|
UHoudiniInputHoudiniSplineComponent* SceneComponentInput = Cast<UHoudiniInputHoudiniSplineComponent>(InputObject);
|
|
if (!SceneComponentInput)
|
|
return;
|
|
OutObjects.Add(SceneComponentInput);
|
|
};
|
|
ForAllHoudiniInputObjects(AddSceneComponent);
|
|
}
|
|
|
|
|
|
void UHoudiniInput::RemoveHoudiniInputObject(UHoudiniInputObject* InInputObject)
|
|
{
|
|
if (!InInputObject)
|
|
return;
|
|
|
|
ForAllHoudiniInputObjectArrays([InInputObject](TArray<UHoudiniInputObject*>& ObjectArray) {
|
|
ObjectArray.Remove(InInputObject);
|
|
});
|
|
|
|
return;
|
|
}
|