Files
Andron666 9c38e93fa4 part7
2022-12-05 20:31:35 +05:00

4203 lines
125 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 "HoudiniPublicAPIAssetWrapper.h"
#include "HoudiniAssetActor.h"
#include "HoudiniAssetComponent.h"
#include "HoudiniEngineBakeUtils.h"
#include "HoudiniEngineCommands.h"
#include "HoudiniEngineUtils.h"
#include "HoudiniOutputDetails.h"
#include "HoudiniParameter.h"
#include "HoudiniParameterButton.h"
#include "HoudiniParameterChoice.h"
#include "HoudiniParameterColor.h"
#include "HoudiniParameterFile.h"
#include "HoudiniParameterFloat.h"
#include "HoudiniParameterFolder.h"
#include "HoudiniParameterInt.h"
#include "HoudiniParameterMultiParm.h"
#include "HoudiniParameterRamp.h"
#include "HoudiniParameterString.h"
#include "HoudiniParameterToggle.h"
#include "HoudiniPDGManager.h"
#include "HoudiniPublicAPI.h"
#include "HoudiniPublicAPIBlueprintLib.h"
#include "HoudiniPublicAPIInputTypes.h"
FHoudiniPublicAPIRampPoint::FHoudiniPublicAPIRampPoint()
: Position(0)
, Interpolation(EHoudiniPublicAPIRampInterpolationType::LINEAR)
{
}
FHoudiniPublicAPIRampPoint::FHoudiniPublicAPIRampPoint(
const float InPosition,
const EHoudiniPublicAPIRampInterpolationType InInterpolation)
: Position(InPosition)
, Interpolation(InInterpolation)
{
}
FHoudiniPublicAPIFloatRampPoint::FHoudiniPublicAPIFloatRampPoint()
: Value(0)
{
}
FHoudiniPublicAPIFloatRampPoint::FHoudiniPublicAPIFloatRampPoint(
const float InPosition,
const float InValue,
const EHoudiniPublicAPIRampInterpolationType InInterpolation)
: FHoudiniPublicAPIRampPoint(InPosition, InInterpolation)
, Value(InValue)
{
}
FHoudiniPublicAPIColorRampPoint::FHoudiniPublicAPIColorRampPoint()
: Value(FLinearColor::Black)
{
}
FHoudiniPublicAPIColorRampPoint::FHoudiniPublicAPIColorRampPoint(
const float InPosition,
const FLinearColor& InValue,
const EHoudiniPublicAPIRampInterpolationType InInterpolation)
: FHoudiniPublicAPIRampPoint(InPosition, InInterpolation)
, Value(InValue)
{
}
FHoudiniParameterTuple::FHoudiniParameterTuple()
: BoolValues()
, Int32Values()
, FloatValues()
, StringValues()
{
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const bool& InValue)
: FHoudiniParameterTuple()
{
BoolValues.Add(InValue);
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const TArray<bool>& InValues)
: BoolValues(InValues)
{
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const int32& InValue)
: FHoudiniParameterTuple()
{
Int32Values.Add(InValue);
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const TArray<int32>& InValues)
: Int32Values(InValues)
{
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const float& InValue)
: FHoudiniParameterTuple()
{
FloatValues.Add(InValue);
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const TArray<float>& InValues)
: FloatValues(InValues)
{
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const FString& InValue)
: FHoudiniParameterTuple()
{
StringValues.Add(InValue);
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const TArray<FString>& InValues)
: StringValues(InValues)
{
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const TArray<FHoudiniPublicAPIFloatRampPoint>& InRampPoints)
: FloatRampPoints(InRampPoints)
{
}
FHoudiniParameterTuple::FHoudiniParameterTuple(const TArray<FHoudiniPublicAPIColorRampPoint>& InRampPoints)
: ColorRampPoints(InRampPoints)
{
}
//
// UHoudiniPublicAPIAssetWrapper
//
UHoudiniPublicAPIAssetWrapper::UHoudiniPublicAPIAssetWrapper()
: HoudiniAssetObject(nullptr)
, bAssetLinkSetupAttemptComplete(false)
{
}
UHoudiniPublicAPIAssetWrapper*
UHoudiniPublicAPIAssetWrapper::CreateWrapper(UObject* InOuter, UObject* InHoudiniAssetActorOrComponent)
{
if (!IsValid(InHoudiniAssetActorOrComponent))
return nullptr;
// Check if InHoudiniAssetActorOrComponent is supported
if (!CanWrapHoudiniObject(InHoudiniAssetActorOrComponent))
return nullptr;
UHoudiniPublicAPIAssetWrapper* NewWrapper = CreateEmptyWrapper(InOuter);
if (!IsValid(NewWrapper))
return nullptr;
// If we cannot wrap the specified actor, return nullptr.
if (!NewWrapper->WrapHoudiniAssetObject(InHoudiniAssetActorOrComponent))
{
NewWrapper->MarkPendingKill();
return nullptr;
}
return NewWrapper;
}
UHoudiniPublicAPIAssetWrapper*
UHoudiniPublicAPIAssetWrapper::CreateEmptyWrapper(UObject* InOuter)
{
UObject* const Outer = InOuter ? InOuter : GetTransientPackage();
UClass* const Class = StaticClass();
UHoudiniPublicAPIAssetWrapper* NewWrapper = NewObject<UHoudiniPublicAPIAssetWrapper>(
Outer, Class,
MakeUniqueObjectName(Outer, Class));
if (!IsValid(NewWrapper))
return nullptr;
return NewWrapper;
}
bool
UHoudiniPublicAPIAssetWrapper::CanWrapHoudiniObject(UObject* InObject)
{
if (!IsValid(InObject))
return false;
return InObject->IsA<AHoudiniAssetActor>() || InObject->IsA<UHoudiniAssetComponent>();
}
bool
UHoudiniPublicAPIAssetWrapper::GetTemporaryCookFolder_Implementation(FDirectoryPath& OutDirectoryPath) const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
OutDirectoryPath = HAC->TemporaryCookFolder;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetTemporaryCookFolder_Implementation(const FDirectoryPath& InDirectoryPath) const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
if (HAC->TemporaryCookFolder.Path != InDirectoryPath.Path)
HAC->TemporaryCookFolder = InDirectoryPath;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetBakeFolder_Implementation(FDirectoryPath& OutDirectoryPath) const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
OutDirectoryPath = HAC->BakeFolder;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetBakeFolder_Implementation(const FDirectoryPath& InDirectoryPath) const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
if (HAC->BakeFolder.Path != InDirectoryPath.Path)
HAC->BakeFolder = InDirectoryPath;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::BakeAllOutputs_Implementation()
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return FHoudiniEngineBakeUtils::BakeHoudiniAssetComponent(
HAC,
HAC->bReplacePreviousBake,
HAC->HoudiniEngineBakeOption,
HAC->bRemoveOutputAfterBake,
HAC->bRecenterBakedActors);
}
bool
UHoudiniPublicAPIAssetWrapper::BakeAllOutputsWithSettings_Implementation(
EHoudiniEngineBakeOption InBakeOption,
bool bInReplacePreviousBake,
bool bInRemoveTempOutputsOnSuccess,
bool bInRecenterBakedActors)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return FHoudiniEngineBakeUtils::BakeHoudiniAssetComponent(HAC, bInReplacePreviousBake, InBakeOption, bInRemoveTempOutputsOnSuccess, bInRecenterBakedActors);
}
bool
UHoudiniPublicAPIAssetWrapper::SetAutoBakeEnabled_Implementation(const bool bInAutoBakeEnabled)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
HAC->SetBakeAfterNextCookEnabled(bInAutoBakeEnabled);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::IsAutoBakeEnabled_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return HAC->IsBakeAfterNextCookEnabled();
}
bool
UHoudiniPublicAPIAssetWrapper::SetBakeMethod_Implementation(const EHoudiniEngineBakeOption InBakeMethod)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
HAC->HoudiniEngineBakeOption = InBakeMethod;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetBakeMethod_Implementation(EHoudiniEngineBakeOption& OutBakeMethod)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
OutBakeMethod = HAC->HoudiniEngineBakeOption;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetRemoveOutputAfterBake_Implementation(const bool bInRemoveOutputAfterBake)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
HAC->bRemoveOutputAfterBake = bInRemoveOutputAfterBake;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetRemoveOutputAfterBake_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return HAC->bRemoveOutputAfterBake;
}
bool
UHoudiniPublicAPIAssetWrapper::SetRecenterBakedActors_Implementation(const bool bInRecenterBakedActors)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
HAC->bRecenterBakedActors = bInRecenterBakedActors;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetRecenterBakedActors_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return HAC->bRecenterBakedActors;
}
bool
UHoudiniPublicAPIAssetWrapper::SetReplacePreviousBake_Implementation(const bool bInReplacePreviousBake)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
HAC->bReplacePreviousBake = bInReplacePreviousBake;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetReplacePreviousBake_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return HAC->bReplacePreviousBake;
}
bool
UHoudiniPublicAPIAssetWrapper::GetValidHoudiniAssetActorWithError(AHoudiniAssetActor*& OutActor) const
{
AHoudiniAssetActor* const Actor = GetHoudiniAssetActor();
if (!IsValid(Actor))
{
SetErrorMessage(
TEXT("Could not find a valid AHoudiniAssetActor for the wrapped asset, or no asset has been wrapped."));
return false;
}
OutActor = Actor;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetValidHoudiniAssetComponentWithError(UHoudiniAssetComponent*& OutHAC) const
{
UHoudiniAssetComponent* const HAC = GetHoudiniAssetComponent();
if (!IsValid(HAC))
{
SetErrorMessage(
TEXT("Could not find a valid HoudiniAssetComponent for the wrapped asset, or no asset has been wrapped."));
return false;
}
OutHAC = HAC;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetValidOutputAtWithError(const int32 InOutputIndex, UHoudiniOutput*& OutOutput) const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
// Check if InOutputIndex is a valid/in-range index
const int32 NumOutputs = HAC->GetNumOutputs();
if (InOutputIndex < 0 || InOutputIndex >= NumOutputs)
{
SetErrorMessage(FString::Printf(
TEXT("Output index %d is out of range [0, %d]"), InOutputIndex, NumOutputs));
return false;
}
UHoudiniOutput* const Output= HAC->GetOutputAt(InOutputIndex);
if (!IsValid(Output))
{
SetErrorMessage(FString::Printf(TEXT("Output at index %d is invalid."), InOutputIndex));
return false;
}
OutOutput = Output;
return true;
}
UHoudiniPDGAssetLink*
UHoudiniPublicAPIAssetWrapper::GetHoudiniPDGAssetLink() const
{
UHoudiniAssetComponent* const HAC = GetHoudiniAssetComponent();
if (!IsValid(HAC))
return nullptr;
return HAC->GetPDGAssetLink();
}
bool
UHoudiniPublicAPIAssetWrapper::GetValidHoudiniPDGAssetLinkWithError(UHoudiniPDGAssetLink*& OutAssetLink) const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
UHoudiniPDGAssetLink* const AssetLink = HAC->GetPDGAssetLink();
if (!IsValid(AssetLink))
{
SetErrorMessage(
TEXT("Could not find a valid HoudiniPDGAssetLink for the wrapped asset. Does it contain a TOP network?"));
return false;
}
OutAssetLink = AssetLink;
return true;
}
void
UHoudiniPublicAPIAssetWrapper::ClearHoudiniAssetObject_Implementation()
{
UHoudiniPDGAssetLink* const AssetLink = GetHoudiniPDGAssetLink();
if (IsValid(AssetLink))
{
if (OnPDGPostTOPNetworkCookDelegateHandle.IsValid())
AssetLink->GetOnPostTOPNetworkCookDelegate().Remove(OnPDGPostTOPNetworkCookDelegateHandle);
if (OnPDGPostBakeDelegateHandle.IsValid())
AssetLink->GetOnPostBakeDelegate().Remove(OnPDGPostBakeDelegateHandle);
}
bAssetLinkSetupAttemptComplete = false;
FHoudiniEngineCommands::GetOnHoudiniProxyMeshesRefinedDelegate().Remove(OnHoudiniProxyMeshesRefinedDelegateHandle);
UHoudiniAssetComponent* const HAC = GetHoudiniAssetComponent();
if (IsValid(HAC))
{
if (OnAssetStateChangeDelegateHandle.IsValid())
HAC->GetOnAssetStateChangeDelegate().Remove(OnAssetStateChangeDelegateHandle);
if (OnPostCookDelegateHandle.IsValid())
HAC->GetOnPostCookDelegate().Remove(OnPostCookDelegateHandle);
if (OnPostBakeDelegateHandle.IsValid())
HAC->GetOnPostBakeDelegate().Remove(OnPostBakeDelegateHandle);
}
OnPDGPostTOPNetworkCookDelegateHandle.Reset();
OnPDGPostBakeDelegateHandle.Reset();
OnAssetStateChangeDelegateHandle.Reset();
OnPostCookDelegateHandle.Reset();
OnPostBakeDelegateHandle.Reset();
OnHoudiniProxyMeshesRefinedDelegateHandle.Reset();
HoudiniAssetObject = nullptr;
CachedHoudiniAssetActor = nullptr;
CachedHoudiniAssetComponent = nullptr;
}
bool
UHoudiniPublicAPIAssetWrapper::WrapHoudiniAssetObject_Implementation(UObject* InHoudiniAssetObjectToWrap)
{
// If InHoudiniAssetObjectToWrap is null, just unwrap any currently wrapped asset
if (!InHoudiniAssetObjectToWrap)
{
ClearHoudiniAssetObject();
return true;
}
// Check if InHoudiniAssetObjectToWrap is supported
if (!CanWrapHoudiniObject(InHoudiniAssetObjectToWrap))
{
UClass* const ObjectClass = IsValid(InHoudiniAssetObjectToWrap) ? InHoudiniAssetObjectToWrap->GetClass() : nullptr;
SetErrorMessage(FString::Printf(
TEXT("Cannot wrap objects of class '%s'."), ObjectClass ? *(ObjectClass->GetName()) : TEXT("Unknown")));
return false;
}
// First unwrap/unbind if we are currently wrapping an instantiated asset
ClearHoudiniAssetObject();
HoudiniAssetObject = InHoudiniAssetObjectToWrap;
// Cache the HoudiniAssetActor and HoudiniAssetComponent
if (HoudiniAssetObject->IsA<AHoudiniAssetActor>())
{
CachedHoudiniAssetActor = Cast<AHoudiniAssetActor>(InHoudiniAssetObjectToWrap);
CachedHoudiniAssetComponent = CachedHoudiniAssetActor->HoudiniAssetComponent;
}
else if (HoudiniAssetObject->IsA<UHoudiniAssetComponent>())
{
CachedHoudiniAssetComponent = Cast<UHoudiniAssetComponent>(InHoudiniAssetObjectToWrap);
CachedHoudiniAssetActor = Cast<AHoudiniAssetActor>(CachedHoudiniAssetComponent->GetOwner());
}
UHoudiniAssetComponent* const HAC = GetHoudiniAssetComponent();
if (IsValid(HAC))
{
// Bind to HandleOnHoudiniAssetStateChange from the HAC: we also implement IHoudiniAssetStateEvents, and
// in the default implementation HandleOnHoudiniAssetStateChange will call the appropriate Handle functions
// for PostInstantiate, PostCook etc
OnAssetStateChangeDelegateHandle = HAC->GetOnAssetStateChangeDelegate().AddUFunction(this, TEXT("HandleOnHoudiniAssetComponentStateChange"));
OnPostCookDelegateHandle = HAC->GetOnPostCookDelegate().AddUFunction(this, TEXT("HandleOnHoudiniAssetComponentPostCook"));
OnPostBakeDelegateHandle = HAC->GetOnPostBakeDelegate().AddUFunction(this, TEXT("HandleOnHoudiniAssetComponentPostBake"));
}
OnHoudiniProxyMeshesRefinedDelegateHandle = FHoudiniEngineCommands::GetOnHoudiniProxyMeshesRefinedDelegate().AddUFunction(this, TEXT("HandleOnHoudiniProxyMeshesRefinedGlobal"));
// PDG asset link bindings: We attempt to bind to PDG here, but it likely is not available yet.
// We have to wait until post instantiation in order to know if there is a PDG asset link
// for this HDA. This is checked again in HandleOnHoudiniAssetComponentStateChange and sets
// bAssetLinkSetupAttemptComplete.
BindToPDGAssetLink();
return true;
}
AHoudiniAssetActor*
UHoudiniPublicAPIAssetWrapper::GetHoudiniAssetActor_Implementation() const
{
return CachedHoudiniAssetActor.Get();
}
UHoudiniAssetComponent*
UHoudiniPublicAPIAssetWrapper::GetHoudiniAssetComponent_Implementation() const
{
return CachedHoudiniAssetComponent.Get();
}
bool
UHoudiniPublicAPIAssetWrapper::DeleteInstantiatedAsset_Implementation()
{
AHoudiniAssetActor* AssetActor = nullptr;
if (!GetValidHoudiniAssetActorWithError(AssetActor))
return false;
// Unbind / unwrap the HDA actor
ClearHoudiniAssetObject();
AssetActor->Destroy();
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::Rebuild_Implementation()
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
HAC->MarkAsNeedRebuild();
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::Recook_Implementation()
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
HAC->MarkAsNeedCook();
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetAutoCookingEnabled_Implementation(const bool bInSetEnabled)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
if (HAC->IsCookingEnabled() == bInSetEnabled)
return false;
HAC->SetCookingEnabled(bInSetEnabled);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::IsAutoCookingEnabled_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return HAC->IsCookingEnabled();
}
bool
UHoudiniPublicAPIAssetWrapper::SetFloatParameterValue_Implementation(FName InParameterTupleName, float InValue, int32 InAtIndex, bool bInMarkChanged)
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is a float
const EHoudiniParameterType ParamType = Param->GetParameterType();
bool bDidChangeValue = false;
if (ParamType == EHoudiniParameterType::Float)
{
UHoudiniParameterFloat* FloatParam = Cast<UHoudiniParameterFloat>(Param);
if (!IsValid(FloatParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= FloatParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, FloatParam->GetNumberOfValues()));
return false;
}
bDidChangeValue = FloatParam->SetValueAt(InValue, InAtIndex);
}
else if (ParamType == EHoudiniParameterType::Color)
{
UHoudiniParameterColor* ColorParam = Cast<UHoudiniParameterColor>(Param);
if (!IsValid(ColorParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
static const int32 NumColorChannels = 4;
if (InAtIndex >= NumColorChannels)
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, NumColorChannels));
return false;
}
FLinearColor CurrentColorValue = ColorParam->GetColorValue();
if (CurrentColorValue.Component(InAtIndex) != InValue)
{
CurrentColorValue.Component(InAtIndex) = InValue;
ColorParam->SetColorValue(CurrentColorValue);
bDidChangeValue = true;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple '%s' is of a type that cannot be set via SetFloatParamterValue."), *InParameterTupleName.ToString()));
return false;
}
if (bDidChangeValue && bInMarkChanged)
Param->MarkChanged(true);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetFloatParameterValue_Implementation(FName InParameterTupleName, float& OutValue, int32 InAtIndex) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is a float
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::Float)
{
UHoudiniParameterFloat* FloatParam = Cast<UHoudiniParameterFloat>(Param);
if (!IsValid(FloatParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= FloatParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, FloatParam->GetNumberOfValues()));
return false;
}
return FloatParam->GetValueAt(InAtIndex, OutValue);
}
else if (ParamType == EHoudiniParameterType::Color)
{
UHoudiniParameterColor* ColorParam = Cast<UHoudiniParameterColor>(Param);
if (!IsValid(ColorParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
static const int32 NumColorChannels = 4;
if (InAtIndex >= NumColorChannels)
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, NumColorChannels));
return false;
}
OutValue = ColorParam->GetColorValue().Component(InAtIndex);
return true;
}
return false;
}
bool
UHoudiniPublicAPIAssetWrapper::SetColorParameterValue_Implementation(FName InParameterTupleName, const FLinearColor& InValue, bool bInMarkChanged)
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is a float
const EHoudiniParameterType ParamType = Param->GetParameterType();
bool bDidChangeValue = false;
if (ParamType == EHoudiniParameterType::Color)
{
UHoudiniParameterColor* ColorParam = Cast<UHoudiniParameterColor>(Param);
if (!IsValid(ColorParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
bDidChangeValue = ColorParam->SetColorValue(InValue);
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple '%s' is of a type that cannot be set via SetColorParamterValue."), *InParameterTupleName.ToString()));
return false;
}
if (bDidChangeValue && bInMarkChanged)
Param->MarkChanged(true);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetColorParameterValue_Implementation(FName InParameterTupleName, FLinearColor& OutValue) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is a float
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::Color)
{
UHoudiniParameterColor* ColorParam = Cast<UHoudiniParameterColor>(Param);
if (!IsValid(ColorParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = ColorParam->GetColorValue();
return true;
}
return false;
}
bool
UHoudiniPublicAPIAssetWrapper::SetIntParameterValue_Implementation(FName InParameterTupleName, int32 InValue, int32 InAtIndex, bool bInMarkChanged)
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
bool bDidChangeValue = false;
if (ParamType == EHoudiniParameterType::Int)
{
UHoudiniParameterInt* IntParam = Cast<UHoudiniParameterInt>(Param);
if (!IsValid(IntParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= IntParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, IntParam->GetNumberOfValues()));
return false;
}
bDidChangeValue = IntParam->SetValueAt(InValue, InAtIndex);
}
else if (ParamType == EHoudiniParameterType::IntChoice)
{
UHoudiniParameterChoice* ChoiceParam = Cast<UHoudiniParameterChoice>(Param);
if (!IsValid(ChoiceParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
bDidChangeValue = ChoiceParam->SetIntValue(InValue);
}
else if (ParamType == EHoudiniParameterType::MultiParm)
{
UHoudiniParameterMultiParm* MultiParam = Cast<UHoudiniParameterMultiParm>(Param);
if (!IsValid(MultiParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
bDidChangeValue = MultiParam->SetValue(InValue);
}
else if (ParamType == EHoudiniParameterType::Toggle)
{
UHoudiniParameterToggle* ToggleParam = Cast<UHoudiniParameterToggle>(Param);
if (!IsValid(ToggleParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
bDidChangeValue = ToggleParam->SetValueAt(InValue != 0, InAtIndex);
}
else if (ParamType == EHoudiniParameterType::Folder)
{
UHoudiniParameterFolder* FolderParam = Cast<UHoudiniParameterFolder>(Param);
if (!IsValid(FolderParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
const bool NewValue = InValue != 0;
if (FolderParam->IsChosen() != NewValue)
{
FolderParam->SetChosen(NewValue);
bDidChangeValue = true;
}
}
else if (ParamType == EHoudiniParameterType::FloatRamp || ParamType == EHoudiniParameterType::ColorRamp)
{
// For ramps we have to use the appropriate function so that delete/insert operations are managed correctly
bDidChangeValue = SetRampParameterNumPoints(InParameterTupleName, InValue);
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple '%s' is of a type that cannot be set via SetIntParameterValue."), *InParameterTupleName.ToString()));
return false;
}
if (bDidChangeValue && bInMarkChanged)
Param->MarkChanged(true);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetIntParameterValue_Implementation(FName InParameterTupleName, int32& OutValue, int32 InAtIndex) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::Int)
{
UHoudiniParameterInt* IntParam = Cast<UHoudiniParameterInt>(Param);
if (!IsValid(IntParam))
return false;
if (InAtIndex >= IntParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, IntParam->GetNumberOfValues()));
return false;
}
return IntParam->GetValueAt(InAtIndex, OutValue);
}
else if (ParamType == EHoudiniParameterType::IntChoice)
{
UHoudiniParameterChoice* ChoiceParam = Cast<UHoudiniParameterChoice>(Param);
if (!IsValid(ChoiceParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = ChoiceParam->GetIntValue(ChoiceParam->GetIntValueIndex());
return true;
}
else if (ParamType == EHoudiniParameterType::MultiParm)
{
UHoudiniParameterMultiParm* MultiParam = Cast<UHoudiniParameterMultiParm>(Param);
if (!IsValid(MultiParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = MultiParam->GetValue();
return true;
}
else if (ParamType == EHoudiniParameterType::Toggle)
{
UHoudiniParameterToggle* ToggleParam = Cast<UHoudiniParameterToggle>(Param);
if (!IsValid(ToggleParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = ToggleParam->GetValueAt(InAtIndex);
return true;
}
else if (ParamType == EHoudiniParameterType::Folder)
{
UHoudiniParameterFolder* FolderParam = Cast<UHoudiniParameterFolder>(Param);
if (!IsValid(FolderParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = FolderParam->IsChosen();
return true;
}
return false;
}
bool
UHoudiniPublicAPIAssetWrapper::SetBoolParameterValue_Implementation(FName InParameterTupleName, bool InValue, int32 InAtIndex, bool bInMarkChanged)
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
bool bDidChangeValue = false;
if (ParamType == EHoudiniParameterType::Toggle)
{
UHoudiniParameterToggle* ToggleParam = Cast<UHoudiniParameterToggle>(Param);
if (!IsValid(ToggleParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
bDidChangeValue = ToggleParam->SetValueAt(InValue, InAtIndex);
}
else if (ParamType == EHoudiniParameterType::Folder)
{
UHoudiniParameterFolder* FolderParam = Cast<UHoudiniParameterFolder>(Param);
if (!IsValid(FolderParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (FolderParam->IsChosen() != InValue)
{
FolderParam->SetChosen(InValue);
bDidChangeValue = true;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple '%s' is of a type that cannot be set via SetBoolParameterValue."), *InParameterTupleName.ToString()));
return false;
}
if (bDidChangeValue && bInMarkChanged)
Param->MarkChanged(true);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetBoolParameterValue_Implementation(FName InParameterTupleName, bool& OutValue, int32 InAtIndex) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::Toggle)
{
UHoudiniParameterToggle* ToggleParam = Cast<UHoudiniParameterToggle>(Param);
if (!IsValid(ToggleParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = ToggleParam->GetValueAt(InAtIndex);
return true;
}
else if (ParamType == EHoudiniParameterType::Folder)
{
UHoudiniParameterFolder* FolderParam = Cast<UHoudiniParameterFolder>(Param);
if (!IsValid(FolderParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = FolderParam->IsChosen();
return true;
}
return false;
}
bool
UHoudiniPublicAPIAssetWrapper::SetStringParameterValue_Implementation(FName InParameterTupleName, const FString& InValue, int32 InAtIndex, bool bInMarkChanged)
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
bool bDidChangeValue = false;
if (ParamType == EHoudiniParameterType::String || ParamType == EHoudiniParameterType::StringAssetRef)
{
UHoudiniParameterString* StringParam = Cast<UHoudiniParameterString>(Param);
if (!IsValid(StringParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= StringParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, StringParam->GetNumberOfValues()));
return false;
}
// We have to handle asset references differently
if (ParamType == EHoudiniParameterType::StringAssetRef)
{
// Find/load the asset, make sure it is a valid reference/object
const FSoftObjectPath AssetRef(InValue);
UObject* const Asset = AssetRef.TryLoad();
if (IsValid(Asset))
{
UObject* const CurrentAsset = StringParam->GetAssetAt(InAtIndex);
if (CurrentAsset != Asset)
{
StringParam->SetAssetAt(Asset, InAtIndex);
bDidChangeValue = true;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Asset reference '%s' is invalid. Not setting parameter value."), *InValue));
return false;
}
}
else
{
bDidChangeValue = StringParam->SetValueAt(InValue, InAtIndex);
}
}
else if (ParamType == EHoudiniParameterType::StringChoice)
{
UHoudiniParameterChoice* ChoiceParam = Cast<UHoudiniParameterChoice>(Param);
if (!IsValid(ChoiceParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
bDidChangeValue = ChoiceParam->SetStringValue(InValue);
}
else if (ParamType == EHoudiniParameterType::File || ParamType == EHoudiniParameterType::FileDir ||
ParamType == EHoudiniParameterType::FileGeo || ParamType == EHoudiniParameterType::FileImage)
{
UHoudiniParameterFile* FileParam = Cast<UHoudiniParameterFile>(Param);
if (!IsValid(FileParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= FileParam->GetNumValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, FileParam->GetNumValues()));
return false;
}
bDidChangeValue = FileParam->SetValueAt(InValue, InAtIndex);
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple '%s' is of a type that cannot be set via SetStringParameterValue."), *InParameterTupleName.ToString()));
return false;
}
if (bDidChangeValue && bInMarkChanged)
Param->MarkChanged(true);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetStringParameterValue_Implementation(FName InParameterTupleName, FString& OutValue, int32 InAtIndex) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::String || ParamType == EHoudiniParameterType::StringAssetRef)
{
UHoudiniParameterString* StringParam = Cast<UHoudiniParameterString>(Param);
if (!IsValid(StringParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= StringParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, StringParam->GetNumberOfValues()));
return false;
}
// For asset references: get the asset, and then get the string reference from it and return that.
// If the asset is null, return the empty string.
if (ParamType == EHoudiniParameterType::StringAssetRef)
{
UObject* const Asset = StringParam->GetAssetAt(InAtIndex);
if (IsValid(Asset))
{
OutValue = UHoudiniParameterString::GetAssetReference(Asset);
}
else
{
OutValue.Empty();
}
}
else
{
OutValue = StringParam->GetValueAt(InAtIndex);
}
return true;
}
else if (ParamType == EHoudiniParameterType::StringChoice)
{
UHoudiniParameterChoice* ChoiceParam = Cast<UHoudiniParameterChoice>(Param);
if (!IsValid(ChoiceParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
OutValue = ChoiceParam->GetStringValue();
return true;
}
else if (ParamType == EHoudiniParameterType::File || ParamType == EHoudiniParameterType::FileDir ||
ParamType == EHoudiniParameterType::FileGeo || ParamType == EHoudiniParameterType::FileImage)
{
UHoudiniParameterFile* FileParam = Cast<UHoudiniParameterFile>(Param);
if (!IsValid(FileParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= FileParam->GetNumValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, FileParam->GetNumValues()));
return false;
}
OutValue = FileParam->GetValueAt(InAtIndex);
return true;
}
return false;
}
bool
UHoudiniPublicAPIAssetWrapper::SetAssetRefParameterValue_Implementation(FName InParameterTupleName, UObject* InValue, int32 InAtIndex, bool bInMarkChanged)
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
bool bDidChangeValue = false;
if (ParamType == EHoudiniParameterType::StringAssetRef)
{
UHoudiniParameterString* StringParam = Cast<UHoudiniParameterString>(Param);
if (!IsValid(StringParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= StringParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, StringParam->GetNumberOfValues()));
return false;
}
// Find/load the asset, make sure it is a valid reference/object
UObject* const CurrentAsset = StringParam->GetAssetAt(InAtIndex);
if (CurrentAsset != InValue)
{
StringParam->SetAssetAt(InValue, InAtIndex);
bDidChangeValue = true;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple '%s' is of a type that cannot be set via SetAssetRefParamter."), *InParameterTupleName.ToString()));
return false;
}
if (bDidChangeValue && bInMarkChanged)
Param->MarkChanged(true);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetAssetRefParameterValue_Implementation(FName InParameterTupleName, UObject*& OutValue, int32 InAtIndex) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::StringAssetRef)
{
UHoudiniParameterString* StringParam = Cast<UHoudiniParameterString>(Param);
if (!IsValid(StringParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
if (InAtIndex >= StringParam->GetNumberOfValues())
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple index %d is out of range (tuple size == %d)."), InAtIndex, StringParam->GetNumberOfValues()));
return false;
}
OutValue = StringParam->GetAssetAt(InAtIndex);
return true;
}
return false;
}
bool
UHoudiniPublicAPIAssetWrapper::SetRampParameterNumPoints_Implementation(FName InParameterTupleName, const int32 InNumPoints) const
{
if (InNumPoints < 1)
{
SetErrorMessage(TEXT("InNumPoints must be >= 1."));
return false;
}
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// If the parameter is not set to auto update, or if cooking is paused, we have to set the cached points and
// not the main points.
// const bool bCookingEnabled = !FHoudiniEngineCommands::IsAssetCookingPaused();
// const bool bUseCachedPoints = (!Param->IsAutoUpdate() || !bCookingEnabled);
const bool bUseCachedPoints = !Param->IsAutoUpdate();
UHoudiniParameterRampFloat* FloatRampParam = nullptr;
UHoudiniParameterRampColor* ColorRampParam = nullptr;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::FloatRamp)
{
FloatRampParam = Cast<UHoudiniParameterRampFloat>(Param);
if (!IsValid(FloatRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else if (ParamType == EHoudiniParameterType::ColorRamp)
{
ColorRampParam = Cast<UHoudiniParameterRampColor>(Param);
if (!IsValid(ColorRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter '%s' is not a float or color ramp parameter."), *(InParameterTupleName.ToString())));
return false;
}
if (bUseCachedPoints)
{
// When using the cached points we only have to resize the array
if (FloatRampParam)
{
const int32 CurrentNumPoints = FloatRampParam->CachedPoints.Num();
if (CurrentNumPoints != InNumPoints)
{
FloatRampParam->CachedPoints.SetNum(InNumPoints);
// FloatRampParam->MarkChanged(true);
}
}
else
{
const int32 CurrentNumPoints = ColorRampParam->CachedPoints.Num();
if (CurrentNumPoints != InNumPoints)
{
ColorRampParam->CachedPoints.SetNum(InNumPoints);
// ColorRampParam->MarkChanged(true);
}
}
// Update the ramp's widget if it is currently visible/selected
const bool bForceFullUpdate = true;
FHoudiniEngineUtils::UpdateEditorProperties(Param, bForceFullUpdate);
}
else
{
int32 NumPendingInsertOperations = 0;
int32 NumPendingDeleteOperations = 0;
TSet<int32> InstanceIndexesPendingDelete;
TArray<UHoudiniParameterRampModificationEvent*>& ModificationEvents = FloatRampParam ? FloatRampParam->ModificationEvents : ColorRampParam->ModificationEvents;
for (UHoudiniParameterRampModificationEvent const* const Event : ModificationEvents)
{
if (!IsValid(Event))
continue;
if (Event->IsInsertEvent())
NumPendingInsertOperations++;
else if (Event->IsDeleteEvent())
{
InstanceIndexesPendingDelete.Add(Event->DeleteInstanceIndex);
NumPendingDeleteOperations++;
}
}
const int32 PointsArraySize = FloatRampParam ? FloatRampParam->Points.Num() : ColorRampParam->Points.Num();
int32 CurrentNumPoints = PointsArraySize + NumPendingInsertOperations - NumPendingDeleteOperations;
if (InNumPoints < CurrentNumPoints)
{
// When deleting points, first remove pending insert operations from the end
if (NumPendingInsertOperations > 0)
{
const int32 NumEvents = ModificationEvents.Num();
TArray<UHoudiniParameterRampModificationEvent*> TempModificationArray;
TempModificationArray.Reserve(NumEvents);
for (int32 Index = NumEvents - 1; Index >= 0; --Index)
{
UHoudiniParameterRampModificationEvent* const Event = ModificationEvents[Index];
if (InNumPoints < CurrentNumPoints && IsValid(Event) && Event->IsInsertEvent())
{
CurrentNumPoints--;
NumPendingInsertOperations--;
continue;
}
TempModificationArray.Add(Event);
}
Algo::Reverse(TempModificationArray);
ModificationEvents = MoveTemp(TempModificationArray);
}
// If we still have points to delete...
if (InNumPoints < CurrentNumPoints)
{
// Deleting points, add delete operations, deleting from the end of Points (points that are not yet
// pending delete)
for (int32 Index = PointsArraySize - 1; (InNumPoints < CurrentNumPoints && Index >= 0); --Index)
{
int32 InstanceIndex = INDEX_NONE;
if (FloatRampParam)
{
UHoudiniParameterRampFloatPoint const* const PointData = FloatRampParam->Points[Index];
if (!IsValid(PointData))
continue;
InstanceIndex = PointData->InstanceIndex;
}
else
{
UHoudiniParameterRampColorPoint const* const PointData = ColorRampParam->Points[Index];
if (!IsValid(PointData))
continue;
InstanceIndex = PointData->InstanceIndex;
}
if (!InstanceIndexesPendingDelete.Contains(InstanceIndex))
{
InstanceIndexesPendingDelete.Add(InstanceIndex);
CurrentNumPoints--;
UHoudiniParameterRampModificationEvent* DeleteEvent = NewObject<UHoudiniParameterRampModificationEvent>(
FloatRampParam, UHoudiniParameterRampModificationEvent::StaticClass());
if (DeleteEvent)
{
if (FloatRampParam)
{
DeleteEvent->SetFloatRampEvent();
}
else
{
DeleteEvent->SetColorRampEvent();
}
DeleteEvent->SetDeleteEvent();
DeleteEvent->DeleteInstanceIndex = InstanceIndex;
ModificationEvents.Add(DeleteEvent);
}
}
}
}
Param->MarkChanged(true);
}
else if (InNumPoints > CurrentNumPoints)
{
// Adding points, add insert operations
while (InNumPoints > CurrentNumPoints)
{
CurrentNumPoints++;
UHoudiniParameterRampModificationEvent* InsertEvent = NewObject<UHoudiniParameterRampModificationEvent>(
Param, UHoudiniParameterRampModificationEvent::StaticClass());
if (InsertEvent)
{
if (FloatRampParam)
{
InsertEvent->SetFloatRampEvent();
}
else
{
InsertEvent->SetColorRampEvent();
}
InsertEvent->SetInsertEvent();
// Leave point position, value and interpolation at default
ModificationEvents.Add(InsertEvent);
}
}
Param->MarkChanged(true);
}
// If at this point InNumPoints != CurrentNumPoints then something went wrong, we couldn't delete all the
// desired points
if (InNumPoints != CurrentNumPoints)
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected error: could not delete the required number of ramp points "
"(target # points = %d; have # points %d)."), InNumPoints, CurrentNumPoints));
return false;
}
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetRampParameterNumPoints_Implementation(FName InParameterTupleName, int32& OutNumPoints) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
// If the parameter is not set to auto update, or if cooking is paused, we have to set the cached points and
// not the main points.
// const bool bCookingEnabled = !FHoudiniEngineCommands::IsAssetCookingPaused();
// const bool bUseCachedPoints = (!Param->IsAutoUpdate() || !bCookingEnabled);
const bool bUseCachedPoints = !Param->IsAutoUpdate();
UHoudiniParameterRampFloat* FloatRampParam = nullptr;
UHoudiniParameterRampColor* ColorRampParam = nullptr;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::FloatRamp)
{
FloatRampParam = Cast<UHoudiniParameterRampFloat>(Param);
if (!IsValid(FloatRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else if (ParamType == EHoudiniParameterType::ColorRamp)
{
ColorRampParam = Cast<UHoudiniParameterRampColor>(Param);
if (!IsValid(ColorRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter '%s' is not a float or color ramp parameter."), *(InParameterTupleName.ToString())));
return false;
}
if (bUseCachedPoints)
{
// When using the cached points we only have to resize the array
if (FloatRampParam)
{
OutNumPoints = FloatRampParam->CachedPoints.Num();
}
else
{
OutNumPoints = ColorRampParam->CachedPoints.Num();
}
}
else
{
int32 NumPendingInsertOperations = 0;
int32 NumPendingDeleteOperations = 0;
TArray<UHoudiniParameterRampModificationEvent*>& ModificationEvents = FloatRampParam ? FloatRampParam->ModificationEvents : ColorRampParam->ModificationEvents;
for (UHoudiniParameterRampModificationEvent const* const Event : ModificationEvents)
{
if (!IsValid(Event))
continue;
if (Event->IsInsertEvent())
NumPendingInsertOperations++;
else if (Event->IsDeleteEvent())
NumPendingDeleteOperations++;
}
const int32 PointsArraySize = FloatRampParam ? FloatRampParam->Points.Num() : ColorRampParam->Points.Num();
OutNumPoints = PointsArraySize + NumPendingInsertOperations - NumPendingDeleteOperations;
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetFloatRampParameterPointValue_Implementation(
FName InParameterTupleName,
const int32 InPointIndex,
const float InPointPosition,
const float InPointValue,
const EHoudiniPublicAPIRampInterpolationType InInterpolationType,
const bool bInMarkChanged)
{
return SetRampParameterPointValue(
InParameterTupleName, InPointIndex, InPointPosition, InPointValue, FLinearColor::Black, InInterpolationType, bInMarkChanged);
}
bool
UHoudiniPublicAPIAssetWrapper::GetFloatRampParameterPointValue_Implementation(
FName InParameterTupleName,
const int32 InPointIndex,
float& OutPointPosition,
float& OutPointValue,
EHoudiniPublicAPIRampInterpolationType& OutInterpolationType) const
{
FLinearColor ColorValue;
return GetRampParameterPointValue(
InParameterTupleName, InPointIndex, OutPointPosition, OutPointValue, ColorValue, OutInterpolationType);
}
bool
UHoudiniPublicAPIAssetWrapper::SetFloatRampParameterPoints_Implementation(
FName InParameterTupleName,
const TArray<FHoudiniPublicAPIFloatRampPoint>& InRampPoints,
const bool bInMarkChanged)
{
const int32 TargetNumPoints = InRampPoints.Num();
if (TargetNumPoints == 0)
{
SetErrorMessage(TEXT("InRampPoints must have at least one entry."));
return false;
}
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
UHoudiniParameterRampFloat* FloatRampParam = nullptr;
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::FloatRamp)
{
FloatRampParam = Cast<UHoudiniParameterRampFloat>(Param);
if (!IsValid(FloatRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(TEXT("Parameter '%s' is not a float ramp or color ramp parameter."), *(Param->GetName())));
return false;
}
// If the parameter is not set to auto update, or if cooking is paused, we have to set the cached points and
// not the main points.
// const bool bCookingEnabled = !FHoudiniEngineCommands::IsAssetCookingPaused();
// const bool bUseCachedPoints = (!Param->IsAutoUpdate() || !bCookingEnabled);
const bool bUseCachedPoints = !Param->IsAutoUpdate();
// Set the ramp point count to match the size of InRampPoints
if (!SetRampParameterNumPoints(InParameterTupleName, TargetNumPoints))
return false;
// Get all ramp points (INDEX_NONE == get all points)
TArray<TPair<UObject*, bool>> RampPointData;
if (!FindRampPointData(Param, INDEX_NONE, RampPointData) || RampPointData.Num() <= 0)
return false;
// Check that we fetched the correct number of point data objects
if (RampPointData.Num() != TargetNumPoints)
{
SetErrorMessage(FString::Printf(TEXT("Failed to set the number of ramp points to %d."), TargetNumPoints));
return false;
}
for (int32 Index = 0; Index < TargetNumPoints; ++Index)
{
TPair<UObject*, bool> const& Entry = RampPointData[Index];
UObject* const PointData = Entry.Key;
const bool bIsPointData = Entry.Value;
if (!IsValid(PointData))
return false;
const FHoudiniPublicAPIFloatRampPoint& NewRampPoint = InRampPoints[Index];
const EHoudiniRampInterpolationType NewInterpolation = UHoudiniPublicAPI::ToHoudiniRampInterpolationType(
NewRampPoint.Interpolation);
if (bIsPointData)
{
UHoudiniParameterRampFloatPoint* FloatPointData = Cast<UHoudiniParameterRampFloatPoint>(PointData);
if (!IsValid(FloatPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampFloatPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
if (bUseCachedPoints)
{
// When setting the cached points, we set the values directly instead of using the setters, but we set
// the bCaching flag on the parameter and mark the position/value/interpolation parent parameters as changed
if (FloatPointData->Position != NewRampPoint.Position)
{
FloatPointData->Position = NewRampPoint.Position;
FloatRampParam->bCaching = true;
}
if (FloatPointData->Value != NewRampPoint.Value)
{
FloatPointData->Value = NewRampPoint.Value;
FloatRampParam->bCaching = true;
}
if (FloatPointData->Interpolation != NewInterpolation)
{
FloatPointData->Interpolation = NewInterpolation;
FloatRampParam->bCaching = true;
}
}
else
{
// When setting the main points, we set the values using the setters on the point data but still manually
// mark the position/value/interpolation parent parameters as changed
if (FloatPointData->Position != NewRampPoint.Position && FloatPointData->PositionParentParm)
{
FloatPointData->SetPosition(NewRampPoint.Position);
if (bInMarkChanged)
FloatPointData->PositionParentParm->MarkChanged(bInMarkChanged);
}
if (FloatPointData->Value != NewRampPoint.Value && FloatPointData->ValueParentParm)
{
FloatPointData->SetValue(NewRampPoint.Value);
if (bInMarkChanged)
FloatPointData->ValueParentParm->MarkChanged(bInMarkChanged);
}
if (FloatPointData->Interpolation != NewInterpolation && FloatPointData->InterpolationParentParm)
{
FloatPointData->SetInterpolation(NewInterpolation);
if (bInMarkChanged)
FloatPointData->InterpolationParentParm->MarkChanged(bInMarkChanged);
}
}
}
else
{
UHoudiniParameterRampModificationEvent* const Event = Cast<UHoudiniParameterRampModificationEvent>(PointData);
if (!IsValid(Event))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampModificationEvent instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
Event->InsertPosition = NewRampPoint.Position;
Event->InsertFloat = NewRampPoint.Value;
Event->InsertInterpolation = NewInterpolation;
}
}
if (bUseCachedPoints)
{
// Update the ramp's widget if it is currently visible/selected
const bool bForceFullUpdate = true;
FHoudiniEngineUtils::UpdateEditorProperties(Param, bForceFullUpdate);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetFloatRampParameterPoints_Implementation(
FName InParameterTupleName,
TArray<FHoudiniPublicAPIFloatRampPoint>& OutRampPoints) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
UHoudiniParameterRampFloat* FloatRampParam = nullptr;
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::FloatRamp)
{
FloatRampParam = Cast<UHoudiniParameterRampFloat>(Param);
if (!IsValid(FloatRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(TEXT("Parameter '%s' is not a float ramp parameter."), *(Param->GetName())));
return false;
}
// Get all ramp points (INDEX_NONE == get all points)
TArray<TPair<UObject*, bool>> RampPointData;
if (!FindRampPointData(Param, INDEX_NONE, RampPointData) || RampPointData.Num() <= 0)
return false;
OutRampPoints.Reserve(RampPointData.Num());
const bool bAllowShrinking = false;
OutRampPoints.SetNum(0, bAllowShrinking);
for (TPair<UObject*, bool> const& Entry : RampPointData)
{
UObject* const PointData = Entry.Key;
const bool bIsPointData = Entry.Value;
if (!IsValid(PointData))
return false;
FHoudiniPublicAPIFloatRampPoint TempPointData;
if (bIsPointData)
{
UHoudiniParameterRampFloatPoint* const FloatPointData = Cast<UHoudiniParameterRampFloatPoint>(PointData);
if (!IsValid(FloatPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampFloatPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
TempPointData.Position = FloatPointData->Position;
TempPointData.Value = FloatPointData->Value;
TempPointData.Interpolation = UHoudiniPublicAPI::ToHoudiniPublicAPIRampInterpolationType(
FloatPointData->Interpolation);
}
else
{
UHoudiniParameterRampModificationEvent* const Event = Cast<UHoudiniParameterRampModificationEvent>(PointData);
if (!IsValid(Event))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampModificationEvent instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
TempPointData.Position = Event->InsertPosition;
TempPointData.Value = Event->InsertFloat;
TempPointData.Interpolation = UHoudiniPublicAPI::ToHoudiniPublicAPIRampInterpolationType(Event->InsertInterpolation);
}
OutRampPoints.Add(TempPointData);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetColorRampParameterPointValue_Implementation(
FName InParameterTupleName,
const int32 InPointIndex,
const float InPointPosition,
const FLinearColor& InPointValue,
const EHoudiniPublicAPIRampInterpolationType InInterpolationType,
const bool bInMarkChanged)
{
const float FloatValue = 0;
return SetRampParameterPointValue(
InParameterTupleName, InPointIndex, InPointPosition, FloatValue, InPointValue, InInterpolationType, bInMarkChanged);
}
bool
UHoudiniPublicAPIAssetWrapper::GetColorRampParameterPointValue_Implementation(
FName InParameterTupleName,
const int32 InPointIndex,
float& OutPointPosition,
FLinearColor& OutPointValue,
EHoudiniPublicAPIRampInterpolationType& OutInterpolationType) const
{
float FloatValue = 0;
return GetRampParameterPointValue(
InParameterTupleName, InPointIndex, OutPointPosition, FloatValue, OutPointValue, OutInterpolationType);
}
bool
UHoudiniPublicAPIAssetWrapper::SetColorRampParameterPoints_Implementation(
FName InParameterTupleName,
const TArray<FHoudiniPublicAPIColorRampPoint>& InRampPoints,
const bool bInMarkChanged)
{
const int32 TargetNumPoints = InRampPoints.Num();
if (TargetNumPoints == 0)
{
SetErrorMessage(TEXT("InRampPoints must have at least one entry."));
return false;
}
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
UHoudiniParameterRampColor* ColorRampParam = nullptr;
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::ColorRamp)
{
ColorRampParam = Cast<UHoudiniParameterRampColor>(Param);
if (!IsValid(ColorRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(TEXT("Parameter '%s' is not a color ramp parameter."), *(Param->GetName())));
return false;
}
// If the parameter is not set to auto update, or if cooking is paused, we have to set the cached points and
// not the main points.
// const bool bCookingEnabled = !FHoudiniEngineCommands::IsAssetCookingPaused();
// const bool bUseCachedPoints = (!Param->IsAutoUpdate() || !bCookingEnabled);
const bool bUseCachedPoints = !Param->IsAutoUpdate();
// Set the ramp point count to match the size of InRampPoints
if (!SetRampParameterNumPoints(InParameterTupleName, TargetNumPoints))
return false;
// Get all ramp points (INDEX_NONE == get all points)
TArray<TPair<UObject*, bool>> RampPointData;
if (!FindRampPointData(Param, INDEX_NONE, RampPointData) || RampPointData.Num() <= 0)
return false;
// Check that we fetched the correct number of point data objects
if (RampPointData.Num() != TargetNumPoints)
{
SetErrorMessage(FString::Printf(TEXT("Failed to set the number of ramp points to %d."), TargetNumPoints));
return false;
}
for (int32 Index = 0; Index < TargetNumPoints; ++Index)
{
TPair<UObject*, bool> const& Entry = RampPointData[Index];
UObject* const PointData = Entry.Key;
const bool bIsPointData = Entry.Value;
if (!IsValid(PointData))
return false;
const FHoudiniPublicAPIColorRampPoint& NewRampPoint = InRampPoints[Index];
const EHoudiniRampInterpolationType NewInterpolation = UHoudiniPublicAPI::ToHoudiniRampInterpolationType(
NewRampPoint.Interpolation);
if (bIsPointData)
{
UHoudiniParameterRampColorPoint* ColorPointData = Cast<UHoudiniParameterRampColorPoint>(PointData);
if (!IsValid(ColorPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampColorPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
if (bUseCachedPoints)
{
// When setting the cached points, we set the values directly instead of using the setters, but we set
// the bCaching flag on the parameter and mark the position/value/interpolation parent parameters as changed
if (ColorPointData->Position != NewRampPoint.Position)
{
ColorPointData->Position = NewRampPoint.Position;
ColorRampParam->bCaching = true;
}
if (ColorPointData->Value != NewRampPoint.Value)
{
ColorPointData->Value = NewRampPoint.Value;
ColorRampParam->bCaching = true;
}
if (ColorPointData->Interpolation != NewInterpolation)
{
ColorPointData->Interpolation = NewInterpolation;
ColorRampParam->bCaching = true;
}
}
else
{
// When setting the main points, we set the values using the setters on the point data but still manually
// mark the position/value/interpolation parent parameters as changed
if (ColorPointData->Position != NewRampPoint.Position && ColorPointData->PositionParentParm)
{
ColorPointData->SetPosition(NewRampPoint.Position);
if (bInMarkChanged)
ColorPointData->PositionParentParm->MarkChanged(bInMarkChanged);
}
if (ColorPointData->Value != NewRampPoint.Value && ColorPointData->ValueParentParm)
{
ColorPointData->SetValue(NewRampPoint.Value);
if (bInMarkChanged)
ColorPointData->ValueParentParm->MarkChanged(bInMarkChanged);
}
if (ColorPointData->Interpolation != NewInterpolation && ColorPointData->InterpolationParentParm)
{
ColorPointData->SetInterpolation(NewInterpolation);
if (bInMarkChanged)
ColorPointData->InterpolationParentParm->MarkChanged(bInMarkChanged);
}
}
}
else
{
UHoudiniParameterRampModificationEvent* const Event = Cast<UHoudiniParameterRampModificationEvent>(PointData);
if (!IsValid(Event))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampModificationEvent instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
Event->InsertPosition = NewRampPoint.Position;
Event->InsertColor = NewRampPoint.Value;
Event->InsertInterpolation = NewInterpolation;
}
}
if (bUseCachedPoints)
{
// Update the ramp's widget if it is currently visible/selected
const bool bForceFullUpdate = true;
FHoudiniEngineUtils::UpdateEditorProperties(Param, bForceFullUpdate);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetColorRampParameterPoints_Implementation(
FName InParameterTupleName,
TArray<FHoudiniPublicAPIColorRampPoint>& OutRampPoints) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
UHoudiniParameterRampColor* ColorRampParam = nullptr;
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::ColorRamp)
{
ColorRampParam = Cast<UHoudiniParameterRampColor>(Param);
if (!IsValid(ColorRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(TEXT("Parameter '%s' is not a float ramp parameter."), *(Param->GetName())));
return false;
}
// Get all ramp points (INDEX_NONE == get all points)
TArray<TPair<UObject*, bool>> RampPointData;
if (!FindRampPointData(Param, INDEX_NONE, RampPointData) || RampPointData.Num() <= 0)
return false;
OutRampPoints.Reserve(RampPointData.Num());
const bool bAllowShrinking = false;
OutRampPoints.SetNum(0, bAllowShrinking);
for (TPair<UObject*, bool> const& Entry : RampPointData)
{
UObject* const PointData = Entry.Key;
const bool bIsPointData = Entry.Value;
if (!IsValid(PointData))
return false;
FHoudiniPublicAPIColorRampPoint TempPointData;
if (bIsPointData)
{
UHoudiniParameterRampColorPoint* const ColorPointData = Cast<UHoudiniParameterRampColorPoint>(PointData);
if (!IsValid(ColorPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampColorPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
TempPointData.Position = ColorPointData->Position;
TempPointData.Value = ColorPointData->Value;
TempPointData.Interpolation = UHoudiniPublicAPI::ToHoudiniPublicAPIRampInterpolationType(
ColorPointData->Interpolation);
}
else
{
UHoudiniParameterRampModificationEvent* const Event = Cast<UHoudiniParameterRampModificationEvent>(PointData);
if (!IsValid(Event))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampModificationEvent instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
TempPointData.Position = Event->InsertPosition;
TempPointData.Value = Event->InsertColor;
TempPointData.Interpolation = UHoudiniPublicAPI::ToHoudiniPublicAPIRampInterpolationType(Event->InsertInterpolation);
}
OutRampPoints.Add(TempPointData);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::TriggerButtonParameter_Implementation(FName InButtonParameterName)
{
UHoudiniParameter* Param = FindValidParameterByName(InButtonParameterName);
if (!Param)
return false;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = Param->GetParameterType();
// bool bDidTrigger = false;
if (ParamType == EHoudiniParameterType::Button)
{
UHoudiniParameterButton* ButtonParam = Cast<UHoudiniParameterButton>(Param);
if (!IsValid(ButtonParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
// Marking the button as changed will result in it being triggered/clicked via HAPI
if (!ButtonParam->HasChanged() || !ButtonParam->NeedsToTriggerUpdate())
{
ButtonParam->MarkChanged(true);
// bDidTrigger = true;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter tuple '%s' is not a button."), *InButtonParameterName.ToString()));
return false;
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetParameterTuples_Implementation(TMap<FName, FHoudiniParameterTuple>& OutParameterTuples) const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
const int32 NumParameters = HAC->GetNumParameters();
OutParameterTuples.Empty(NumParameters);
OutParameterTuples.Reserve(NumParameters);
for (int32 Index = 0; Index < NumParameters; ++Index)
{
const UHoudiniParameter* const Param = HAC->GetParameterAt(Index);
const EHoudiniParameterType ParameterType = Param->GetParameterType();
const int32 TupleSize = Param->GetTupleSize();
const FName PTName(Param->GetParameterName());
FHoudiniParameterTuple ParameterTuple;
bool bSkipped = false;
switch (ParameterType)
{
case EHoudiniParameterType::Color:
case EHoudiniParameterType::Float:
{
// Output as float
ParameterTuple.FloatValues.SetNumZeroed(TupleSize);
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
GetFloatParameterValue(PTName, ParameterTuple.FloatValues[TupleIndex], TupleIndex);
}
break;
}
case EHoudiniParameterType::Int:
case EHoudiniParameterType::IntChoice:
case EHoudiniParameterType::MultiParm:
{
// Output as int
ParameterTuple.Int32Values.SetNumZeroed(TupleSize);
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
GetIntParameterValue(PTName, ParameterTuple.Int32Values[TupleIndex], TupleIndex);
}
break;
}
case EHoudiniParameterType::String:
case EHoudiniParameterType::StringChoice:
case EHoudiniParameterType::StringAssetRef:
case EHoudiniParameterType::File:
case EHoudiniParameterType::FileDir:
case EHoudiniParameterType::FileGeo:
case EHoudiniParameterType::FileImage:
{
// Output as string
ParameterTuple.StringValues.SetNumZeroed(TupleSize);
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
GetStringParameterValue(PTName, ParameterTuple.StringValues[TupleIndex], TupleIndex);
}
break;
}
case EHoudiniParameterType::Toggle:
{
// Output as bool
ParameterTuple.BoolValues.SetNumZeroed(TupleSize);
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
GetBoolParameterValue(PTName, ParameterTuple.BoolValues[TupleIndex], TupleIndex);
}
break;
}
case EHoudiniParameterType::ColorRamp:
{
GetColorRampParameterPoints(PTName, ParameterTuple.ColorRampPoints);
break;
}
case EHoudiniParameterType::FloatRamp:
{
GetFloatRampParameterPoints(PTName, ParameterTuple.FloatRampPoints);
break;
}
case EHoudiniParameterType::Button:
case EHoudiniParameterType::ButtonStrip:
case EHoudiniParameterType::Input:
case EHoudiniParameterType::Invalid:
case EHoudiniParameterType::Folder:
case EHoudiniParameterType::FolderList:
case EHoudiniParameterType::Label:
case EHoudiniParameterType::Separator:
default:
// Skipped
bSkipped = true;
break;
}
if (!bSkipped)
OutParameterTuples.Add(PTName, ParameterTuple);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetParameterTuples_Implementation(const TMap<FName, FHoudiniParameterTuple>& InParameterTuples)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
bool bSuccess = true;
for (const TPair<FName, FHoudiniParameterTuple>& Entry : InParameterTuples)
{
const FName& ParameterTupleName = Entry.Key;
const FHoudiniParameterTuple& ParameterTuple = Entry.Value;
if (ParameterTuple.BoolValues.Num() > 0)
{
// Set as bool
const int32 TupleSize = ParameterTuple.BoolValues.Num();
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
if (!SetBoolParameterValue(ParameterTupleName, ParameterTuple.BoolValues[TupleIndex], TupleIndex))
{
SetErrorMessage(FString::Printf(
TEXT("SetParameterTuples: Failed to set %s as a bool at tuple index %d."), *ParameterTupleName.ToString(), TupleIndex));
bSuccess = false;
break;
}
}
}
else if (ParameterTuple.FloatValues.Num() > 0)
{
// Set as float
const int32 TupleSize = ParameterTuple.FloatValues.Num();
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
if (!SetFloatParameterValue(ParameterTupleName, ParameterTuple.FloatValues[TupleIndex], TupleIndex))
{
SetErrorMessage(FString::Printf(
TEXT("SetParameterTuples: Failed to set %s as a float at tuple index %d."),
*ParameterTupleName.ToString(), TupleIndex));
bSuccess = false;
break;
}
}
}
else if (ParameterTuple.Int32Values.Num() > 0)
{
// Set as int
const int32 TupleSize = ParameterTuple.Int32Values.Num();
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
if (!SetIntParameterValue(ParameterTupleName, ParameterTuple.Int32Values[TupleIndex], TupleIndex))
{
SetErrorMessage(FString::Printf(
TEXT("SetParameterTuples: Failed to set %s as a int at tuple index %d."),
*ParameterTupleName.ToString(), TupleIndex));
bSuccess = false;
break;
}
}
}
else if (ParameterTuple.StringValues.Num() > 0)
{
// Set as string
const int32 TupleSize = ParameterTuple.StringValues.Num();
for (int32 TupleIndex = 0; TupleIndex < TupleSize; ++TupleIndex)
{
if (!SetStringParameterValue(ParameterTupleName, ParameterTuple.StringValues[TupleIndex], TupleIndex))
{
SetErrorMessage(FString::Printf(
TEXT("SetParameterTuples: Failed to set %s as a string at tuple index %d."),
*ParameterTupleName.ToString(), TupleIndex));
bSuccess = false;
break;
}
}
}
else if (ParameterTuple.FloatRampPoints.Num() > 0)
{
// Set as a float ramp
if (!SetFloatRampParameterPoints(ParameterTupleName, ParameterTuple.FloatRampPoints))
bSuccess = false;
}
else if (ParameterTuple.ColorRampPoints.Num() > 0)
{
// Set as a color ramp
if (!SetColorRampParameterPoints(ParameterTupleName, ParameterTuple.ColorRampPoints))
bSuccess = false;
}
}
return bSuccess;
}
UHoudiniPublicAPIInput*
UHoudiniPublicAPIAssetWrapper::CreateEmptyInput_Implementation(TSubclassOf<UHoudiniPublicAPIInput> InInputClass)
{
UHoudiniPublicAPI* API = UHoudiniPublicAPIBlueprintLib::GetAPI();
if (!IsValid(API))
return nullptr;
UHoudiniPublicAPIInput* const NewInput = API->CreateEmptyInput(InInputClass, this);
if (!IsValid(NewInput))
{
SetErrorMessage(FString::Printf(
TEXT("Failed to create a new input of class '%s'."),
*(IsValid(InInputClass.Get()) ? InInputClass->GetName() : FString())));
return nullptr;
}
return NewInput;
}
int32
UHoudiniPublicAPIAssetWrapper::GetNumNodeInputs_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return -1;
int32 NumNodeInputs = 0;
const int32 NumInputs = HAC->GetNumInputs();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
UHoudiniInput const* const Input = HAC->GetInputAt(Index);
if (!IsValid(Input))
continue;
if (!Input->IsObjectPathParameter())
NumNodeInputs++;
}
return NumNodeInputs;
}
bool
UHoudiniPublicAPIAssetWrapper::SetInputAtIndex_Implementation(const int32 InNodeInputIndex, const UHoudiniPublicAPIInput* InInput)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
UHoudiniInput* HoudiniInput = GetHoudiniNodeInputByIndex(InNodeInputIndex);
if (!IsValid(HoudiniInput))
{
SetErrorMessage(FString::Printf(
TEXT("SetInputAtIndex: Could not find a HoudiniInput for InNodeInputIndex %d. Has the HDA been instantiated?"),
InNodeInputIndex));
return false;
}
return PopulateHoudiniInput(InInput, HoudiniInput);
}
bool
UHoudiniPublicAPIAssetWrapper::GetInputAtIndex_Implementation(const int32 InNodeInputIndex, UHoudiniPublicAPIInput*& OutInput)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
const UHoudiniInput* HoudiniInput = GetHoudiniNodeInputByIndex(InNodeInputIndex);
if (!IsValid(HoudiniInput))
{
SetErrorMessage(FString::Printf(
TEXT("GetInputAtIndex: Could not find a HoudiniInput for InNodeInputIndex %d. Has the HDA been instantiated?"),
InNodeInputIndex));
return false;
}
UHoudiniPublicAPIInput* APIInput = nullptr;
const bool bSuccessfullyCopied = CreateAndPopulateAPIInput(HoudiniInput, APIInput);
if (!IsValid(APIInput))
return false;
OutInput = APIInput;
return bSuccessfullyCopied;
}
bool
UHoudiniPublicAPIAssetWrapper::SetInputsAtIndices_Implementation(const TMap<int32, UHoudiniPublicAPIInput*>& InInputs)
{
bool bAnyFailures = false;
for (const TPair<int32, UHoudiniPublicAPIInput*>& Entry : InInputs)
{
if (!SetInputAtIndex(Entry.Key, Entry.Value))
{
SetErrorMessage(FString::Printf(
TEXT("SetInputsAtIndices: Failed to set node input at index %d"), Entry.Key));
bAnyFailures = true;
}
}
return !bAnyFailures;
}
bool
UHoudiniPublicAPIAssetWrapper::GetInputsAtIndices_Implementation(TMap<int32, UHoudiniPublicAPIInput*>& OutInputs)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
bool bAnyFailures = false;
const int32 NumInputs = HAC->GetNumInputs();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
UHoudiniInput const* const HoudiniInput = HAC->GetInputAt(Index);
if (!IsValid(HoudiniInput) || HoudiniInput->IsObjectPathParameter())
continue;
UHoudiniPublicAPIInput* APIInput = nullptr;
const bool bSuccessfullyCopied = CreateAndPopulateAPIInput(HoudiniInput, APIInput);
if (!IsValid(APIInput))
{
bAnyFailures = true;
continue;
}
if (!bSuccessfullyCopied)
bAnyFailures = true;
OutInputs.Add(HoudiniInput->GetInputIndex(), APIInput);
}
return !bAnyFailures;
}
bool
UHoudiniPublicAPIAssetWrapper::SetInputParameter_Implementation(const FName& InParameterName, const UHoudiniPublicAPIInput* InInput)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
UHoudiniInput* HoudiniInput = FindValidHoudiniNodeInputParameter(InParameterName);
if (!IsValid(HoudiniInput))
{
SetErrorMessage(FString::Printf(
TEXT("SetInputParameter: Could not find a parameter-based HoudiniInput with name %s. Has the HDA been instantiated?"),
*(InParameterName.ToString())));
return false;
}
return PopulateHoudiniInput(InInput, HoudiniInput);
}
bool
UHoudiniPublicAPIAssetWrapper::GetInputParameter_Implementation(const FName& InParameterName, UHoudiniPublicAPIInput*& OutInput)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
const UHoudiniInput* HoudiniInput = FindValidHoudiniNodeInputParameter(InParameterName);
if (!IsValid(HoudiniInput))
{
SetErrorMessage(FString::Printf(
TEXT("GetInputParameter: Could not find a parameter-based HoudiniInput with name %s. Has the HDA been instantiated?"),
*(InParameterName.ToString())));
return false;
}
UHoudiniPublicAPIInput* APIInput = nullptr;
const bool bSuccessfullyCopied = CreateAndPopulateAPIInput(HoudiniInput, APIInput);
if (!IsValid(APIInput))
return false;
OutInput = APIInput;
return bSuccessfullyCopied;
}
bool
UHoudiniPublicAPIAssetWrapper::SetInputParameters_Implementation(const TMap<FName, UHoudiniPublicAPIInput*>& InInputs)
{
bool bAnyFailures = false;
for (const TPair<FName, UHoudiniPublicAPIInput*>& Entry : InInputs)
{
if (!SetInputParameter(Entry.Key, Entry.Value))
{
SetErrorMessage(FString::Printf(
TEXT("SetInputParameters: Failed to set input parameter %s"), *(Entry.Key.ToString())));
bAnyFailures = true;
}
}
return !bAnyFailures;
}
bool
UHoudiniPublicAPIAssetWrapper::GetInputParameters_Implementation(TMap<FName, UHoudiniPublicAPIInput*>& OutInputs)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
bool bAnyFailures = false;
const int32 NumInputs = HAC->GetNumInputs();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
UHoudiniInput const* const HoudiniInput = HAC->GetInputAt(Index);
if (!IsValid(HoudiniInput) || !HoudiniInput->IsObjectPathParameter())
continue;
UHoudiniPublicAPIInput* APIInput = nullptr;
const bool bSuccessfullyCopied = CreateAndPopulateAPIInput(HoudiniInput, APIInput);
if (!IsValid(APIInput))
{
bAnyFailures = true;
continue;
}
if (!bSuccessfullyCopied)
bAnyFailures = true;
OutInputs.Add(FName(HoudiniInput->GetName()), APIInput);
}
return !bAnyFailures;
}
int32
UHoudiniPublicAPIAssetWrapper::GetNumOutputs_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return -1;
return HAC->GetNumOutputs();
}
EHoudiniOutputType
UHoudiniPublicAPIAssetWrapper::GetOutputTypeAt_Implementation(const int32 InIndex) const
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return EHoudiniOutputType::Invalid;
return Output->GetType();
}
bool
UHoudiniPublicAPIAssetWrapper::GetOutputIdentifiersAt_Implementation(const int32 InIndex, TArray<FHoudiniPublicAPIOutputObjectIdentifier>& OutIdentifiers) const
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return false;
const TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& OutputObjects = Output->GetOutputObjects();
OutIdentifiers.Empty();
OutIdentifiers.Reserve(OutputObjects.Num());
for (const TPair<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& Entry : OutputObjects)
{
OutIdentifiers.Add(FHoudiniPublicAPIOutputObjectIdentifier(Entry.Key));
}
return true;
}
UObject*
UHoudiniPublicAPIAssetWrapper::GetOutputObjectAt_Implementation(const int32 InIndex, const FHoudiniPublicAPIOutputObjectIdentifier& InIdentifier) const
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return nullptr;
const TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& OutputObjects = Output->GetOutputObjects();
FHoudiniOutputObject const* const OutputObject = OutputObjects.Find(InIdentifier.GetIdentifier());
if (!OutputObject)
return nullptr;
return OutputObject->bProxyIsCurrent ? OutputObject->ProxyObject : OutputObject->OutputObject;
}
UObject*
UHoudiniPublicAPIAssetWrapper::GetOutputComponentAt_Implementation(const int32 InIndex, const FHoudiniPublicAPIOutputObjectIdentifier& InIdentifier) const
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return nullptr;
const TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& OutputObjects = Output->GetOutputObjects();
FHoudiniOutputObject const* const OutputObject = OutputObjects.Find(InIdentifier.GetIdentifier());
if (!OutputObject)
return nullptr;
return OutputObject->bProxyIsCurrent ? OutputObject->ProxyComponent : OutputObject->OutputComponent;
}
bool
UHoudiniPublicAPIAssetWrapper::GetOutputBakeNameFallbackAt_Implementation(const int32 InIndex, const FHoudiniPublicAPIOutputObjectIdentifier& InIdentifier, FString& OutBakeName) const
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return false;
const TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& OutputObjects = Output->GetOutputObjects();
FHoudiniOutputObject const* const OutputObject =OutputObjects.Find(InIdentifier.GetIdentifier());
if (!OutputObject)
return false;
OutBakeName = OutputObject->BakeName;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetOutputBakeNameFallbackAt_Implementation(const int32 InIndex, const FHoudiniPublicAPIOutputObjectIdentifier& InIdentifier, const FString& InBakeName)
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return false;
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& OutputObjects = Output->GetOutputObjects();
FHoudiniOutputObject* const OutputObject = OutputObjects.Find(InIdentifier.GetIdentifier());
if (!OutputObject)
return false;
OutputObject->BakeName = InBakeName;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::BakeOutputObjectAt_Implementation(const int32 InIndex, const FHoudiniPublicAPIOutputObjectIdentifier& InIdentifier, const FName InBakeName, const EHoudiniLandscapeOutputBakeType InLandscapeBakeType)
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return false;
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject>& OutputObjects = Output->GetOutputObjects();
const FHoudiniOutputObjectIdentifier& Identifier = InIdentifier.GetIdentifier();
FHoudiniOutputObject* const OutputObject = OutputObjects.Find(Identifier);
if (!OutputObject)
{
SetErrorMessage(FString::Printf(
TEXT("BakeOutputObjectAt: Could not find an output object using the specified identifier.")));
return false;
}
// Determine the object to bake (this is different depending on landscape, curve or mesh
const EHoudiniOutputType OutputType = Output->GetType();
if (OutputType == EHoudiniOutputType::Mesh && OutputObject->bProxyIsCurrent)
{
// Output is currently a proxy, this cannot be baked without cooking first.
SetErrorMessage(FString::Printf(
TEXT("BakeOutputObjectAt: Object is a proxy mesh, please refine it before baking to CB.")));
return false;
}
UObject* ObjectToBake = nullptr;
switch (OutputType)
{
case EHoudiniOutputType::Landscape:
{
UHoudiniLandscapePtr* const LandscapePtr = Cast<UHoudiniLandscapePtr>(OutputObject->OutputObject);
if (IsValid(LandscapePtr))
{
ObjectToBake = LandscapePtr->LandscapeSoftPtr.IsValid() ? LandscapePtr->LandscapeSoftPtr.Get() : nullptr;
}
break;
}
case EHoudiniOutputType::Curve:
ObjectToBake = OutputObject->OutputComponent;
break;
case EHoudiniOutputType::Mesh:
ObjectToBake = OutputObject->OutputObject;
break;
case EHoudiniOutputType::Instancer:
case EHoudiniOutputType::Skeletal:
case EHoudiniOutputType::Invalid:
default:
SetErrorMessage(FString::Printf(
TEXT("BakeOutputObjectAt: unsupported output type (%d) for baking to CB."), OutputType));
return false;
}
if (!IsValid(ObjectToBake))
{
SetErrorMessage(FString::Printf(TEXT("BakeOutputObjectAt: Could not find a valid object to bake to CB.")));
return false;
}
// Find the corresponding HGPO in the output
FHoudiniGeoPartObject HoudiniGeoPartObject;
for (const auto& HGPO : Output->GetHoudiniGeoPartObjects())
{
if (!Identifier.Matches(HGPO))
continue;
HoudiniGeoPartObject = HGPO;
break;
}
if (!HoudiniGeoPartObject.IsValid())
{
SetErrorMessage(TEXT("BakeOutputObjectAt: Could not find a valid HGPO for the output object. Please recook."));
return false;
}
// Determine the HoudiniAssetName
FString HoudiniAssetName;
if (HAC->GetOwner() && (HAC->GetOwner()->IsPendingKill()))
{
// If the HAC has a valid owner, use the owner's name
// TODO: Should this be more specific, such as checking for a HoudiniAssetActor?
HoudiniAssetName = HAC->GetOwner()->GetName();
}
else if (HAC->GetHoudiniAsset())
{
// Otherwise, if the HAC has a valid HoudiniAsset, use its name
HoudiniAssetName = HAC->GetHoudiniAsset()->GetName();
}
else
{
// Fallback to the HAC's name
HoudiniAssetName = HAC->GetName();
}
TArray<UHoudiniOutput*> AllOutputs;
HAC->GetOutputs(AllOutputs);
FHoudiniOutputDetails::OnBakeOutputObject(
InBakeName.IsNone() ? OutputObject->BakeName : InBakeName.ToString(),
ObjectToBake,
Identifier,
*OutputObject,
HoudiniGeoPartObject,
HAC,
HoudiniAssetName,
HAC->BakeFolder.Path,
HAC->TemporaryCookFolder.Path,
OutputType,
InLandscapeBakeType,
AllOutputs);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::HasAnyCurrentProxyOutput_Implementation() const
{
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return false;
return HAC->HasAnyCurrentProxyOutput();
}
bool
UHoudiniPublicAPIAssetWrapper::HasAnyCurrentProxyOutputAt_Implementation(const int32 InIndex) const
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return false;
return Output->HasAnyCurrentProxy();
}
bool
UHoudiniPublicAPIAssetWrapper::IsOutputCurrentProxyAt_Implementation(const int32 InIndex, const FHoudiniPublicAPIOutputObjectIdentifier& InIdentifier) const
{
UHoudiniOutput* Output = nullptr;
if (!GetValidOutputAtWithError(InIndex, Output))
return false;
return Output->IsProxyCurrent(InIdentifier.GetIdentifier());
}
EHoudiniProxyRefineRequestResult
UHoudiniPublicAPIAssetWrapper::RefineAllCurrentProxyOutputs_Implementation(const bool bInSilent)
{
AHoudiniAssetActor* AssetActor = nullptr;
if (!GetValidHoudiniAssetActorWithError(AssetActor))
return EHoudiniProxyRefineRequestResult::Invalid;
return FHoudiniEngineCommands::RefineHoudiniProxyMeshActorArrayToStaticMeshes({ AssetActor }, bInSilent);
}
bool
UHoudiniPublicAPIAssetWrapper::HasPDGAssetLink_Implementation() const
{
return IsValid(GetHoudiniPDGAssetLink());
}
bool
UHoudiniPublicAPIAssetWrapper::GetPDGTOPNetworkPaths_Implementation(TArray<FString>& OutTOPNetworkPaths) const
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
const uint32 NumNetworks = AssetLink->AllTOPNetworks.Num();
OutTOPNetworkPaths.Empty(NumNetworks);
for (UTOPNetwork const* const TOPNet : AssetLink->AllTOPNetworks)
{
OutTOPNetworkPaths.Add(TOPNet->NodePath);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetPDGTOPNodePaths_Implementation(const FString& InNetworkRelativePath, TArray<FString>& OutTOPNodePaths) const
{
int32 NetworkIndex = INDEX_NONE;
UTOPNetwork* TOPNet = nullptr;
if (!GetValidTOPNetworkByPathWithError(InNetworkRelativePath, NetworkIndex, TOPNet))
return false;
const uint32 NumNodes = TOPNet->AllTOPNodes.Num();
OutTOPNodePaths.Empty(NumNodes);
for (UTOPNode const* const TOPNode : TOPNet->AllTOPNodes)
{
OutTOPNodePaths.Add(TOPNode->NodePath);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::PDGDirtyAllNetworks_Implementation()
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
for (UTOPNetwork* const TOPNetwork : AssetLink->AllTOPNetworks)
{
if (!IsValid(TOPNetwork))
continue;
FHoudiniPDGManager::DirtyAll(TOPNetwork);
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::PDGDirtyNetwork_Implementation(const FString& InNetworkRelativePath)
{
int32 NetworkIndex = INDEX_NONE;
UTOPNetwork* TOPNet = nullptr;
if (!GetValidTOPNetworkByPathWithError(InNetworkRelativePath, NetworkIndex, TOPNet))
return false;
FHoudiniPDGManager::DirtyAll(TOPNet);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::PDGDirtyNode_Implementation(const FString& InNetworkRelativePath, const FString& InNodeRelativePath)
{
int32 NetworkIndex = INDEX_NONE;
int32 NodeIndex = INDEX_NONE;
UTOPNode* TOPNode = nullptr;
if (!GetValidTOPNodeByPathWithError(InNetworkRelativePath, InNodeRelativePath, NetworkIndex, NodeIndex, TOPNode))
return false;
FHoudiniPDGManager::DirtyTOPNode(TOPNode);
return true;
}
// bool
// UHoudiniPublicAPIAssetWrapper::PDGCookOutputsForAllNetworks_Implementation()
// {
// UHoudiniPDGAssetLink* const AssetLink = GetHoudiniPDGAssetLink();
// if (!IsValid(AssetLink))
// return false;
//
// for (UTOPNetwork* const TOPNetwork : AssetLink->AllTOPNetworks)
// {
// if (!IsValid(TOPNetwork))
// continue;
//
// FHoudiniPDGManager::CookOutput(TOPNetwork);
// }
//
// return true;
// }
bool
UHoudiniPublicAPIAssetWrapper::PDGCookOutputsForNetwork_Implementation(const FString& InNetworkRelativePath)
{
int32 NetworkIndex = INDEX_NONE;
UTOPNetwork* TOPNet = nullptr;
if (!GetValidTOPNetworkByPathWithError(InNetworkRelativePath, NetworkIndex, TOPNet))
return false;
FHoudiniPDGManager::CookOutput(TOPNet);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::PDGCookNode_Implementation(const FString& InNetworkRelativePath, const FString& InNodeRelativePath)
{
int32 NetworkIndex = INDEX_NONE;
int32 NodeIndex = INDEX_NONE;
UTOPNode* TOPNode = nullptr;
if (!GetValidTOPNodeByPathWithError(InNetworkRelativePath, InNodeRelativePath, NetworkIndex, NodeIndex, TOPNode))
return false;
FHoudiniPDGManager::CookTOPNode(TOPNode);
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::PDGBakeAllOutputs_Implementation()
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
return PDGBakeAllOutputsWithSettings(
AssetLink->HoudiniEngineBakeOption,
AssetLink->PDGBakeSelectionOption,
AssetLink->PDGBakePackageReplaceMode,
AssetLink->bRecenterBakedActors);
}
bool
UHoudiniPublicAPIAssetWrapper::PDGBakeAllOutputsWithSettings_Implementation(
const EHoudiniEngineBakeOption InBakeOption,
const EPDGBakeSelectionOption InBakeSelection,
const EPDGBakePackageReplaceModeOption InBakeReplacementMode,
const bool bInRecenterBakedActors)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
bool bBakeSuccess = false;
switch (InBakeOption)
{
case EHoudiniEngineBakeOption::ToActor:
bBakeSuccess = FHoudiniEngineBakeUtils::BakePDGAssetLinkOutputsKeepActors(
AssetLink,
InBakeSelection,
InBakeReplacementMode,
bInRecenterBakedActors);
break;
case EHoudiniEngineBakeOption::ToBlueprint:
bBakeSuccess = FHoudiniEngineBakeUtils::BakePDGAssetLinkBlueprints(
AssetLink,
InBakeSelection,
InBakeReplacementMode,
bInRecenterBakedActors);
break;
default:
bBakeSuccess = false;
break;
}
return bBakeSuccess;
}
bool
UHoudiniPublicAPIAssetWrapper::SetPDGAutoBakeEnabled_Implementation(const bool bInAutoBakeEnabled)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
AssetLink->bBakeAfterAllWorkResultObjectsLoaded = bInAutoBakeEnabled;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::IsPDGAutoBakeEnabled_Implementation() const
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
return AssetLink->bBakeAfterAllWorkResultObjectsLoaded;
}
bool
UHoudiniPublicAPIAssetWrapper::SetPDGBakeMethod_Implementation(const EHoudiniEngineBakeOption InBakeMethod)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
AssetLink->HoudiniEngineBakeOption = InBakeMethod;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetPDGBakeMethod_Implementation(EHoudiniEngineBakeOption& OutBakeMethod)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
OutBakeMethod = AssetLink->HoudiniEngineBakeOption;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetPDGBakeSelection_Implementation(const EPDGBakeSelectionOption InBakeSelection)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
AssetLink->PDGBakeSelectionOption = InBakeSelection;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetPDGBakeSelection_Implementation(EPDGBakeSelectionOption& OutBakeSelection)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
OutBakeSelection = AssetLink->PDGBakeSelectionOption;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::SetPDGRecenterBakedActors_Implementation(const bool bInRecenterBakedActors)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
AssetLink->bRecenterBakedActors = bInRecenterBakedActors;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetPDGRecenterBakedActors_Implementation() const
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
return AssetLink->bRecenterBakedActors;
}
bool
UHoudiniPublicAPIAssetWrapper::SetPDGBakingReplacementMode_Implementation(const EPDGBakePackageReplaceModeOption InBakingReplacementMode)
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
AssetLink->PDGBakePackageReplaceMode = InBakingReplacementMode;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetPDGBakingReplacementMode_Implementation(EPDGBakePackageReplaceModeOption& OutBakingReplacementMode) const
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
OutBakingReplacementMode = AssetLink->PDGBakePackageReplaceMode;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::BindToPDGAssetLink()
{
if (bAssetLinkSetupAttemptComplete)
return true;
UHoudiniPDGAssetLink* const PDGAssetLink = GetHoudiniPDGAssetLink();
if (IsValid(PDGAssetLink))
{
OnPDGPostTOPNetworkCookDelegateHandle = PDGAssetLink->GetOnPostTOPNetworkCookDelegate().AddUFunction(this, TEXT("HandleOnHoudiniPDGAssetLinkTOPNetPostCook"));
OnPDGPostBakeDelegateHandle = PDGAssetLink->GetOnPostBakeDelegate().AddUFunction(this, TEXT("HandleOnHoudiniPDGAssetLinkPostBake"));
bAssetLinkSetupAttemptComplete = true;
return true;
}
return false;
}
void
UHoudiniPublicAPIAssetWrapper::HandleOnHoudiniAssetComponentStateChange(UHoudiniAssetComponent* InHAC, const EHoudiniAssetState InFromState, const EHoudiniAssetState InToState)
{
if (!IsValid(InHAC))
return;
if (InHAC != GetHoudiniAssetComponent())
{
SetErrorMessage(FString::Printf(
TEXT("HandleOnHoudiniAssetComponentStateChange: unexpected InHAC: %s, expected the wrapper's HAC."),
IsValid(InHAC) ? *InHAC->GetName() : TEXT("")));
return;
}
if (InToState == EHoudiniAssetState::PreInstantiation)
{
if (OnPreInstantiationDelegate.IsBound())
OnPreInstantiationDelegate.Broadcast(this);
}
if (InFromState == EHoudiniAssetState::Instantiating && InToState == EHoudiniAssetState::PreCook)
{
// PDG link setup / bindings: we have to wait until post instantiation to check if we have an asset link and
// configure bindings
if (!bAssetLinkSetupAttemptComplete)
{
BindToPDGAssetLink();
bAssetLinkSetupAttemptComplete = true;
}
if (OnPostInstantiationDelegate.IsBound())
OnPostInstantiationDelegate.Broadcast(this);
}
if (InFromState == EHoudiniAssetState::PreProcess)
{
if (OnPreProcessStateExitedDelegate.IsBound())
OnPreProcessStateExitedDelegate.Broadcast(this);
}
if (InFromState == EHoudiniAssetState::Processing && InToState == EHoudiniAssetState::None)
{
if (OnPostProcessingDelegate.IsBound())
OnPostProcessingDelegate.Broadcast(this);
}
}
void
UHoudiniPublicAPIAssetWrapper::HandleOnHoudiniAssetComponentPostCook(UHoudiniAssetComponent* InHAC, const bool bInCookSuccess)
{
if (!IsValid(InHAC))
return;
if (InHAC != GetHoudiniAssetComponent())
{
SetErrorMessage(FString::Printf(
TEXT("HandleOnHoudiniAssetComponentPostCook: unexpected InHAC: %s, expected the wrapper's HAC."),
IsValid(InHAC) ? *InHAC->GetName() : TEXT("")));
return;
}
if (OnPostCookDelegate.IsBound())
OnPostCookDelegate.Broadcast(this, bInCookSuccess);
}
void
UHoudiniPublicAPIAssetWrapper::HandleOnHoudiniAssetComponentPostBake(UHoudiniAssetComponent* InHAC, const bool bInBakeSuccess)
{
if (!IsValid(InHAC))
return;
if (InHAC != GetHoudiniAssetComponent())
{
SetErrorMessage(FString::Printf(
TEXT("HandleOnHoudiniAssetComponentPostBake: unexpected InHAC: %s, expected the wrapper's HAC."),
IsValid(InHAC) ? *InHAC->GetName() : TEXT("")));
return;
}
if (OnPostBakeDelegate.IsBound())
OnPostBakeDelegate.Broadcast(this, bInBakeSuccess);
}
void
UHoudiniPublicAPIAssetWrapper::HandleOnHoudiniPDGAssetLinkTOPNetPostCook(UHoudiniPDGAssetLink* InPDGAssetLink, UTOPNetwork* InTOPNet, const bool bInAnyWorkItemsFailed)
{
if (!IsValid(InPDGAssetLink))
return;
if (InPDGAssetLink != GetHoudiniPDGAssetLink())
{
SetErrorMessage(FString::Printf(
TEXT("HandleOnHoudiniPDGAssetLinkTOPNetPostCook: unexpected InPDGAssetLink: %s, expected the wrapper's PDGAssetLink."),
IsValid(InPDGAssetLink) ? *InPDGAssetLink->GetName() : TEXT("")));
return;
}
if (OnPostPDGTOPNetworkCookDelegate.IsBound())
OnPostPDGTOPNetworkCookDelegate.Broadcast(this, !bInAnyWorkItemsFailed);
}
void
UHoudiniPublicAPIAssetWrapper::HandleOnHoudiniPDGAssetLinkPostBake(UHoudiniPDGAssetLink* InPDGAssetLink, const bool bInBakeSuccess)
{
if (!IsValid(InPDGAssetLink))
return;
if (InPDGAssetLink != GetHoudiniPDGAssetLink())
{
SetErrorMessage(FString::Printf(
TEXT("HandleOnHoudiniPDGAssetLinkPostBake: unexpected InPDGAssetLink: %s, expected the wrapper's PDGAssetLink."),
IsValid(InPDGAssetLink) ? *InPDGAssetLink->GetName() : TEXT("")));
return;
}
if (OnPostPDGBakeDelegate.IsBound())
OnPostPDGBakeDelegate.Broadcast(this, bInBakeSuccess);
}
void
UHoudiniPublicAPIAssetWrapper::HandleOnHoudiniProxyMeshesRefinedGlobal(UHoudiniAssetComponent* InHAC, const EHoudiniProxyRefineResult InResult)
{
if (!IsValid(InHAC))
return;
if (InHAC != GetHoudiniAssetComponent())
return;
if (OnProxyMeshesRefinedDelegate.IsBound())
OnProxyMeshesRefinedDelegate.Broadcast(this, InResult);
}
UHoudiniParameter*
UHoudiniPublicAPIAssetWrapper::FindValidParameterByName(const FName& InParameterTupleName) const
{
AActor* const Actor = GetHoudiniAssetActor();
const FString ActorName = IsValid(Actor) ? Actor->GetName() : FString();
UHoudiniAssetComponent* const HAC = GetHoudiniAssetComponent();
if (!IsValid(HAC))
{
SetErrorMessage(FString::Printf(TEXT("Could not find HAC on Actor '%s'"), *ActorName));
return nullptr;
}
UHoudiniParameter* const Param = HAC->FindParameterByName(InParameterTupleName.ToString());
if (!IsValid(Param))
{
SetErrorMessage(FString::Printf(
TEXT("Could not find valid parameter tuple '%s' on '%s'."),
*InParameterTupleName.ToString(), *ActorName));
return nullptr;
}
return Param;
}
bool
UHoudiniPublicAPIAssetWrapper::FindRampPointData(
UHoudiniParameter* const InParam,
const int32 InIndex,
TArray<TPair<UObject*, bool>>& OutPointData) const
{
if (!IsValid(InParam))
return false;
// If the parameter is not set to auto update, or if cooking is paused, we have to set the cached points and
// not the main points.
// const bool bCookingEnabled = !FHoudiniEngineCommands::IsAssetCookingPaused();
// const bool bUseCachedPoints = (!InParam->IsAutoUpdate() || !bCookingEnabled);
const bool bUseCachedPoints = !InParam->IsAutoUpdate();
const bool bFetchAllPoints = (InIndex == INDEX_NONE);
UHoudiniParameterRampFloat* FloatRampParam = nullptr;
UHoudiniParameterRampColor* ColorRampParam = nullptr;
// Handle all the cases where the underlying parameter value is an int or bool
const EHoudiniParameterType ParamType = InParam->GetParameterType();
if (ParamType == EHoudiniParameterType::FloatRamp)
{
FloatRampParam = Cast<UHoudiniParameterRampFloat>(InParam);
if (!IsValid(FloatRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *InParam->GetClass()->GetName(), ParamType));
return false;
}
}
else if (ParamType == EHoudiniParameterType::ColorRamp)
{
ColorRampParam = Cast<UHoudiniParameterRampColor>(InParam);
if (!IsValid(ColorRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *InParam->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(
TEXT("Parameter '%s' is not a float or color ramp parameter."), *(InParam->GetName())));
return false;
}
if (bUseCachedPoints)
{
// When using the cached points we only have to resize the array
if (FloatRampParam)
{
if (!bFetchAllPoints && !FloatRampParam->CachedPoints.IsValidIndex(InIndex))
{
SetErrorMessage(FString::Printf(
TEXT("Ramp point index %d is out of range [0, %d]."), InIndex, FloatRampParam->CachedPoints.Num() - 1));
return false;
}
if (bFetchAllPoints)
{
// Get all points
OutPointData.Reserve(FloatRampParam->CachedPoints.Num());
const bool bAllowShrinking = false;
OutPointData.SetNum(0, bAllowShrinking);
for (UHoudiniParameterRampFloatPoint* const RampPoint : FloatRampParam->CachedPoints)
{
const bool bIsPointData = true;
OutPointData.Add(TPair<UObject*, bool>(RampPoint, bIsPointData));
}
}
else
{
OutPointData.Reserve(1);
const bool bAllowShrinking = false;
OutPointData.SetNum(0, bAllowShrinking);
const bool bIsPointData = true;
OutPointData.Add(TPair<UObject*, bool>(FloatRampParam->CachedPoints[InIndex], bIsPointData));
}
return true;
}
else
{
if (!bFetchAllPoints && !ColorRampParam->CachedPoints.IsValidIndex(InIndex))
{
SetErrorMessage(FString::Printf(
TEXT("Ramp point index %d is out of range [0, %d]."), InIndex, ColorRampParam->CachedPoints.Num() - 1));
return false;
}
if (bFetchAllPoints)
{
// Get all points
OutPointData.Reserve(ColorRampParam->CachedPoints.Num());
const bool bAllowShrinking = false;
OutPointData.SetNum(0, bAllowShrinking);
for (UHoudiniParameterRampColorPoint* const RampPoint : ColorRampParam->CachedPoints)
{
const bool bIsPointData = true;
OutPointData.Add(TPair<UObject*, bool>(RampPoint, bIsPointData));
}
}
else
{
OutPointData.Reserve(1);
const bool bAllowShrinking = false;
OutPointData.SetNum(0, bAllowShrinking);
const bool bIsPointData = true;
OutPointData.Add(TPair<UObject*, bool>(ColorRampParam->CachedPoints[InIndex], bIsPointData));
}
return true;
}
}
else
{
TSet<int32> InstanceIndexesPendingDelete;
int32 NumInsertOps = 0;
TArray<UHoudiniParameterRampModificationEvent*>& ModificationEvents = FloatRampParam ? FloatRampParam->ModificationEvents : ColorRampParam->ModificationEvents;
for (UHoudiniParameterRampModificationEvent const* const Event : ModificationEvents)
{
if (!IsValid(Event))
continue;
if (Event->IsInsertEvent())
NumInsertOps++;
else if (Event->IsDeleteEvent())
InstanceIndexesPendingDelete.Add(Event->DeleteInstanceIndex);
}
const int32 PointsArraySize = FloatRampParam ? FloatRampParam->Points.Num() : ColorRampParam->Points.Num();
const int32 NumActivePointsInArray = PointsArraySize - InstanceIndexesPendingDelete.Num();
const int32 TotalNumPoints = NumActivePointsInArray + NumInsertOps;
// Reserve the expected amount of space needed in the array and reset / destruct existing items so we can
// add from index 0
if (bFetchAllPoints)
OutPointData.Reserve(TotalNumPoints);
else
OutPointData.Reserve(1);
const bool bAllowShrinking = false;
OutPointData.SetNum(0, bAllowShrinking);
if (bFetchAllPoints || InIndex < NumActivePointsInArray)
{
// Getting all points or point is in the points array
if (FloatRampParam)
{
const int32 ArraySize = FloatRampParam->Points.Num();
int32 PointIndex = 0;
for (int32 Index = 0; Index < ArraySize && (bFetchAllPoints || PointIndex <= InIndex); ++Index)
{
UHoudiniParameterRampFloatPoint* const PointData = FloatRampParam->Points[Index];
const bool bIsDeletedPoint = (PointData && InstanceIndexesPendingDelete.Contains(PointData->InstanceIndex));
if (!bIsDeletedPoint)
{
if (PointIndex == InIndex || bFetchAllPoints)
{
const bool bIsPointData = true;
OutPointData.Add(TPair<UObject*, bool>(PointData, bIsPointData));
}
// If we are fetching only the point at InIndex, then we are done here
if (PointIndex == InIndex)
return true;
PointIndex++;
}
}
}
else
{
const int32 ArraySize = ColorRampParam->Points.Num();
int32 PointIndex = 0;
for (int32 Index = 0; Index < ArraySize && (bFetchAllPoints || PointIndex <= InIndex); ++Index)
{
UHoudiniParameterRampColorPoint* const PointData = ColorRampParam->Points[Index];
const bool bIsDeletedPoint = (PointData && InstanceIndexesPendingDelete.Contains(PointData->InstanceIndex));
if (!bIsDeletedPoint)
{
if (PointIndex == InIndex || bFetchAllPoints)
{
const bool bIsPointData = true;
OutPointData.Add(TPair<UObject*, bool>(PointData, bIsPointData));
}
// If we are fetching only the point at InIndex, then we are done here
if (PointIndex == InIndex)
return true;
PointIndex++;
}
}
}
}
if (bFetchAllPoints || InIndex < TotalNumPoints)
{
// Point is an insert operation
const int32 NumEvents = ModificationEvents.Num();
int32 PointIndex = NumActivePointsInArray;
for (int32 Index = 0; Index < NumEvents && (bFetchAllPoints || PointIndex <= InIndex); ++Index)
{
UHoudiniParameterRampModificationEvent* const Event = ModificationEvents[Index];
if (!IsValid(Event))
continue;
if (!Event->IsInsertEvent())
continue;
if (PointIndex == InIndex || bFetchAllPoints)
{
const bool bIsPointData = false;
OutPointData.Add(TPair<UObject*, bool>(Event, bIsPointData));
}
if (PointIndex == InIndex)
return true;
PointIndex++;
}
}
else
{
// Point is out of range
SetErrorMessage(FString::Printf(
TEXT("Ramp point index %d is out of range [0, %d]."), InIndex, TotalNumPoints));
return false;
}
if (bFetchAllPoints)
{
if (TotalNumPoints != OutPointData.Num())
{
SetErrorMessage(FString::Printf(
TEXT("Failed to fetch all ramp points. Got %d, expected %d."), OutPointData.Num(), TotalNumPoints));
return false;
}
return true;
}
}
// If we reach this point we didn't find the point
SetErrorMessage(FString::Printf(TEXT("Could not find valid ramp point at index %d."), InIndex));
return false;
}
bool UHoudiniPublicAPIAssetWrapper::SetRampParameterPointValue(
FName InParameterTupleName,
const int32 InPointIndex,
const float InPosition,
const float InFloatValue,
const FLinearColor& InColorValue,
const EHoudiniPublicAPIRampInterpolationType InInterpolation,
const bool bInMarkChanged)
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
UHoudiniParameterRampFloat* FloatRampParam = nullptr;
UHoudiniParameterRampColor* ColorRampParam = nullptr;
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::FloatRamp)
{
FloatRampParam = Cast<UHoudiniParameterRampFloat>(Param);
if (!IsValid(FloatRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else if (ParamType == EHoudiniParameterType::ColorRamp)
{
ColorRampParam = Cast<UHoudiniParameterRampColor>(Param);
if (!IsValid(ColorRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(TEXT("Parameter '%s' is not a float ramp or color ramp parameter."), *(Param->GetName())));
return false;
}
const EHoudiniRampInterpolationType NewInterpolation = UHoudiniPublicAPI::ToHoudiniRampInterpolationType(
InInterpolation);
// If the parameter is not set to auto update, or if cooking is paused, we have to set the cached points and
// not the main points.
// const bool bCookingEnabled = !FHoudiniEngineCommands::IsAssetCookingPaused();
// const bool bUseCachedPoints = (!Param->IsAutoUpdate() || !bCookingEnabled);
const bool bUseCachedPoints = !Param->IsAutoUpdate();
// Get the point at InPointIndex's data
TArray<TPair<UObject*, bool>> RampPointData;
if (!FindRampPointData(Param, InPointIndex, RampPointData) || RampPointData.Num() < 1)
return false;
UObject* const PointData = RampPointData[0].Key;
const bool bIsPointData = RampPointData[0].Value;
if (!IsValid(PointData))
return false;
if (bIsPointData)
{
UHoudiniParameterRampFloatPoint* FloatPointData = nullptr;
UHoudiniParameterRampColorPoint* ColorPointData = nullptr;
if (FloatRampParam)
{
FloatPointData = Cast<UHoudiniParameterRampFloatPoint>(PointData);
if (!IsValid(FloatPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampFloatPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
}
else
{
ColorPointData = Cast<UHoudiniParameterRampColorPoint>(PointData);
if (!IsValid(ColorPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampColorPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
}
if (bUseCachedPoints)
{
// When setting the cached points, we set the values directly instead of using the setters, but we set
// the bCaching flag on the parameter and mark the position/value/interpolation parent parameters as changed
if (FloatPointData)
{
if (FloatPointData->Position != InPosition)
{
FloatPointData->Position = InPosition;
FloatRampParam->bCaching = true;
}
if (FloatPointData->Value != InFloatValue)
{
FloatPointData->Value = InFloatValue;
FloatRampParam->bCaching = true;
}
if (FloatPointData->Interpolation != NewInterpolation)
{
FloatPointData->Interpolation = NewInterpolation;
FloatRampParam->bCaching = true;
}
}
else if (ColorPointData)
{
if (ColorPointData->Position != InPosition)
{
ColorPointData->Position = InPosition;
ColorRampParam->bCaching = true;
}
if (ColorPointData->Value != InColorValue)
{
ColorPointData->Value = InColorValue;
ColorRampParam->bCaching = true;
}
if (ColorPointData->Interpolation != NewInterpolation)
{
ColorPointData->Interpolation = NewInterpolation;
ColorRampParam->bCaching = true;
}
}
// Update the ramp's widget if it is currently visible/selected
const bool bForceFullUpdate = true;
FHoudiniEngineUtils::UpdateEditorProperties(Param, bForceFullUpdate);
}
else
{
// When setting the main points, we set the values using the setters on the point data but still manually
// mark the position/value/interpolation parent parameters as changed
if (FloatPointData)
{
if (FloatPointData->Position != InPosition && FloatPointData->PositionParentParm)
{
FloatPointData->SetPosition(InPosition);
if (bInMarkChanged)
FloatPointData->PositionParentParm->MarkChanged(bInMarkChanged);
}
if (FloatPointData->Value != InFloatValue && FloatPointData->ValueParentParm)
{
FloatPointData->SetValue(InFloatValue);
if (bInMarkChanged)
FloatPointData->ValueParentParm->MarkChanged(bInMarkChanged);
}
if (FloatPointData->Interpolation != NewInterpolation && FloatPointData->InterpolationParentParm)
{
FloatPointData->SetInterpolation(NewInterpolation);
if (bInMarkChanged)
FloatPointData->InterpolationParentParm->MarkChanged(bInMarkChanged);
}
}
else if (ColorPointData)
{
if (ColorPointData->Position != InPosition && ColorPointData->PositionParentParm)
{
ColorPointData->SetPosition(InPosition);
if (bInMarkChanged)
ColorPointData->PositionParentParm->MarkChanged(bInMarkChanged);
}
if (ColorPointData->Value != InColorValue && ColorPointData->ValueParentParm)
{
ColorPointData->SetValue(InColorValue);
if (bInMarkChanged)
ColorPointData->ValueParentParm->MarkChanged(bInMarkChanged);
}
if (ColorPointData->Interpolation != NewInterpolation && ColorPointData->InterpolationParentParm)
{
ColorPointData->SetInterpolation(NewInterpolation);
if (bInMarkChanged)
ColorPointData->InterpolationParentParm->MarkChanged(bInMarkChanged);
}
}
}
}
else
{
UHoudiniParameterRampModificationEvent* const Event = Cast<UHoudiniParameterRampModificationEvent>(PointData);
if (!IsValid(Event))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampModificationEvent instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
Event->InsertPosition = InPosition;
if (FloatRampParam)
{
Event->InsertFloat = InFloatValue;
}
else if (ColorRampParam)
{
Event->InsertColor = InColorValue;
}
Event->InsertInterpolation = NewInterpolation;
}
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetRampParameterPointValue(
FName InParameterTupleName,
const int32 InPointIndex,
float& OutPosition,
float& OutFloatValue,
FLinearColor& OutColorValue,
EHoudiniPublicAPIRampInterpolationType& OutInterpolation) const
{
UHoudiniParameter* Param = FindValidParameterByName(InParameterTupleName);
if (!Param)
return false;
UHoudiniParameterRampFloat* FloatRampParam = nullptr;
UHoudiniParameterRampColor* ColorRampParam = nullptr;
const EHoudiniParameterType ParamType = Param->GetParameterType();
if (ParamType == EHoudiniParameterType::FloatRamp)
{
FloatRampParam = Cast<UHoudiniParameterRampFloat>(Param);
if (!IsValid(FloatRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else if (ParamType == EHoudiniParameterType::ColorRamp)
{
ColorRampParam = Cast<UHoudiniParameterRampColor>(Param);
if (!IsValid(ColorRampParam))
{
SetErrorMessage(FString::Printf(
TEXT("Unexpected parameter class (%s) vs type (%d)"), *Param->GetClass()->GetName(), ParamType));
return false;
}
}
else
{
SetErrorMessage(FString::Printf(TEXT("Parameter '%s' is not a float ramp or color ramp parameter."), *(Param->GetName())));
return false;
}
TArray<TPair<UObject*, bool>> RampPointData;
if (!FindRampPointData(Param, InPointIndex, RampPointData) || RampPointData.Num() < 1)
return false;
UObject* const PointData = RampPointData[0].Key;
const bool bIsPointData = RampPointData[0].Value;
if (!IsValid(PointData))
return false;
if (bIsPointData)
{
UHoudiniParameterRampFloatPoint* FloatPointData = nullptr;
UHoudiniParameterRampColorPoint* ColorPointData = nullptr;
if (FloatRampParam)
{
FloatPointData = Cast<UHoudiniParameterRampFloatPoint>(PointData);
if (!IsValid(FloatPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampFloatPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
}
else
{
ColorPointData = Cast<UHoudiniParameterRampColorPoint>(PointData);
if (!IsValid(ColorPointData))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampColorPoint instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
}
// When setting the cached points, we set the values directly instead of using the setters, but we set
// the bCaching flag on the parameter and mark the position/value/interpolation parent parameters as changed
if (FloatPointData)
{
OutPosition = FloatPointData->Position;
OutFloatValue = FloatPointData->Value;
OutInterpolation = UHoudiniPublicAPI::ToHoudiniPublicAPIRampInterpolationType(
FloatPointData->Interpolation);
}
else if (ColorPointData)
{
OutPosition = ColorPointData->Position;
OutColorValue = ColorPointData->Value;
OutInterpolation = UHoudiniPublicAPI::ToHoudiniPublicAPIRampInterpolationType(
ColorPointData->Interpolation);
}
}
else
{
UHoudiniParameterRampModificationEvent* const Event = Cast<UHoudiniParameterRampModificationEvent>(PointData);
if (!IsValid(Event))
{
SetErrorMessage(FString::Printf(
TEXT("Expected UHoudiniParameterRampModificationEvent instance, but received incompatible class '%s'."),
*(PointData->GetClass()->GetName())));
return false;
}
OutPosition = Event->InsertPosition;
if (FloatRampParam)
{
OutFloatValue = Event->InsertFloat;
}
else if (ColorRampParam)
{
OutColorValue = Event->InsertColor;
}
OutInterpolation = UHoudiniPublicAPI::ToHoudiniPublicAPIRampInterpolationType(Event->InsertInterpolation);
}
return true;
}
UHoudiniInput*
UHoudiniPublicAPIAssetWrapper::GetHoudiniNodeInputByIndex(const int32 InNodeInputIndex)
{
if (InNodeInputIndex < 0)
return nullptr;
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return nullptr;
const int32 NumInputs = HAC->GetNumInputs();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
UHoudiniInput* const Input = HAC->GetInputAt(Index);
if (!IsValid(Input))
continue;
if (Input->GetInputIndex() == InNodeInputIndex)
return Input;
}
return nullptr;
}
const UHoudiniInput*
UHoudiniPublicAPIAssetWrapper::GetHoudiniNodeInputByIndex(const int32 InNodeInputIndex) const
{
if (InNodeInputIndex < 0)
return nullptr;
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return nullptr;
const int32 NumInputs = HAC->GetNumInputs();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
UHoudiniInput const* const Input = HAC->GetInputAt(Index);
if (!IsValid(Input))
continue;
if (Input->GetInputIndex() == InNodeInputIndex)
return Input;
}
return nullptr;
}
UHoudiniInput*
UHoudiniPublicAPIAssetWrapper::FindValidHoudiniNodeInputParameter(const FName& InInputParameterName)
{
if (InInputParameterName == NAME_None)
return nullptr;
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return nullptr;
const FString InputParameterName = InInputParameterName.ToString();
const int32 NumInputs = HAC->GetNumInputs();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
UHoudiniInput* const Input = HAC->GetInputAt(Index);
if (!IsValid(Input))
continue;
if (Input->IsObjectPathParameter() && Input->GetName() == InputParameterName)
return Input;
}
return nullptr;
}
const UHoudiniInput*
UHoudiniPublicAPIAssetWrapper::FindValidHoudiniNodeInputParameter(const FName& InInputParameterName) const
{
if (InInputParameterName == NAME_None)
return nullptr;
UHoudiniAssetComponent* HAC = nullptr;
if (!GetValidHoudiniAssetComponentWithError(HAC))
return nullptr;
const FString InputParameterName = InInputParameterName.ToString();
const int32 NumInputs = HAC->GetNumInputs();
for (int32 Index = 0; Index < NumInputs; ++Index)
{
UHoudiniInput const* const Input = HAC->GetInputAt(Index);
if (!IsValid(Input))
continue;
if (Input->IsObjectPathParameter() && Input->GetName() == InputParameterName)
return Input;
}
return nullptr;
}
bool
UHoudiniPublicAPIAssetWrapper::CreateAndPopulateAPIInput(const UHoudiniInput* InHoudiniInput, UHoudiniPublicAPIInput*& OutAPIInput)
{
if (!IsValid(InHoudiniInput))
return false;
TSubclassOf<UHoudiniPublicAPIInput> APIInputClass;
const EHoudiniInputType InputType = InHoudiniInput->GetInputType();
switch (InputType)
{
case EHoudiniInputType::Geometry:
APIInputClass = UHoudiniPublicAPIGeoInput::StaticClass();
break;
case EHoudiniInputType::Curve:
APIInputClass = UHoudiniPublicAPICurveInput::StaticClass();
break;
case EHoudiniInputType::Asset:
APIInputClass = UHoudiniPublicAPIAssetInput::StaticClass();
break;
case EHoudiniInputType::World:
APIInputClass = UHoudiniPublicAPIWorldInput::StaticClass();
break;
case EHoudiniInputType::Landscape:
APIInputClass = UHoudiniPublicAPILandscapeInput::StaticClass();
break;
case EHoudiniInputType::Skeletal:
// Not yet implemented
SetErrorMessage(FString::Printf(TEXT("GetInputAtIndex: Input type not yet implemented %d"), InputType));
return false;
case EHoudiniInputType::Invalid:
SetErrorMessage(FString::Printf(TEXT("GetInputAtIndex: Invalid input type %d"), InputType));
return false;
}
UHoudiniPublicAPIInput* APIInput = CreateEmptyInput(APIInputClass);
if (!IsValid(APIInput))
{
return false;
}
const bool bSuccessfullyCopied = APIInput->PopulateFromHoudiniInput(InHoudiniInput);
OutAPIInput = APIInput;
return bSuccessfullyCopied;
}
bool
UHoudiniPublicAPIAssetWrapper::PopulateHoudiniInput(const UHoudiniPublicAPIInput* InAPIInput, UHoudiniInput* InHoudiniInput) const
{
if (!IsValid(InHoudiniInput))
return false;
return InAPIInput->UpdateHoudiniInput(InHoudiniInput);
}
bool
UHoudiniPublicAPIAssetWrapper::GetValidTOPNetworkByPathWithError(const FString& InNetworkRelativePath, int32& OutNetworkIndex, UTOPNetwork*& OutNetwork) const
{
UHoudiniPDGAssetLink* AssetLink = nullptr;
if (!GetValidHoudiniPDGAssetLinkWithError(AssetLink))
return false;
int32 NetworkIndex = INDEX_NONE;
UTOPNetwork* const Network = UHoudiniPDGAssetLink::GetTOPNetworkByNodePath(
InNetworkRelativePath, AssetLink->AllTOPNetworks, NetworkIndex);
if (!IsValid(Network))
{
SetErrorMessage(FString::Printf(
TEXT("Could not find valid TOP network at relative path '%s'."), *InNetworkRelativePath));
return false;
}
OutNetworkIndex = NetworkIndex;
OutNetwork = Network;
return true;
}
bool
UHoudiniPublicAPIAssetWrapper::GetValidTOPNodeByPathWithError(
const FString& InNetworkRelativePath,
const FString& InNodeRelativePath,
int32& OutNetworkIndex,
int32& OutNodeIndex,
UTOPNode*& OutNode) const
{
int32 NetworkIndex = INDEX_NONE;
UTOPNetwork* Network = nullptr;
if (!GetValidTOPNetworkByPathWithError(InNetworkRelativePath, NetworkIndex, Network))
return false;
int32 NodeIndex = INDEX_NONE;
UTOPNode* const Node = UHoudiniPDGAssetLink::GetTOPNodeByNodePath(
InNodeRelativePath, Network->AllTOPNodes, NodeIndex);
if (!IsValid(Node))
{
SetErrorMessage(FString::Printf(
TEXT("Could not find valid TOP node at relative path '%s'."), *InNodeRelativePath));
return false;
}
OutNetworkIndex = NetworkIndex;
OutNodeIndex = NodeIndex;
OutNode = Node;
return true;
}