Files

4147 lines
128 KiB
C++

/*
* Copyright (c) <2017> Side Effects Software Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include "HoudiniAssetInput.h"
#include "HoudiniApi.h"
#include "HoudiniEngineUtils.h"
#include "HoudiniSplineComponent.h"
#include "HoudiniAssetParameter.h"
#include "HoudiniAssetComponent.h"
#include "HoudiniAssetParameterInt.h"
#include "HoudiniAssetParameterChoice.h"
#include "HoudiniAssetParameterToggle.h"
#include "HoudiniEngine.h"
#include "HoudiniAssetActor.h"
#include "HoudiniPluginSerializationVersion.h"
#include "HoudiniEngineString.h"
#include "HoudiniLandscapeUtils.h"
#include "HoudiniEngineRuntimePrivatePCH.h"
#include "Components/SplineComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Engine/Selection.h"
#include "Internationalization/Internationalization.h"
#include "EngineUtils.h" // for TActorIterator<>
#if WITH_EDITOR
#include "Editor.h"
#include "Editor/UnrealEdEngine.h"
#include "UnrealEdGlobals.h"
#endif
#if WITH_EDITOR
// Allows checking of objects currently being dragged around
struct FHoudiniMoveTracker
{
FHoudiniMoveTracker() : IsObjectMoving(false)
{
GEditor->OnBeginObjectMovement().AddLambda([this](UObject&) { IsObjectMoving = true; });
GEditor->OnEndObjectMovement().AddLambda([this](UObject&) { IsObjectMoving = false; });
}
static FHoudiniMoveTracker& Get() { static FHoudiniMoveTracker Instance; return Instance; }
bool IsObjectMoving;
};
#endif
static FName NAME_HoudiniNoUpload( TEXT( "HoudiniNoUpload" ) );
#define LOCTEXT_NAMESPACE HOUDINI_MODULE_RUNTIME
void
FHoudiniAssetInputOutlinerMesh::Serialize( FArchive & Ar )
{
Ar.UsingCustomVersion( FHoudiniCustomSerializationVersion::GUID );
HoudiniAssetParameterVersion = VER_HOUDINI_PLUGIN_SERIALIZATION_AUTOMATIC_VERSION;
Ar << HoudiniAssetParameterVersion;
Ar << ActorPtr;
if ( ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_OUTLINER_INPUT_SAVE_ACTOR_PATHNAME )
&& ( HoudiniAssetParameterVersion != VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_419_SERIALIZATION_FIX ) )
{
Ar << ActorPathName;
}
if ( HoudiniAssetParameterVersion < VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_OUTLINER_INPUT_SAVE_ACTOR_ONLY )
{
Ar << StaticMeshComponent;
Ar << StaticMesh;
}
Ar << ActorTransform;
Ar << AssetId;
if ( Ar.IsLoading() && !Ar.IsTransacting() )
AssetId = -1;
if ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_ADDED_UNREAL_SPLINE
&& HoudiniAssetParameterVersion < VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_OUTLINER_INPUT_SAVE_ACTOR_ONLY )
{
Ar << SplineComponent;
Ar << NumberOfSplineControlPoints;
Ar << SplineLength;
Ar << SplineResolution;
Ar << ComponentTransform;
}
if ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_ADDED_KEEP_TRANSFORM )
Ar << KeepWorldTransform;
// UE4.19 SERIALIZATION FIX:
// The component materials serialization (24) was actually missing in the UE4.19 H17.0 / H16.5 plugin.
// However subsequent serialized changes (25+) were present in those version. This caused crashes when loading
// a level that was saved with 4.19+16.5/17.0 on a newer version of Unreal or Houdini...
// If the serialized version is exactly that of the fix, we can ignore the materials paths as well
if ( ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_OUTLINER_INPUT_SAVE_MAT )
&& (HoudiniAssetParameterVersion != VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_419_SERIALIZATION_FIX ) )
Ar << MeshComponentsMaterials;
if ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_OUTLINER_INSTANCE_INDEX )
Ar << InstanceIndex;
}
void
FHoudiniAssetInputOutlinerMesh::RebuildSplineTransformsArrayIfNeeded()
{
// Rebuilding the SplineTransform array after reloading the asset
// This is required to properly detect Transform changes after loading the asset.
// We need an Unreal spline
if ( !SplineComponent || SplineComponent->IsPendingKill() )
return;
// If those are different, the input component has changed
if ( NumberOfSplineControlPoints != SplineComponent->GetNumberOfSplinePoints() )
return;
// If those are equals, there's no need to rebuild the array
if ( SplineControlPointsTransform.Num() == SplineComponent->GetNumberOfSplinePoints() )
return;
SplineControlPointsTransform.SetNumUninitialized(SplineComponent->GetNumberOfSplinePoints());
for ( int32 n = 0; n < SplineControlPointsTransform.Num(); n++ )
SplineControlPointsTransform[n] = SplineComponent->GetTransformAtSplinePoint( n, ESplineCoordinateSpace::Local, true );
}
bool
FHoudiniAssetInputOutlinerMesh::HasSplineComponentChanged(float fCurrentSplineResolution) const
{
if ( !SplineComponent || SplineComponent->IsPendingKill() )
return false;
// Total length of the spline has changed ?
if ( SplineComponent->GetSplineLength() != SplineLength )
return true;
// Number of CVs has changed ?
if ( NumberOfSplineControlPoints != SplineComponent->GetNumberOfSplinePoints() )
return true;
if ( SplineControlPointsTransform.Num() != SplineComponent->GetNumberOfSplinePoints() )
return true;
// Current Spline resolution has changed?
if ( fCurrentSplineResolution == -1.0 && SplineResolution != -1.0)
{
const UHoudiniRuntimeSettings * HoudiniRuntimeSettings = GetDefault< UHoudiniRuntimeSettings >();
if ( HoudiniRuntimeSettings )
fCurrentSplineResolution = HoudiniRuntimeSettings->MarshallingSplineResolution;
else
fCurrentSplineResolution = HAPI_UNREAL_PARAM_SPLINE_RESOLUTION_DEFAULT;
}
if ( SplineResolution != fCurrentSplineResolution )
return true;
// Has any of the CV's transform been modified?
for ( int32 n = 0; n < SplineControlPointsTransform.Num(); n++ )
{
if ( !SplineControlPointsTransform[ n ].GetLocation().Equals( SplineComponent->GetLocationAtSplinePoint( n, ESplineCoordinateSpace::Local) ) )
return true;
if ( !SplineControlPointsTransform[ n ].GetRotation().Equals( SplineComponent->GetQuaternionAtSplinePoint( n, ESplineCoordinateSpace::World ) ) )
return true;
if ( !SplineControlPointsTransform[ n ].GetScale3D().Equals( SplineComponent->GetScaleAtSplinePoint( n ) ) )
return true;
}
return false;
}
bool
FHoudiniAssetInputOutlinerMesh::HasActorTransformChanged() const
{
if ( !ActorPtr.IsValid() )
return false;
if ( !ActorTransform.Equals( ActorPtr->GetTransform() ) )
return true;
return false;
}
bool
FHoudiniAssetInputOutlinerMesh::HasComponentTransformChanged() const
{
if ( StaticMeshComponent && !StaticMeshComponent->IsPendingKill() )
{
// Handle instances here
UInstancedStaticMeshComponent * InstancedStaticMeshComponent = Cast< UInstancedStaticMeshComponent >( StaticMeshComponent );
if (InstancedStaticMeshComponent )
{
FTransform InstanceTransform;
if ( InstancedStaticMeshComponent->GetInstanceTransform( InstanceIndex, InstanceTransform, true ) )
return !ComponentTransform.Equals( InstanceTransform );
}
else
return !ComponentTransform.Equals( StaticMeshComponent->GetComponentTransform() );
}
if ( SplineComponent && !SplineComponent->IsPendingKill() )
return !ComponentTransform.Equals( SplineComponent->GetComponentTransform() );
return false;
}
bool
FHoudiniAssetInputOutlinerMesh::HasComponentMaterialsChanged() const
{
if ( !StaticMeshComponent || StaticMeshComponent->IsPendingKill() )
return false;
if ( StaticMeshComponent->GetNumMaterials() != MeshComponentsMaterials.Num() )
return true;
for ( int32 n = 0; n < MeshComponentsMaterials.Num(); n++ )
{
UMaterialInterface* MI = StaticMeshComponent->GetMaterial(n);
FString mat_interface_path = MI->GetPathName();
if (!MI || MI->IsPendingKill())
mat_interface_path = FString();
if (mat_interface_path != MeshComponentsMaterials[ n ])
return true;
}
return false;
}
bool
FHoudiniAssetInputOutlinerMesh::NeedsComponentUpdate() const
{
if ( !ActorPtr.IsValid() || ActorPtr->IsPendingKill() )
return false;
if ( StaticMesh && ( !StaticMesh->IsValidLowLevel() || StaticMesh->IsPendingKill() ) )
return true;
if ( StaticMeshComponent && ( !StaticMeshComponent->IsValidLowLevel() || StaticMeshComponent->IsPendingKill() ) )
return true;
if ( SplineComponent && ( !SplineComponent->IsValidLowLevel() || SplineComponent->IsPendingKill() ) )
return true;
if ( !StaticMesh && !StaticMeshComponent && !SplineComponent )
return true;
return false;
}
bool
FHoudiniAssetInputOutlinerMesh::TryToUpdateActorPtrFromActorPathName()
{
// Ensure our current ActorPathName looks valid
if (ActorPathName.IsEmpty() || ActorPathName.Equals(TEXT("None"), ESearchCase::IgnoreCase))
return false;
// We'll try to find the corresponding actor by browsing through all the actors in the world..
// Get the editor world
UWorld* World = nullptr;
#if WITH_EDITOR
World = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr;
#endif
if (!World)
return false;
// Then try to find the actor corresponding to our path/name
bool FoundActor = false;
for (TActorIterator<AActor> ActorIt(World); ActorIt; ++ActorIt)
{
if (ActorIt->GetPathName() != ActorPathName)
continue;
// We found the actor
ActorPtr = *ActorIt;
FoundActor = true;
break;
}
if ( FoundActor )
{
// We need to invalid our components so they can be updated later
// from the new actor
StaticMesh = NULL;
StaticMeshComponent = NULL;
SplineComponent = NULL;
}
return FoundActor;
}
UHoudiniAssetInput::UHoudiniAssetInput( const FObjectInitializer & ObjectInitializer )
: Super( ObjectInitializer )
, InputCurve( nullptr )
, InputAssetComponent( nullptr )
, InputLandscapeProxy( nullptr )
, ConnectedAssetId( -1 )
, InputIndex( 0 )
, ChoiceIndex( EHoudiniAssetInputType::GeometryInput )
, UnrealSplineResolution( -1.0f )
, OutlinerInputsNeedPostLoadInit( false )
, HoudiniAssetInputFlagsPacked( 0u )
{
// flags
bLandscapeExportMaterials = true;
bKeepWorldTransform = 2;
bLandscapeExportAsHeightfield = true;
bLandscapeAutoSelectComponent = true;
bPackBeforeMerge = false;
bExportAllLODs = false;
bExportSockets = false;
bUpdateInputLandscape = false;
ChoiceStringValue = TEXT( "" );
// Initialize the arrays
InputObjects.SetNumUninitialized( 1 );
InputObjects[ 0 ] = nullptr;
InputTransforms.SetNumUninitialized( 1 );
InputTransforms[ 0 ] = FTransform::Identity;
TransformUIExpanded.SetNumUninitialized( 1 );
TransformUIExpanded[ 0 ] = false;
SkeletonInputObjects.SetNumUninitialized(1);
SkeletonInputObjects[0] = nullptr;
InputLandscapeTransform = FTransform::Identity;
}
UHoudiniAssetInput *
UHoudiniAssetInput::Create( UObject * InPrimaryObject, int32 InInputIndex, HAPI_NodeId InNodeId )
{
UHoudiniAssetInput * HoudiniAssetInput = nullptr;
if (!InPrimaryObject || InPrimaryObject->IsPendingKill())
return HoudiniAssetInput;
// Get name of this input.
HAPI_StringHandle InputStringHandle;
if ( HAPI_RESULT_SUCCESS != FHoudiniApi::GetNodeInputName(
FHoudiniEngine::Get().GetSession(),
InNodeId, InInputIndex, &InputStringHandle ) )
{
return HoudiniAssetInput;
}
HoudiniAssetInput = NewObject< UHoudiniAssetInput >(
InPrimaryObject,
UHoudiniAssetInput::StaticClass(),
NAME_None, RF_Public | RF_Transactional );
// Set component and other information.
HoudiniAssetInput->PrimaryObject = InPrimaryObject;
HoudiniAssetInput->InputIndex = InInputIndex;
// Get input string from handle.
HoudiniAssetInput->SetNameAndLabel( InputStringHandle );
HoudiniAssetInput->SetNodeId( InNodeId );
// Set the default input type from the input's label
HoudiniAssetInput->SetDefaultInputTypeFromLabel();
// Create necessary widget resources.
HoudiniAssetInput->CreateWidgetResources();
return HoudiniAssetInput;
}
UHoudiniAssetInput *
UHoudiniAssetInput::Create(
UObject * InPrimaryObject,
UHoudiniAssetParameter * InParentParameter,
HAPI_NodeId InNodeId,
const HAPI_ParmInfo & ParmInfo)
{
UObject * Outer = InPrimaryObject;
if (!Outer)
{
Outer = InParentParameter;
if (!Outer)
{
// Must have either component or parent not null.
check(false);
}
}
UHoudiniAssetInput * HoudiniAssetInput = NewObject< UHoudiniAssetInput >(
Outer, UHoudiniAssetInput::StaticClass(), NAME_None, RF_Public | RF_Transactional);
// This is being used as a parameter
HoudiniAssetInput->bIsObjectPathParameter = true;
HoudiniAssetInput->CreateParameter(InPrimaryObject, InParentParameter, InNodeId, ParmInfo);
HoudiniAssetInput->SetNodeId( InNodeId );
// Set the default input type from the input's label
HoudiniAssetInput->SetDefaultInputTypeFromLabel();
// Create necessary widget resources.
HoudiniAssetInput->CreateWidgetResources();
// Use the value of the object path param to preset the
// default object for Geometry inputs
HoudiniAssetInput->SetDefaultAssetFromHDA();
return HoudiniAssetInput;
}
void
UHoudiniAssetInput::CreateWidgetResources()
{
ChoiceStringValue = TEXT( "" );
StringChoiceLabels.Empty();
{
FString * ChoiceLabel = new FString( TEXT( "Geometry Input" ) );
StringChoiceLabels.Add( TSharedPtr< FString >( ChoiceLabel ) );
if ( ChoiceIndex == EHoudiniAssetInputType::GeometryInput )
ChoiceStringValue = *ChoiceLabel;
}
{
FString * ChoiceLabel = new FString( TEXT( "Asset Input" ) );
StringChoiceLabels.Add( TSharedPtr< FString >( ChoiceLabel ) );
if ( ChoiceIndex == EHoudiniAssetInputType::AssetInput )
ChoiceStringValue = *ChoiceLabel;
}
{
FString * ChoiceLabel = new FString( TEXT( "Curve Input" ) );
StringChoiceLabels.Add( TSharedPtr< FString >( ChoiceLabel ) );
if ( ChoiceIndex == EHoudiniAssetInputType::CurveInput )
ChoiceStringValue = *ChoiceLabel;
}
{
FString * ChoiceLabel = new FString( TEXT( "Landscape Input" ) );
StringChoiceLabels.Add( TSharedPtr< FString >( ChoiceLabel ) );
if ( ChoiceIndex == EHoudiniAssetInputType::LandscapeInput )
ChoiceStringValue = *ChoiceLabel;
}
{
FString * ChoiceLabel = new FString( TEXT( "World Outliner Input" ) );
StringChoiceLabels.Add( TSharedPtr< FString >( ChoiceLabel ) );
if ( ChoiceIndex == EHoudiniAssetInputType::WorldInput )
ChoiceStringValue = *ChoiceLabel;
}
{
FString * ChoiceLabel = new FString(TEXT("Skeletal Mesh Input"));
StringChoiceLabels.Add(TSharedPtr< FString >(ChoiceLabel));
if (ChoiceIndex == EHoudiniAssetInputType::SkeletonInput)
ChoiceStringValue = *ChoiceLabel;
}
}
void
UHoudiniAssetInput::DisconnectAndDestroyInputAsset()
{
if ( ChoiceIndex == EHoudiniAssetInputType::AssetInput )
{
if( bIsObjectPathParameter )
{
FHoudiniApi::SetParmStringValue(
FHoudiniEngine::Get().GetSession(), NodeId, "",
ParmId, 0 );
}
if ( InputAssetComponent )
InputAssetComponent->RemoveDownstreamAsset( GetHoudiniAssetComponent(), InputIndex );
bInputAssetConnectedInHoudini = false;
ConnectedAssetId = -1;
}
else
{
if ( bIsObjectPathParameter )
{
FHoudiniApi::SetParmStringValue(
FHoudiniEngine::Get().GetSession(), NodeId, "", ParmId, 0 );
}
else
{
HAPI_NodeId HostAssetId = GetAssetId();
if ( FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId ) && FHoudiniEngineUtils::IsValidNodeId( HostAssetId ) )
FHoudiniEngineUtils::HapiDisconnectAsset( HostAssetId, InputIndex );
}
// Destroy all the geo input assets
for ( HAPI_NodeId AssetNodeId : CreatedInputDataAssetIds )
{
if ( FHoudiniEngineUtils::IsHoudiniNodeValid( AssetNodeId ) )
FHoudiniEngineUtils::DestroyHoudiniAsset( AssetNodeId );
}
CreatedInputDataAssetIds.Empty();
// Then simply destroy the input's parent OBJ node
if ( FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId ) )
{
HAPI_NodeId ParentId = FHoudiniEngineUtils::HapiGetParentNodeId( ConnectedAssetId );
if ( FHoudiniEngineUtils::IsHoudiniNodeValid( ParentId ) )
FHoudiniEngineUtils::DestroyHoudiniAsset( ParentId );
FHoudiniEngineUtils::DestroyHoudiniAsset( ConnectedAssetId );
}
ConnectedAssetId = -1;
if ( ChoiceIndex == EHoudiniAssetInputType::WorldInput )
{
// World Input Actors' Meshes need to have their corresponding Input Assets destroyed too.
for ( int32 n = 0; n < InputOutlinerMeshArray.Num(); n++ )
{
InputOutlinerMeshArray[ n ].AssetId = -1;
}
}
/*
if ( ChoiceIndex == EHoudiniAssetInputType::WorldInput )
{
// World Input Actors' Meshes need to have their corresponding Input Assets destroyed too.
for ( int32 n = 0; n < InputOutlinerMeshArray.Num(); n++ )
{
if ( FHoudiniEngineUtils::IsValidAssetId( InputOutlinerMeshArray[n].AssetId ) )
{
FHoudiniEngineUtils::HapiDisconnectAsset( ConnectedAssetId, n );//InputOutlinerMeshArray[n].AssetId);
FHoudiniEngineUtils::DestroyHoudiniAsset( InputOutlinerMeshArray[n].AssetId );
InputOutlinerMeshArray[n].AssetId = -1;
}
}
}
else if ( ChoiceIndex == EHoudiniAssetInputType::GeometryInput )
{
// Destroy all the geo input assets
for ( HAPI_NodeId AssetNodeId : CreatedInputDataAssetIds )
{
FHoudiniEngineUtils::DestroyHoudiniAsset( AssetNodeId );
}
CreatedInputDataAssetIds.Empty();
}
else if ( ChoiceIndex == EHoudiniAssetInputType::LandscapeInput )
{
// Destroy the landscape node
// If the landscape was sent as a heightfield, also destroy the heightfield's input node
FHoudiniLandscapeUtils::DestroyLandscapeAssetNode( ConnectedAssetId, CreatedInputDataAssetIds );
}
else if ( ChoiceIndex == EHoudiniAssetInputType::SkeletonInput )
{
// Destroy all the skeleton input assets
for ( HAPI_NodeId AssetNodeId : CreatedInputDataAssetIds )
{
FHoudiniEngineUtils::DestroyHoudiniAsset( AssetNodeId );
}
CreatedInputDataAssetIds.Empty();
}
if ( FHoudiniEngineUtils::IsValidAssetId( ConnectedAssetId ) )
{
FHoudiniEngineUtils::DestroyHoudiniAsset( ConnectedAssetId );
ConnectedAssetId = -1;
}
*/
}
}
bool
UHoudiniAssetInput::CreateParameter(
UObject * InPrimaryObject,
UHoudiniAssetParameter * InParentParameter,
HAPI_NodeId InNodeId, const HAPI_ParmInfo & ParmInfo)
{
// Inputs should never call this
check(bIsObjectPathParameter);
if (!Super::CreateParameter(InPrimaryObject, InParentParameter, InNodeId, ParmInfo))
return false;
// We can only handle node type.
if (ParmInfo.type != HAPI_PARMTYPE_NODE)
return false;
return true;
}
#if WITH_EDITOR
void
UHoudiniAssetInput::PostEditUndo()
{
Super::PostEditUndo();
if ( InputCurve
&& !InputCurve->IsPendingKill()
&& ChoiceIndex == EHoudiniAssetInputType::CurveInput )
{
USceneComponent* RootComp = GetHoudiniAssetComponent();
if ( RootComp && !RootComp->IsPendingKill() )
{
AActor* Owner = RootComp->GetOwner();
if ( Owner && !Owner->IsPendingKill() )
Owner->AddOwnedComponent( InputCurve );
InputCurve->AttachToComponent( RootComp, FAttachmentTransformRules::KeepRelativeTransform );
InputCurve->RegisterComponent();
InputCurve->SetVisibility( true );
}
}
}
void
UHoudiniAssetInput::ForceSetInputObject(UObject * InObject, int32 AtIndex, bool CommitChange)
{
EHoudiniAssetInputType::Enum NewInputType = EHoudiniAssetInputType::GeometryInput;
if ( AActor* Actor = Cast<AActor>( InObject ) )
{
// Update the OutlinerInputArray from the actor
UpdateInputOulinerArrayFromActor(Actor, true);
if ( InputOutlinerMeshArray.Num() > 0 )
NewInputType = EHoudiniAssetInputType::WorldInput;
// Looking for houdini assets
if ( AHoudiniAssetActor * HoudiniAssetActor = Cast<AHoudiniAssetActor>( Actor ) )
{
OnInputActorSelected( Actor );
if ( InputAssetComponent )
NewInputType = EHoudiniAssetInputType::AssetInput;
}
// Looking for Landscapes
if (ALandscapeProxy * Landscape = Cast< ALandscapeProxy >( Actor ) )
{
// Store new landscape.
OnLandscapeActorSelected( Actor );
NewInputType = EHoudiniAssetInputType::LandscapeInput;
}
}
else
{
// The object is not a world actor, so add it to the geo input
if( InputObjects.IsValidIndex( AtIndex ) )
{
InputObjects[ AtIndex ] = InObject;
}
else
{
InputObjects.Insert( InObject, AtIndex );
}
if ( !InputTransforms.IsValidIndex( AtIndex ) )
{
InputTransforms.Insert( FTransform::Identity, AtIndex );
}
if ( !TransformUIExpanded.IsValidIndex( AtIndex ) )
{
TransformUIExpanded.Insert( false, AtIndex );
}
}
if( CommitChange )
{
// We must change ChoiceIndex's value manually before calling ChangeInputType:
// If we are forcing the input to an asset input, not doing so would actually destroy the
// parent HDA node on the houdini side, making the input HDA to need a reinstantiation on first cook...
ChoiceIndex = NewInputType;
ChangeInputType( NewInputType, true );
MarkChanged();
}
}
// Note: This method is only used for testing
void
UHoudiniAssetInput::ClearInputs()
{
ChangeInputType( EHoudiniAssetInputType::GeometryInput, true );
InputOutlinerMeshArray.Empty();
InputObjects.Empty();
InputTransforms.Empty();
TransformUIExpanded.Empty();
MarkChanged();
}
#endif // WITH_EDITOR
bool
UHoudiniAssetInput::ConnectInputNode()
{
if (!FHoudiniEngineUtils::IsValidNodeId(ConnectedAssetId))
return false;
// Helper for connecting our input or setting the object path parameter
if (!bIsObjectPathParameter)
{
// Now we can connect input node to the asset node.
HOUDINI_CHECK_ERROR_RETURN(FHoudiniApi::ConnectNodeInput(
FHoudiniEngine::Get().GetSession(), GetAssetId(), InputIndex,
ConnectedAssetId, 0), false);
}
else
{
if (!FHoudiniEngineUtils::IsValidNodeId(NodeId))
return false;
// Now we can assign the input node path to the parameter
std::string ParamNameString = TCHAR_TO_UTF8(*GetParameterName());
HOUDINI_CHECK_ERROR_RETURN(FHoudiniApi::SetParmNodeValue(
FHoudiniEngine::Get().GetSession(), NodeId,
ParamNameString.c_str(), ConnectedAssetId), false);
}
return true;
}
bool
UHoudiniAssetInput::UploadParameterValue()
{
bool Success = true;
if ( !PrimaryObject || PrimaryObject->IsPendingKill() )
return false;
HAPI_NodeId HostAssetId = GetAssetId();
switch ( ChoiceIndex )
{
case EHoudiniAssetInputType::GeometryInput:
{
if ( !InputObjects.Num() )
{
// Either mesh was reset or null mesh has been assigned.
DisconnectAndDestroyInputAsset();
}
else
{
if ( bStaticMeshChanged || bLoadedParameter )
{
// Disconnect and destroy currently connected asset, if there's one.
DisconnectAndDestroyInputAsset();
// Connect input and create connected asset. Will return by reference.
if ( !FHoudiniEngineUtils::HapiCreateInputNodeForObjects(
HostAssetId, InputObjects, InputTransforms,
ConnectedAssetId, CreatedInputDataAssetIds,
false, bExportAllLODs, bExportSockets ) )
{
bChanged = false;
ConnectedAssetId = -1;
return false;
}
else
{
Success &= ConnectInputNode();
}
bStaticMeshChanged = false;
}
Success &= UpdateObjectMergeTransformType();
Success &= UpdateObjectMergePackBeforeMerge();
}
break;
}
case EHoudiniAssetInputType::AssetInput:
{
// Process connected asset.
if ( InputAssetComponent
&& !InputAssetComponent->IsPendingKill()
&& FHoudiniEngineUtils::IsValidNodeId( InputAssetComponent->GetAssetId() ) )
{
// Connect a new asset / we have previously connected asset
if (!bInputAssetConnectedInHoudini)
ConnectInputAssetActor();
else
bChanged = false;
Success &= bInputAssetConnectedInHoudini;
Success &= UpdateObjectMergeTransformType();
}
else if ( bInputAssetConnectedInHoudini && ( !InputAssetComponent || InputAssetComponent->IsPendingKill() ) )
{
// Previously connected asset deleted/invalid
DisconnectInputAssetActor();
}
else
{
// Nothing to connect
bChanged = false;
if ( InputAssetComponent )
return false;
}
break;
}
case EHoudiniAssetInputType::CurveInput:
{
// If we have no curve node, create it.
bool bCreated = false;
if ( !FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId ) )
{
if ( !FHoudiniEngineUtils::HapiCreateCurveNode( ConnectedAssetId ) )
{
bChanged = false;
ConnectedAssetId = -1;
return false;
}
// Connect the node to the asset.
Success &= ConnectInputNode();
bCreated = true;
}
if ( bLoadedParameter || bCreated )
{
// If we just loaded or created our curve, we need to set parameters.
for (TMap< FString, UHoudiniAssetParameter * >::TIterator
IterParams(InputCurveParameters); IterParams; ++IterParams)
{
UHoudiniAssetParameter * Parameter = IterParams.Value();
if ( !Parameter || Parameter->IsPendingKill() )
continue;
// We need to update the node id for the parameters.
Parameter->SetNodeId(ConnectedAssetId);
// Upload parameter value.
Success &= Parameter->UploadParameterValue();
}
}
if ( ConnectedAssetId != -1 && InputCurve && !InputCurve->IsPendingKill() )
{
// The curve node has now been created and set up, we can upload points and rotation/scale attributes
const TArray< FTransform > & CurvePoints = InputCurve->GetCurvePoints();
TArray<FVector> Positions;
InputCurve->GetCurvePositions(Positions);
TArray<FQuat> Rotations;
InputCurve->GetCurveRotations(Rotations);
TArray<FVector> Scales;
InputCurve->GetCurveScales(Scales);
if(!FHoudiniEngineUtils::HapiCreateCurveInputNodeForData(
HostAssetId,
ConnectedAssetId,
&Positions,
&Rotations,
&Scales,
nullptr))
{
bChanged = false;
ConnectedAssetId = -1;
return false;
}
}
if (bCreated && InputCurve && !InputCurve->IsPendingKill() )
{
// We need to check that the SplineComponent has no offset.
// if the input was set to WorldOutliner before, it might have one
FTransform CurveTransform = InputCurve->GetRelativeTransform();
if (!CurveTransform.GetLocation().IsZero())
InputCurve->SetRelativeLocation(FVector::ZeroVector);
}
Success &= UpdateObjectMergeTransformType();
// Cook the spline node.
if( HAPI_RESULT_SUCCESS != FHoudiniApi::CookNode( FHoudiniEngine::Get().GetSession(), ConnectedAssetId, nullptr ) )
Success = false;
// We need to update the curve.
Success &= UpdateInputCurve();
bSwitchedToCurve = false;
break;
}
case EHoudiniAssetInputType::LandscapeInput:
{
if ( !InputLandscapeProxy || InputLandscapeProxy->IsPendingKill() )
{
// Either landscape was reset or null landscape has been assigned.
DisconnectAndDestroyInputAsset();
}
else
{
// Disconnect and destroy currently connected asset, if there's one.
DisconnectAndDestroyInputAsset();
// If we're auto-selecting the components, we need to get the asset's bound
FBox Bounds(ForceInitToZero);
if ( bLandscapeAutoSelectComponent )
{
// Get our asset's or our connected input asset's bounds
UHoudiniAssetComponent* AssetComponent = (UHoudiniAssetComponent*)PrimaryObject;
if (!AssetComponent || AssetComponent->IsPendingKill())
AssetComponent = InputAssetComponent;
// Get the bounds, without this input
if (AssetComponent && !AssetComponent->IsPendingKill())
Bounds = AssetComponent->GetAssetBounds(this, true);
}
// Connect input and create connected asset. Will return by reference.
if ( !FHoudiniEngineUtils::HapiCreateInputNodeForLandscape(
HostAssetId, InputLandscapeProxy.Get(),
ConnectedAssetId, CreatedInputDataAssetIds,
bLandscapeExportSelectionOnly, bLandscapeExportCurves,
bLandscapeExportMaterials, bLandscapeExportAsMesh, bLandscapeExportLighting,
bLandscapeExportNormalizedUVs, bLandscapeExportTileUVs, Bounds,
bLandscapeExportAsHeightfield, bLandscapeAutoSelectComponent ) )
{
bChanged = false;
ConnectedAssetId = -1;
return false;
}
// Connect the inputs and update the transform type
Success &= ConnectInputNode();
Success &= UpdateObjectMergeTransformType();
}
break;
}
case EHoudiniAssetInputType::WorldInput:
{
if ( InputOutlinerMeshArray.Num() <= 0 )
{
// Either mesh was reset or null mesh has been assigned.
DisconnectAndDestroyInputAsset();
}
else
{
if ( bStaticMeshChanged || bLoadedParameter )
{
// Disconnect and destroy currently connected asset, if there's one.
DisconnectAndDestroyInputAsset();
// Connect input and create connected asset. Will return by reference.
if ( !FHoudiniEngineUtils::HapiCreateInputNodeForWorldOutliner(
HostAssetId, InputOutlinerMeshArray, ConnectedAssetId, CreatedInputDataAssetIds,
UnrealSplineResolution, bExportAllLODs, bExportSockets ) )
{
bChanged = false;
ConnectedAssetId = -1;
return false;
}
else
{
Success &= ConnectInputNode();
}
bStaticMeshChanged = false;
Success &= UpdateObjectMergeTransformType();
}
Success &= UpdateObjectMergePackBeforeMerge();
}
break;
}
case EHoudiniAssetInputType::SkeletonInput:
{
if (!SkeletonInputObjects.Num())
{
// Either mesh was reset or null mesh has been assigned.
DisconnectAndDestroyInputAsset();
}
else
{
if (bStaticMeshChanged || bLoadedParameter)
{
// Disconnect and destroy currently connected asset, if there's one.
DisconnectAndDestroyInputAsset();
// Connect input and create connected asset. Will return by reference.
if ( !FHoudiniEngineUtils::HapiCreateInputNodeForObjects(
HostAssetId, SkeletonInputObjects, InputTransforms,
ConnectedAssetId, CreatedInputDataAssetIds, true ) )
{
bChanged = false;
ConnectedAssetId = -1;
return false;
}
else
{
Success &= ConnectInputNode();
}
bStaticMeshChanged = false;
}
Success &= UpdateObjectMergeTransformType();
Success &= UpdateObjectMergePackBeforeMerge();
}
break;
}
default:
{
check( 0 );
}
}
bLoadedParameter = false;
return Success & Super::UploadParameterValue();
}
uint32
UHoudiniAssetInput::GetDefaultTranformTypeValue() const
{
switch (ChoiceIndex)
{
// NONE
case EHoudiniAssetInputType::CurveInput:
case EHoudiniAssetInputType::GeometryInput:
return 0;
// INTO THIS OBJECT
case EHoudiniAssetInputType::AssetInput:
case EHoudiniAssetInputType::LandscapeInput:
case EHoudiniAssetInputType::WorldInput:
return 1;
}
return 0;
}
UObject*
UHoudiniAssetInput::GetInputObject( int32 AtIndex ) const
{
return InputObjects.IsValidIndex( AtIndex ) ? InputObjects[ AtIndex ] : nullptr;
}
FTransform
UHoudiniAssetInput::GetInputTransform( int32 AtIndex ) const
{
return InputTransforms.IsValidIndex( AtIndex ) ? InputTransforms[ AtIndex ] : FTransform::Identity;
}
UObject*
UHoudiniAssetInput::GetSkeletonInputObject( int32 AtIndex ) const
{
return SkeletonInputObjects.IsValidIndex( AtIndex ) ? SkeletonInputObjects[ AtIndex ] : nullptr;
}
UHoudiniAssetComponent*
UHoudiniAssetInput::GetHoudiniAssetComponent()
{
return Cast<UHoudiniAssetComponent>( PrimaryObject );
}
const UHoudiniAssetComponent*
UHoudiniAssetInput::GetHoudiniAssetComponent() const
{
return Cast<const UHoudiniAssetComponent>( PrimaryObject );
}
HAPI_NodeId
UHoudiniAssetInput::GetAssetId() const
{
return NodeId;
}
bool
UHoudiniAssetInput::UpdateObjectMergeTransformType()
{
if ( !PrimaryObject || PrimaryObject->IsPendingKill() )
return false;
uint32 nTransformType = -1;
if ( bKeepWorldTransform == 2 )
nTransformType = GetDefaultTranformTypeValue();
else if ( bKeepWorldTransform )
nTransformType = 1;
else
nTransformType = 0;
// Geometry inputs are always set to none
if ( ChoiceIndex == EHoudiniAssetInputType::GeometryInput )
nTransformType = 0;
// Get the Input node ID from the host ID
HAPI_NodeId InputNodeId = -1;
HAPI_NodeId HostAssetId = GetAssetId();
bool bSuccess = false;
const std::string sXformType = "xformtype";
if ( bIsObjectPathParameter )
{
// Directly change the Parameter xformtype
if ( HAPI_RESULT_SUCCESS == FHoudiniApi::SetParmIntValue(
FHoudiniEngine::Get().GetSession(),
NodeId, sXformType.c_str(), 0, nTransformType ) )
bSuccess = true;
}
else
{
// Query the object merge's node ID via the input
if ( HAPI_RESULT_SUCCESS == FHoudiniApi::QueryNodeInput(
FHoudiniEngine::Get().GetSession(),
HostAssetId, InputIndex, &InputNodeId ) )
{
// Change Parameter xformtype
if ( HAPI_RESULT_SUCCESS == FHoudiniApi::SetParmIntValue(
FHoudiniEngine::Get().GetSession(),
InputNodeId, sXformType.c_str(), 0, nTransformType ) )
bSuccess = true;
}
}
if ( ChoiceIndex == EHoudiniAssetInputType::WorldInput )
{
// For World Inputs, we need to go through each asset selected
// and change the transform type on the merge node's input
for ( int32 n = 0; n < InputOutlinerMeshArray.Num(); n++ )
{
// Get the Input node ID from the host ID
InputNodeId = -1;
if ( HAPI_RESULT_SUCCESS != FHoudiniApi::QueryNodeInput(
FHoudiniEngine::Get().GetSession(),
ConnectedAssetId, n, &InputNodeId ) )
continue;
if ( InputNodeId == -1 )
continue;
// Change Parameter xformtype
if ( HAPI_RESULT_SUCCESS != FHoudiniApi::SetParmIntValue(
FHoudiniEngine::Get().GetSession(), InputNodeId,
sXformType.c_str(), 0, nTransformType ) )
bSuccess = false;
}
}
return bSuccess;
}
bool
UHoudiniAssetInput::UpdateObjectMergePackBeforeMerge()
{
if ( !PrimaryObject || PrimaryObject->IsPendingKill() )
return false;
uint32 nPackValue = bPackBeforeMerge ? 1 : 0;
// Get the Input node ID from the host ID
HAPI_NodeId InputNodeId = -1;
HAPI_NodeId HostAssetId = GetAssetId();
bool bSuccess = true;
const std::string sPack = "pack";
// Going through each input asset plugged in the geometry input,
// or through each input asset select in a world input.
int32 NumberOfInputObjects = 0;
if (ChoiceIndex == EHoudiniAssetInputType::GeometryInput)
NumberOfInputObjects = InputObjects.Num();
else if (ChoiceIndex == EHoudiniAssetInputType::WorldInput)
NumberOfInputObjects = InputOutlinerMeshArray.Num();
if (bIsObjectPathParameter)
{
// Directly change the Parameter xformtype
if (HAPI_RESULT_SUCCESS == FHoudiniApi::SetParmIntValue(
FHoudiniEngine::Get().GetSession(),
NodeId, sPack.c_str(), 0, nPackValue))
bSuccess = true;
}
else
{
// Query the object merge's node ID via the input
if (HAPI_RESULT_SUCCESS == FHoudiniApi::QueryNodeInput(
FHoudiniEngine::Get().GetSession(),
HostAssetId, InputIndex, &InputNodeId))
{
// Change Parameter xformtype
if (HAPI_RESULT_SUCCESS == FHoudiniApi::SetParmIntValue(
FHoudiniEngine::Get().GetSession(),
InputNodeId, sPack.c_str(), 0, nPackValue))
bSuccess = true;
}
}
// We also need to modify the transform types of the merge node's inputs
for ( int n = 0; n < NumberOfInputObjects; n++ )
{
// Get the Input node ID from the host ID
InputNodeId = -1;
if ( HAPI_RESULT_SUCCESS != FHoudiniApi::QueryNodeInput(
FHoudiniEngine::Get().GetSession(),
ConnectedAssetId, n, &InputNodeId ) )
continue;
if ( InputNodeId == -1 )
continue;
// Change Parameter xformtype
if ( HAPI_RESULT_SUCCESS != FHoudiniApi::SetParmIntValue(
FHoudiniEngine::Get().GetSession(), InputNodeId,
sPack.c_str(), 0, nPackValue ) )
bSuccess = false;
}
return bSuccess;
}
void
UHoudiniAssetInput::BeginDestroy()
{
Super::BeginDestroy();
// Destroy anything curve related.
DestroyInputCurve();
// Disconnect and destroy the asset we may have connected.
DisconnectAndDestroyInputAsset();
}
void
UHoudiniAssetInput::PostLoad()
{
Super::PostLoad();
// Generate widget related resources.
CreateWidgetResources();
// Patch input curve parameter links.
for ( TMap< FString, UHoudiniAssetParameter * >::TIterator IterParams( InputCurveParameters ); IterParams; ++IterParams )
{
FString ParameterKey = IterParams.Key();
UHoudiniAssetParameter * Parameter = IterParams.Value();
if ( Parameter )
{
Parameter->SetHoudiniAssetComponent( nullptr );
Parameter->SetParentParameter( this );
}
}
if ( InputCurve && !InputCurve->IsPendingKill() )
{
if (ChoiceIndex == EHoudiniAssetInputType::CurveInput)
{
// Set input callback object for this curve.
InputCurve->SetHoudiniAssetInput(this);
USceneComponent* RootComp = GetHoudiniAssetComponent();
if( RootComp && !RootComp->IsPendingKill() )
{
InputCurve->AttachToComponent( RootComp, FAttachmentTransformRules::KeepRelativeTransform );
}
}
else
{
// Manually destroying the "ghost" curve
InputCurve->DetachFromComponent( FDetachmentTransformRules::KeepRelativeTransform );
InputCurve->UnregisterComponent();
InputCurve->DestroyComponent();
InputCurve = nullptr;
}
}
if (InputOutlinerMeshArray.Num() > 0)
{
// Proper initialization of the outliner inputs is delayed to the first WorldTick,
// As some of the Actors' components might not be properly initialized yet
OutlinerInputsNeedPostLoadInit = true;
#if WITH_EDITOR
StartWorldOutlinerTicking();
#endif
}
// Also set the expanded ui
TransformUIExpanded.SetNumZeroed( InputObjects.Num() );
}
void
UHoudiniAssetInput::Serialize( FArchive & Ar )
{
// Call base implementation.
Super::Serialize( Ar );
Ar.UsingCustomVersion( FHoudiniCustomSerializationVersion::GUID );
// Serialize current choice selection.
SerializeEnumeration< EHoudiniAssetInputType::Enum >( Ar, ChoiceIndex );
Ar << ChoiceStringValue;
// We need these temporary variables for undo state tracking.
bool bLocalInputAssetConnectedInHoudini = bInputAssetConnectedInHoudini;
UHoudiniAssetComponent * LocalInputAssetComponent = InputAssetComponent;
Ar << HoudiniAssetInputFlagsPacked;
// Serialize input index.
Ar << InputIndex;
// Serialize input objects (if it's assigned).
if ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_MULTI_GEO_INPUT)
{
Ar << InputObjects;
}
else
{
UObject* InputObject = nullptr;
Ar << InputObject;
InputObjects.Empty();
InputObjects.Add( InputObject );
}
// Serialize input asset.
Ar << InputAssetComponent;
// Serialize curve and curve parameters (if we have those).
Ar << InputCurve;
Ar << InputCurveParameters;
// Serialize landscape used for input.
if (HoudiniAssetParameterVersion >= VER_HOUDINI_ENGINE_PARAM_LANDSCAPE_INPUT)
{
if (HoudiniAssetParameterVersion < VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_INPUT_SOFT_REF)
{
ALandscapeProxy* InputLandscapePtr = nullptr;
Ar << InputLandscapePtr;
InputLandscapeProxy = InputLandscapePtr;
}
else
{
Ar << InputLandscapeProxy;
}
}
// Serialize world outliner inputs.
if ( HoudiniAssetParameterVersion >= VER_HOUDINI_ENGINE_PARAM_WORLD_OUTLINER_INPUT )
{
Ar << InputOutlinerMeshArray;
}
// Create necessary widget resources.
if ( Ar.IsLoading() )
{
bLoadedParameter = true;
if ( Ar.IsTransacting() )
{
bInputAssetConnectedInHoudini = bLocalInputAssetConnectedInHoudini;
if ( LocalInputAssetComponent != InputAssetComponent )
{
if ( InputAssetComponent )
bInputAssetConnectedInHoudini = false;
if ( LocalInputAssetComponent )
LocalInputAssetComponent->RemoveDownstreamAsset( GetHoudiniAssetComponent(), InputIndex );
}
}
else
{
// If we're loading for real for the first time we need to reset this
// flag so we can reconnect when we get our parameters uploaded.
bInputAssetConnectedInHoudini = false;
}
}
if ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_UNREAL_SPLINE_RESOLUTION_PER_INPUT )
Ar << UnrealSplineResolution;
if ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_GEOMETRY_INPUT_TRANSFORMS )
{
Ar << InputTransforms;
}
else
{
InputTransforms.SetNum( InputObjects.Num() );
for( int32 n = 0; n < InputTransforms.Num(); n++ )
InputTransforms[ n ] = FTransform::Identity;
}
if ( ( HoudiniAssetParameterVersion >= VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_INPUT_LANDSCAPE_TRANSFORM )
&& ( HoudiniAssetParameterVersion != VER_HOUDINI_PLUGIN_SERIALIZATION_VERSION_419_SERIALIZATION_FIX ) )
Ar << InputLandscapeTransform;
}
void
UHoudiniAssetInput::AddReferencedObjects( UObject * InThis, FReferenceCollector & Collector )
{
UHoudiniAssetInput * HoudiniAssetInput = Cast< UHoudiniAssetInput >( InThis );
if ( HoudiniAssetInput && !HoudiniAssetInput->IsPendingKill() )
{
// Add reference to held geometry object.
if ( HoudiniAssetInput->InputObjects.Num()
&& ( HoudiniAssetInput->GetChoiceIndex() == EHoudiniAssetInputType::GeometryInput ) )
Collector.AddReferencedObjects( HoudiniAssetInput->InputObjects, InThis );
/*
// Add reference to held input asset component, if we have one.
if ( HoudiniAssetInput->InputAssetComponent && !HoudiniAssetInput->InputAssetComponent->IsPendingKill()
&& ( HoudiniAssetInput->GetChoiceIndex() == EHoudiniAssetInputType::AssetInput ) )
Collector.AddReferencedObject( HoudiniAssetInput->InputAssetComponent, InThis );
*/
// Add reference to held curve object.
if ( HoudiniAssetInput->InputCurve && !HoudiniAssetInput->InputCurve->IsPendingKill() )
Collector.AddReferencedObject( HoudiniAssetInput->InputCurve, InThis );
// Do not add references to Actors/Landscapes in our world outliner as if they are in
// an other level it will prevent saving due to the external references...
// Add references for all curve input parameters.
for ( TMap< FString, UHoudiniAssetParameter * >::TIterator IterParams( HoudiniAssetInput->InputCurveParameters );
IterParams; ++IterParams )
{
UHoudiniAssetParameter * HoudiniAssetParameter = IterParams.Value();
if ( HoudiniAssetParameter && !HoudiniAssetParameter->IsPendingKill() )
Collector.AddReferencedObject( HoudiniAssetParameter, InThis );
}
}
// Call base implementation.
Super::AddReferencedObjects( InThis, Collector );
}
void
UHoudiniAssetInput::ClearInputCurveParameters()
{
for( TMap< FString, UHoudiniAssetParameter * >::TIterator IterParams( InputCurveParameters ); IterParams; ++IterParams )
{
UHoudiniAssetParameter * HoudiniAssetParameter = IterParams.Value();
if ( HoudiniAssetParameter )
HoudiniAssetParameter->ConditionalBeginDestroy();
}
InputCurveParameters.Empty();
}
void
UHoudiniAssetInput::DisconnectInputCurve()
{
// If we have spline, delete it.
if ( InputCurve && !InputCurve->IsPendingKill() )
{
InputCurve->DetachFromComponent( FDetachmentTransformRules::KeepRelativeTransform );
InputCurve->UnregisterComponent();
}
}
void
UHoudiniAssetInput::DestroyInputCurve()
{
// If we have spline, delete it.
if ( InputCurve && !InputCurve->IsPendingKill() )
{
InputCurve->DetachFromComponent( FDetachmentTransformRules::KeepRelativeTransform );
InputCurve->UnregisterComponent();
InputCurve->DestroyComponent();
InputCurve = nullptr;
}
ClearInputCurveParameters();
}
#if WITH_EDITOR
void
UHoudiniAssetInput::OnStaticMeshDropped( UObject * InObject, int32 AtIndex )
{
UObject* InputObject = GetInputObject( AtIndex );
if ( InObject != InputObject )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Geometry Change" ),
PrimaryObject );
Modify();
if ( InputObjects.IsValidIndex( AtIndex ) )
{
InputObjects[ AtIndex ] = InObject;
}
else
{
check( AtIndex == 0 );
InputObjects.Add( InObject );
}
if ( !InputTransforms.IsValidIndex( AtIndex ) )
InputTransforms.Add( FTransform::Identity );
if ( !TransformUIExpanded.IsValidIndex( AtIndex ) )
TransformUIExpanded.Add( false );
bStaticMeshChanged = true;
MarkChanged();
OnParamStateChanged();
}
}
void
UHoudiniAssetInput::OnSkeletalMeshDropped( UObject * InObject, int32 AtIndex )
{
UObject* InputObject = GetSkeletonInputObject( AtIndex );
if ( InObject != InputObject )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Skeleton Input Change" ),
PrimaryObject );
Modify();
if ( SkeletonInputObjects.IsValidIndex( AtIndex ) )
{
SkeletonInputObjects[ AtIndex ] = InObject;
}
else
{
check( AtIndex == 0 );
SkeletonInputObjects.Add( InObject );
}
/*
if ( !InputTransforms.IsValidIndex( AtIndex ) )
InputTransforms.Add( FTransform::Identity );
if ( !TransformUIExpanded.IsValidIndex( AtIndex ) )
TransformUIExpanded.Add( false );
*/
bStaticMeshChanged = true;
MarkChanged();
OnParamStateChanged();
}
}
FReply
UHoudiniAssetInput::OnThumbnailDoubleClick( const FGeometry & InMyGeometry, const FPointerEvent & InMouseEvent, int32 AtIndex )
{
UObject* InputObject = GetInputObject( AtIndex );
if ( InputObject && InputObject->IsA( UStaticMesh::StaticClass() ) && GEditor )
GEditor->EditObject( InputObject );
return FReply::Handled();
}
void UHoudiniAssetInput::OnStaticMeshBrowse( int32 AtIndex )
{
UObject* InputObject = GetInputObject( AtIndex );
if ( GEditor && InputObject )
{
TArray< UObject * > Objects;
Objects.Add( InputObject );
GEditor->SyncBrowserToObjects( Objects );
}
}
void UHoudiniAssetInput::OnSkeletalMeshBrowse( int32 AtIndex )
{
UObject* InputObject = GetSkeletonInputObject( AtIndex );
if ( GEditor && InputObject )
{
TArray< UObject * > Objects;
Objects.Add( InputObject );
GEditor->SyncBrowserToObjects( Objects );
}
}
FReply
UHoudiniAssetInput::OnResetStaticMeshClicked( int32 AtIndex )
{
OnStaticMeshDropped( nullptr, AtIndex );
return FReply::Handled();
}
FReply
UHoudiniAssetInput::OnResetSkeletalMeshClicked( int32 AtIndex )
{
OnSkeletalMeshDropped( nullptr, AtIndex );
return FReply::Handled();
}
void
UHoudiniAssetInput::OnChoiceChange( TSharedPtr< FString > NewChoice )
{
if ( !NewChoice.IsValid() )
return;
ChoiceStringValue = *( NewChoice.Get() );
// We need to match selection based on label.
bool bLocalChanged = false;
int32 ActiveLabel = 0;
for ( int32 LabelIdx = 0; LabelIdx < StringChoiceLabels.Num(); ++LabelIdx )
{
FString * ChoiceLabel = StringChoiceLabels[ LabelIdx ].Get();
if ( ChoiceLabel && ChoiceLabel->Equals( ChoiceStringValue ) )
{
bLocalChanged = true;
ActiveLabel = LabelIdx;
break;
}
}
if ( !bLocalChanged )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Type Change" ),
PrimaryObject );
Modify();
// Switch mode.
EHoudiniAssetInputType::Enum newChoice = static_cast<EHoudiniAssetInputType::Enum>(ActiveLabel);
ChangeInputType( newChoice, false );
MarkChanged();
}
bool
UHoudiniAssetInput::ChangeInputType(const EHoudiniAssetInputType::Enum& newType, const bool& ForceRefresh )
{
if ( ChoiceIndex == newType && !ForceRefresh )
return true;
switch ( ChoiceIndex )
{
case EHoudiniAssetInputType::GeometryInput:
{
// We are switching away from geometry input.
break;
}
case EHoudiniAssetInputType::AssetInput:
{
// We are switching away from asset input. WIll be handled in DisconnectAndDestroyInputAsset
break;
}
case EHoudiniAssetInputType::CurveInput:
{
// We are switching away from curve input.
DisconnectInputCurve();
break;
}
case EHoudiniAssetInputType::LandscapeInput:
{
break;
}
case EHoudiniAssetInputType::WorldInput:
{
// We are switching away from World Outliner input.
// Stop monitoring the Actors for transform changes.
StopWorldOutlinerTicking();
break;
}
case EHoudiniAssetInputType::SkeletonInput:
{
// We are switching away from a skeleton input.
break;
}
default:
{
// Unhandled new input type?
check(0);
break;
}
}
// Disconnect currently connected asset.
DisconnectAndDestroyInputAsset();
// Make sure we'll fully update the editor properties
if ( ChoiceIndex != newType && InputAssetComponent )
InputAssetComponent->bEditorPropertiesNeedFullUpdate = true;
// Switch mode.
ChoiceIndex = newType;
// Switch mode.
switch ( newType )
{
case EHoudiniAssetInputType::GeometryInput:
{
// We are switching to geometry input.
if ( InputObjects.Num() )
bStaticMeshChanged = true;
break;
}
case EHoudiniAssetInputType::AssetInput:
{
// We are switching to asset input.
ConnectInputAssetActor();
break;
}
case EHoudiniAssetInputType::CurveInput:
{
// We are switching to curve input.
// Create new spline component if necessary.
USceneComponent* RootComp = GetHoudiniAssetComponent();
if( RootComp && !RootComp->IsPendingKill()
&& RootComp->GetOwner() && !RootComp->GetOwner()->IsPendingKill() )
{
if( !InputCurve || InputCurve->IsPendingKill() )
{
InputCurve = NewObject< UHoudiniSplineComponent >(
RootComp->GetOwner(), UHoudiniSplineComponent::StaticClass(),
NAME_None, RF_Public | RF_Transactional );
}
// Attach or re-attach curve component to asset.
InputCurve->AttachToComponent( RootComp, FAttachmentTransformRules::KeepRelativeTransform );
InputCurve->RegisterComponent();
InputCurve->SetVisibility( true );
InputCurve->SetHoudiniAssetInput( this );
bSwitchedToCurve = true;
}
break;
}
case EHoudiniAssetInputType::LandscapeInput:
{
// We are switching to Landscape input.
break;
}
case EHoudiniAssetInputType::WorldInput:
{
// We are switching to World Outliner input.
// Start monitoring the Actors for transform changes.
StartWorldOutlinerTicking();
// Force recook and reconnect of the input assets.
HAPI_NodeId HostAssetId = GetAssetId();
if ( FHoudiniEngineUtils::HapiCreateInputNodeForWorldOutliner(
HostAssetId, InputOutlinerMeshArray,
ConnectedAssetId, CreatedInputDataAssetIds,
UnrealSplineResolution ) )
{
ConnectInputNode();
}
break;
}
case EHoudiniAssetInputType::SkeletonInput:
{
// We are switching to skeleton input.
if ( SkeletonInputObjects.Num() )
bStaticMeshChanged = true;
break;
}
default:
{
// Unhandled new input type?
check(0);
break;
}
}
// Make sure the Input choice string corresponds to the selected input type
if ( StringChoiceLabels.IsValidIndex( ChoiceIndex ) )
{
FString NewStringChoice = *( StringChoiceLabels[ ChoiceIndex ].Get() );
if ( !ChoiceStringValue.Equals(NewStringChoice, ESearchCase::IgnoreCase ) )
ChoiceStringValue = NewStringChoice;
}
// If we have input object and geometry asset, we need to connect it back.
MarkChanged();
return true;
}
bool
UHoudiniAssetInput::OnShouldFilterActor( const AActor * const Actor ) const
{
if ( !Actor )
return false;
if ( ChoiceIndex == EHoudiniAssetInputType::AssetInput )
{
// Only return HoudiniAssetActors
if ( Actor->IsA<AHoudiniAssetActor>() )
{
// But not our own Asset Actor
if( const USceneComponent* RootComp = Cast<const USceneComponent>( GetHoudiniAssetComponent() ))
{
if( RootComp && Cast<AHoudiniAssetActor>( RootComp->GetOwner() ) != Actor )
return true;
}
return false;
}
}
else if ( ChoiceIndex == EHoudiniAssetInputType::LandscapeInput )
{
// Only returns Landscape
if ( Actor->IsA<ALandscapeProxy>() )
{
ALandscapeProxy* LandscapeProxy = const_cast<ALandscapeProxy *>(Cast<const ALandscapeProxy>(Actor));
// But not a landscape generated by this asset
const UHoudiniAssetComponent * HoudiniAssetComponent = GetHoudiniAssetComponent();
if (HoudiniAssetComponent && LandscapeProxy && !HoudiniAssetComponent->HasLandscapeActor( LandscapeProxy->GetLandscapeActor() ) )
return true;
}
return false;
}
else if ( ChoiceIndex == EHoudiniAssetInputType::WorldInput )
{
for ( auto & OutlinerMesh : InputOutlinerMeshArray )
if ( OutlinerMesh.ActorPtr.Get() == Actor )
return true;
}
return false;
}
void
UHoudiniAssetInput::OnActorSelected( AActor * Actor )
{
if ( ChoiceIndex == EHoudiniAssetInputType::AssetInput )
return OnInputActorSelected( Actor );
else if ( ChoiceIndex == EHoudiniAssetInputType::WorldInput )
return OnWorldOutlinerActorSelected( Actor );
else if ( ChoiceIndex == EHoudiniAssetInputType::LandscapeInput )
return OnLandscapeActorSelected( Actor );
}
void
UHoudiniAssetInput::OnInputActorSelected( AActor * Actor )
{
if ( ( !Actor || Actor->IsPendingKill() )
&& ( InputAssetComponent && !InputAssetComponent->IsPendingKill() ) )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Asset Change" ),
PrimaryObject );
Modify();
// Tell the old input asset we are no longer connected.
InputAssetComponent->RemoveDownstreamAsset( GetHoudiniAssetComponent(), InputIndex );
// We cleared the selection so just reset all the values.
InputAssetComponent = nullptr;
ConnectedAssetId = -1;
}
else
{
AHoudiniAssetActor * HoudiniAssetActor = (AHoudiniAssetActor *)Actor;
if ( !HoudiniAssetActor || HoudiniAssetActor->IsPendingKill() )
return;
UHoudiniAssetComponent * ConnectedHoudiniAssetComponent = HoudiniAssetActor->GetHoudiniAssetComponent();
if ( !ConnectedHoudiniAssetComponent || ConnectedHoudiniAssetComponent->IsPendingKill() )
return;
// If we just selected the already selected Actor do nothing if we are properly connected.
if ( ConnectedHoudiniAssetComponent == InputAssetComponent && bInputAssetConnectedInHoudini)
return;
// Do not allow the input asset to be ourself!
if ( ConnectedHoudiniAssetComponent == PrimaryObject )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Asset Change" ),
PrimaryObject );
Modify();
// Tell the old input asset we are no longer connected.
if ( InputAssetComponent && !InputAssetComponent->IsPendingKill() )
InputAssetComponent->RemoveDownstreamAsset( GetHoudiniAssetComponent(), InputIndex );
InputAssetComponent = ConnectedHoudiniAssetComponent;
ConnectedAssetId = InputAssetComponent->GetAssetId();
// Do we have to wait for the input asset to cook?
const UHoudiniAssetComponent* HAC = GetHoudiniAssetComponent();
if ( HAC && !HAC->IsPendingKill() )
GetHoudiniAssetComponent()->UpdateWaitingForUpstreamAssetsToInstantiate( true );
// Mark as disconnected since we need to reconnect to the new asset.
bInputAssetConnectedInHoudini = false;
}
MarkChanged();
}
void
UHoudiniAssetInput::OnLandscapeActorSelected( AActor * Actor )
{
ALandscapeProxy * LandscapeProxy = Cast< ALandscapeProxy >( Actor );
if ( LandscapeProxy && !LandscapeProxy->IsPendingKill() )
{
// If we just selected the already selected landscape, do nothing.
if ( LandscapeProxy == InputLandscapeProxy )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Landscape Change." ),
PrimaryObject );
Modify();
// Store new landscape.
InputLandscapeProxy = LandscapeProxy;
InputLandscapeTransform = LandscapeProxy->ActorToWorld();
}
else
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Landscape Change." ),
PrimaryObject );
Modify();
InputLandscapeProxy = nullptr;
InputLandscapeTransform = FTransform::Identity;
}
MarkChanged();
}
void
UHoudiniAssetInput::OnWorldOutlinerActorSelected( AActor * )
{
// Do nothing.
}
void
UHoudiniAssetInput::TickWorldOutlinerInputs()
{
// Do not tick non world inputs
if (ChoiceIndex != EHoudiniAssetInputType::WorldInput)
{
return;
}
// Only tick/cook when in Editor
// This prevents PIE cooks or runtime cooks due to inputs moving
if (!GetWorld() || (GetWorld()->WorldType != EWorldType::Editor))
{
return;
}
#if WITH_EDITOR
// Stop outliner objects from causing recooks while input objects are dragged around
if (FHoudiniMoveTracker::Get().IsObjectMoving)
{
return;
}
#endif
// PostLoad initialization must be done on the first tick
// as some components might now have been fully initialized during PostLoad()
if ( OutlinerInputsNeedPostLoadInit )
{
for (auto & OutlinerInput : InputOutlinerMeshArray)
{
if (OutlinerInput.ActorPtr.IsValid())
continue;
// Try to update the actor ptr via the pathname
OutlinerInput.TryToUpdateActorPtrFromActorPathName();
}
UpdateInputOulinerArray();
// The spline Transform array might need to be rebuilt after loading
for (auto & OutlinerInput : InputOutlinerMeshArray)
{
OutlinerInput.RebuildSplineTransformsArrayIfNeeded();
OutlinerInput.KeepWorldTransform = bKeepWorldTransform;
OutlinerInput.SplineResolution = UnrealSplineResolution;
}
OutlinerInputsNeedPostLoadInit = false;
return;
}
// Don't do anything more if HEngine cooking is paused
// We need to be able to detect updates/changes to the input actor when cooking is unpaused
if ( !FHoudiniEngine::Get().GetEnableCookingGlobal() )
return;
// Lambda use to Modify / Prechange only once
bool bLocalChanged = false;
auto MarkLocalChanged = [&]()
{
if ( !bLocalChanged )
{
Modify();
bLocalChanged = true;
}
};
// Refresh the input's component from the actor
// If the Actor is a blueprint, its component are recreated for every modification
if ( UpdateInputOulinerArray() )
{
MarkLocalChanged();
bStaticMeshChanged = true;
MarkChanged();
}
//
if ( bStaticMeshChanged )
return;
// Check for destroyed / modified outliner inputs
for ( auto & OutlinerInput : InputOutlinerMeshArray )
{
if ( !OutlinerInput.ActorPtr.IsValid() )
continue;
if ( OutlinerInput.HasActorTransformChanged() && ( OutlinerInput.AssetId >= 0 ) )
{
MarkLocalChanged();
// Updates to the new Transform
UpdateWorldOutlinerTransforms( OutlinerInput );
HAPI_TransformEuler HapiTransform;
FHoudiniApi::TransformEuler_Init(&HapiTransform);
//FMemory::Memzero< HAPI_TransformEuler >( HapiTransform );
FHoudiniEngineUtils::TranslateUnrealTransform( OutlinerInput.ComponentTransform, HapiTransform );
HAPI_NodeInfo LocalAssetNodeInfo;
FHoudiniApi::NodeInfo_Init(&LocalAssetNodeInfo);
const HAPI_Result LocalResult = FHoudiniApi::GetNodeInfo(
FHoudiniEngine::Get().GetSession(), OutlinerInput.AssetId,
&LocalAssetNodeInfo);
if ( LocalResult == HAPI_RESULT_SUCCESS )
FHoudiniApi::SetObjectTransform(
FHoudiniEngine::Get().GetSession(),
LocalAssetNodeInfo.parentId, &HapiTransform );
}
else if ( OutlinerInput.HasComponentTransformChanged()
|| ( OutlinerInput.HasSplineComponentChanged( UnrealSplineResolution ) )
|| ( OutlinerInput.KeepWorldTransform != bKeepWorldTransform ) )
{
MarkLocalChanged();
// Update to the new Transforms
UpdateWorldOutlinerTransforms( OutlinerInput );
// The component or spline has been modified so so we need to indicate that the "static mesh"
// has changed in order to rebuild the asset properly in UploadParameterValue()
bStaticMeshChanged = true;
}
else if ( OutlinerInput.HasComponentMaterialsChanged() )
{
MarkLocalChanged();
// Update the materials
UpdateWorldOutlinerMaterials( OutlinerInput );
// The materials have been changed so so we need to indicate that the "static mesh"
// has changed in order to rebuild the asset properly in UploadParameterValue()
bStaticMeshChanged = true;
}
}
if ( bLocalChanged )
MarkChanged();
}
#endif
void
UHoudiniAssetInput::ConnectInputAssetActor()
{
// Check the component we're connected to is valid
if ( !InputAssetComponent )
return;
if ( !FHoudiniEngineUtils::IsValidNodeId( InputAssetComponent->GetAssetId() ) )
return;
// Check we have the correct Id
if ( ConnectedAssetId != InputAssetComponent->GetAssetId() )
ConnectedAssetId = InputAssetComponent->GetAssetId();
// Connect if needed
if ( !bInputAssetConnectedInHoudini )
{
InputAssetComponent->AddDownstreamAsset( GetHoudiniAssetComponent(), InputIndex );
bInputAssetConnectedInHoudini = ConnectInputNode();
}
}
void
UHoudiniAssetInput::DisconnectInputAssetActor()
{
if ( bInputAssetConnectedInHoudini && !InputAssetComponent )
{
if( bIsObjectPathParameter )
{
std::string ParamNameString = TCHAR_TO_UTF8( *GetParameterName() );
FHoudiniApi::SetParmStringValue(
FHoudiniEngine::Get().GetSession(), NodeId, "",
ParmId, 0);
}
else
{
FHoudiniEngineUtils::HapiDisconnectAsset( GetAssetId(), InputIndex );
}
bInputAssetConnectedInHoudini = false;
}
}
void
UHoudiniAssetInput::ConnectLandscapeActor()
{}
void
UHoudiniAssetInput::DisconnectLandscapeActor()
{}
HAPI_NodeId
UHoudiniAssetInput::GetConnectedAssetId() const
{
return ConnectedAssetId;
}
bool
UHoudiniAssetInput::IsGeometryAssetConnected() const
{
if ( FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId ) && ( ChoiceIndex == EHoudiniAssetInputType::GeometryInput ) )
{
for ( auto InputObject : InputObjects )
{
if ( InputObject && !InputObject->IsPendingKill() )
return true;
}
}
return false;
}
bool
UHoudiniAssetInput::IsInputAssetConnected() const
{
if ( FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId )
&& InputAssetComponent
&& !InputAssetComponent->IsPendingKill()
&& bInputAssetConnectedInHoudini )
{
if ( ChoiceIndex == EHoudiniAssetInputType::AssetInput )
return true;
}
return false;
}
bool
UHoudiniAssetInput::IsCurveAssetConnected() const
{
if (InputCurve && !InputCurve->IsPendingKill() && ( ChoiceIndex == EHoudiniAssetInputType::CurveInput ) )
return true;
return false;
}
bool
UHoudiniAssetInput::IsLandscapeAssetConnected() const
{
if ( FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId ) )
{
if ( ChoiceIndex == EHoudiniAssetInputType::LandscapeInput )
return true;
}
return false;
}
bool
UHoudiniAssetInput::IsWorldInputAssetConnected() const
{
if ( FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId ) )
{
if ( ChoiceIndex == EHoudiniAssetInputType::WorldInput )
return true;
}
return false;
}
void
UHoudiniAssetInput::OnInputCurveChanged()
{
MarkChanged();
}
void
UHoudiniAssetInput::ExternalDisconnectInputAssetActor()
{
InputAssetComponent = nullptr;
ConnectedAssetId = -1;
MarkChanged();
}
bool
UHoudiniAssetInput::DoesInputAssetNeedInstantiation()
{
//if ( ChoiceIndex != EHoudiniAssetInputType::AssetInput )
// return false;
if ( InputAssetComponent == nullptr )
return false;
if ( !FHoudiniEngineUtils::IsValidNodeId(InputAssetComponent->GetAssetId() ) )
return true;
return false;
}
UHoudiniAssetComponent *
UHoudiniAssetInput::GetConnectedInputAssetComponent()
{
return InputAssetComponent;
}
void
UHoudiniAssetInput::NotifyChildParameterChanged( UHoudiniAssetParameter * HoudiniAssetParameter )
{
if ( HoudiniAssetParameter && ChoiceIndex == EHoudiniAssetInputType::CurveInput )
{
if ( FHoudiniEngineUtils::IsValidNodeId( ConnectedAssetId ) )
{
// We need to upload changed param back to HAPI.
if (! HoudiniAssetParameter->UploadParameterValue() )
{
HOUDINI_LOG_ERROR( TEXT("%s UploadParameterValue failed"), InputAssetComponent ? *InputAssetComponent->GetOwner()->GetName() : TEXT("unknown") );
}
}
MarkChanged();
}
}
bool
UHoudiniAssetInput::UpdateInputCurve()
{
bool Success = true;
FString CurvePointsString;
EHoudiniSplineComponentType::Enum CurveTypeValue = EHoudiniSplineComponentType::Bezier;
EHoudiniSplineComponentMethod::Enum CurveMethodValue = EHoudiniSplineComponentMethod::CVs;
int32 CurveClosed = 1;
if(ConnectedAssetId != -1)
{
FHoudiniEngineUtils::HapiGetParameterDataAsString(
ConnectedAssetId, HAPI_UNREAL_PARAM_CURVE_COORDS, TEXT( "" ),
CurvePointsString );
FHoudiniEngineUtils::HapiGetParameterDataAsInteger(
ConnectedAssetId, HAPI_UNREAL_PARAM_CURVE_TYPE,
(int32) EHoudiniSplineComponentType::Bezier, (int32 &) CurveTypeValue );
FHoudiniEngineUtils::HapiGetParameterDataAsInteger(
ConnectedAssetId, HAPI_UNREAL_PARAM_CURVE_METHOD,
(int32) EHoudiniSplineComponentMethod::CVs, (int32 &) CurveMethodValue );
FHoudiniEngineUtils::HapiGetParameterDataAsInteger(
ConnectedAssetId, HAPI_UNREAL_PARAM_CURVE_CLOSED, 1, CurveClosed );
}
// We need to get the NodeInfo to get the parent id
HAPI_NodeInfo NodeInfo;
FHoudiniApi::NodeInfo_Init(&NodeInfo);
HOUDINI_CHECK_ERROR_RETURN(
FHoudiniApi::GetNodeInfo(FHoudiniEngine::Get().GetSession(), ConnectedAssetId, &NodeInfo),
false);
// Construct geo part object.
FHoudiniGeoPartObject HoudiniGeoPartObject( ConnectedAssetId, NodeInfo.parentId, ConnectedAssetId, 0 );
HoudiniGeoPartObject.bIsCurve = true;
HAPI_AttributeInfo AttributeRefinedCurvePositions;
FHoudiniApi::AttributeInfo_Init(&AttributeRefinedCurvePositions);
//FMemory::Memzero< HAPI_AttributeInfo >( AttributeRefinedCurvePositions );
TArray< float > RefinedCurvePositions;
FHoudiniEngineUtils::HapiGetAttributeDataAsFloat(
HoudiniGeoPartObject, HAPI_UNREAL_ATTRIB_POSITION,
AttributeRefinedCurvePositions, RefinedCurvePositions );
// Process coords string and extract positions.
TArray< FVector > CurvePoints;
FHoudiniEngineUtils::ExtractStringPositions( CurvePointsString, CurvePoints );
TArray< FVector > CurveDisplayPoints;
FHoudiniEngineUtils::ConvertScaleAndFlipVectorData( RefinedCurvePositions, CurveDisplayPoints );
if (InputCurve && !InputCurve->IsPendingKill() )
{
InputCurve->Construct(
HoudiniGeoPartObject, CurveDisplayPoints, CurveTypeValue, CurveMethodValue,
(CurveClosed == 1));
}
// We also need to construct curve parameters we care about.
TMap< FString, UHoudiniAssetParameter * > NewInputCurveParameters;
TArray< HAPI_ParmInfo > ParmInfos;
ParmInfos.SetNumUninitialized( NodeInfo.parmCount );
for (int32 Idx = 0; Idx < ParmInfos.Num(); Idx++)
FHoudiniApi::ParmInfo_Init(&(ParmInfos[Idx]));
HOUDINI_CHECK_ERROR_RETURN(
FHoudiniApi::GetParameters(
FHoudiniEngine::Get().GetSession(), ConnectedAssetId,
&ParmInfos[ 0 ], 0, NodeInfo.parmCount ), false);
// Retrieve integer values for this asset.
TArray< int32 > ParmValueInts;
ParmValueInts.SetNumZeroed( NodeInfo.parmIntValueCount );
if ( NodeInfo.parmIntValueCount > 0 )
{
HOUDINI_CHECK_ERROR_RETURN(
FHoudiniApi::GetParmIntValues(
FHoudiniEngine::Get().GetSession(), ConnectedAssetId, &ParmValueInts[ 0 ], 0, NodeInfo.parmIntValueCount ),
false );
}
// Retrieve float values for this asset.
TArray< float > ParmValueFloats;
ParmValueFloats.SetNumZeroed( NodeInfo.parmFloatValueCount );
if ( NodeInfo.parmFloatValueCount > 0 )
{
HOUDINI_CHECK_ERROR_RETURN(
FHoudiniApi::GetParmFloatValues(
FHoudiniEngine::Get().GetSession(), ConnectedAssetId, &ParmValueFloats[ 0 ], 0, NodeInfo.parmFloatValueCount ),
false );
}
// Retrieve string values for this asset.
TArray< HAPI_StringHandle > ParmValueStrings;
ParmValueStrings.SetNumZeroed( NodeInfo.parmStringValueCount );
if ( NodeInfo.parmStringValueCount > 0 )
{
HOUDINI_CHECK_ERROR_RETURN(
FHoudiniApi::GetParmStringValues(
FHoudiniEngine::Get().GetSession(), ConnectedAssetId, true, &ParmValueStrings[ 0 ], 0, NodeInfo.parmStringValueCount ),
false );
}
// Create properties for parameters.
for ( int32 ParamIdx = 0; ParamIdx < NodeInfo.parmCount; ++ParamIdx )
{
// Retrieve param info at this index.
const HAPI_ParmInfo & ParmInfo = ParmInfos[ ParamIdx ];
// If parameter is invisible, skip it.
if ( ParmInfo.invisible )
continue;
FString LocalParameterName;
FHoudiniEngineString HoudiniEngineString( ParmInfo.nameSH );
if ( !HoudiniEngineString.ToFString( LocalParameterName ) )
{
// We had trouble retrieving name of this parameter, skip it.
continue;
}
// See if it's one of parameters we are interested in.
if ( !LocalParameterName.Equals( TEXT( HAPI_UNREAL_PARAM_CURVE_METHOD ) ) &&
!LocalParameterName.Equals( TEXT( HAPI_UNREAL_PARAM_CURVE_TYPE ) ) &&
!LocalParameterName.Equals( TEXT( HAPI_UNREAL_PARAM_CURVE_CLOSED ) ) )
{
// Not parameter we are interested in.
continue;
}
// See if this parameter has already been created.
UHoudiniAssetParameter * const * FoundHoudiniAssetParameter = InputCurveParameters.Find( LocalParameterName );
UHoudiniAssetParameter * HoudiniAssetParameter = FoundHoudiniAssetParameter ? *FoundHoudiniAssetParameter : nullptr;
// If parameter exists, we can reuse it.
if ( HoudiniAssetParameter && !HoudiniAssetParameter->IsPendingKill() )
{
// Remove parameter from current map.
InputCurveParameters.Remove( LocalParameterName );
// Reinitialize parameter and add it to map.
HoudiniAssetParameter->CreateParameter( nullptr, this, ConnectedAssetId, ParmInfo );
NewInputCurveParameters.Add( LocalParameterName, HoudiniAssetParameter );
continue;
}
else
{
if ( ParmInfo.type == HAPI_PARMTYPE_INT )
{
if ( !ParmInfo.choiceCount )
HoudiniAssetParameter = UHoudiniAssetParameterInt::Create( nullptr, this, ConnectedAssetId, ParmInfo );
else
HoudiniAssetParameter = UHoudiniAssetParameterChoice::Create( nullptr, this, ConnectedAssetId, ParmInfo );
}
else if ( ParmInfo.type == HAPI_PARMTYPE_TOGGLE )
{
HoudiniAssetParameter = UHoudiniAssetParameterToggle::Create( nullptr, this, ConnectedAssetId, ParmInfo );
}
else
{
Success = false;
check( false );
}
if ( HoudiniAssetParameter && !HoudiniAssetParameter->IsPendingKill() )
NewInputCurveParameters.Add( LocalParameterName, HoudiniAssetParameter );
}
}
ClearInputCurveParameters();
InputCurveParameters = NewInputCurveParameters;
if ( bSwitchedToCurve )
{
#if WITH_EDITOR
// We need to trigger details panel update.
OnParamStateChanged();
// The editor caches the current selection visualizer, so we need to trick
// and pretend the selection has changed so that the HSplineVisualizer can be drawn immediately
if (GUnrealEd)
GUnrealEd->NoteSelectionChange();
#endif
bSwitchedToCurve = false;
}
return Success;
}
FText
UHoudiniAssetInput::HandleChoiceContentText() const
{
return FText::FromString( ChoiceStringValue );
}
#if WITH_EDITOR
void
UHoudiniAssetInput::CheckStateChangedExportOnlySelected( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportSelectionOnly != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Export Selected Landscape Components mode change." ),
PrimaryObject );
Modify();
bLandscapeExportSelectionOnly = bState;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportOnlySelected() const
{
if ( bLandscapeExportSelectionOnly )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedAutoSelectLandscape( ECheckBoxState NewState )
{
int32 bState = (NewState == ECheckBoxState::Checked);
if (bLandscapeAutoSelectComponent != bState)
{
// Record undo information.
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Export Auto-Select Landscape Components mode change."),
PrimaryObject);
Modify();
bLandscapeAutoSelectComponent = bState;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedAutoSelectLandscape() const
{
if ( bLandscapeAutoSelectComponent )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportCurves( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportCurves != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Export Landscape Curve mode change." ),
PrimaryObject );
Modify();
bLandscapeExportCurves = bState;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportCurves() const
{
if ( bLandscapeExportCurves )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportAsMesh( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportAsMesh != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Export Landscape As Mesh mode change." ),
PrimaryObject );
Modify();
bLandscapeExportAsMesh = bState;
if ( bState )
bLandscapeExportAsHeightfield = false;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportAsMesh() const
{
if ( bLandscapeExportAsMesh )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportAsHeightfield( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportAsHeightfield != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Export Landscape As Heightfield mode change."),
PrimaryObject);
Modify();
bLandscapeExportAsHeightfield = bState;
if ( bState )
bLandscapeExportAsMesh = false;
else
bLandscapeExportAsMesh = true;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportAsHeightfield() const
{
if ( bLandscapeExportAsHeightfield )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportAsPoints( ECheckBoxState NewState )
{
int32 bState = (NewState == ECheckBoxState::Checked);
uint32 bExportAsPoints = !bLandscapeExportAsHeightfield && !bLandscapeExportAsMesh;
if (bExportAsPoints != bState)
{
// Record undo information.
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Export Landscape As Points mode change."),
PrimaryObject);
Modify();
if ( bState )
{
bLandscapeExportAsHeightfield = false;
bLandscapeExportAsMesh = false;
}
else
{
bLandscapeExportAsHeightfield = true;
bLandscapeExportAsMesh = false;
}
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportAsPoints() const
{
if ( !bLandscapeExportAsHeightfield && !bLandscapeExportAsMesh )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportMaterials( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportMaterials != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Export Landscape Materials mode change." ),
PrimaryObject );
Modify();
bLandscapeExportMaterials = bState;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportMaterials() const
{
if ( bLandscapeExportMaterials )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportLighting( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportLighting != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Export Landscape Lighting mode change." ),
PrimaryObject );
Modify();
bLandscapeExportLighting = bState;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportLighting() const
{
if ( bLandscapeExportLighting )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportNormalizedUVs( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportNormalizedUVs != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Export Landscape Normalized UVs mode change." ),
PrimaryObject );
Modify();
bLandscapeExportNormalizedUVs = bState;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportNormalizedUVs() const
{
if ( bLandscapeExportNormalizedUVs )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportTileUVs( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bLandscapeExportTileUVs != bState )
{
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Export Landscape Tile UVs mode change." ),
PrimaryObject );
Modify();
bLandscapeExportTileUVs = bState;
// Mark this parameter as changed.
MarkChanged();
}
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportTileUVs() const
{
if ( bLandscapeExportTileUVs )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedKeepWorldTransform(ECheckBoxState NewState)
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bKeepWorldTransform == bState )
return;
// Record undo information.
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Input Transform Type change."),
PrimaryObject);
Modify();
bKeepWorldTransform = bState;
// Mark this parameter as changed.
MarkChanged();
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedKeepWorldTransform() const
{
if ( bKeepWorldTransform == 2 )
{
if (GetDefaultTranformTypeValue())
return ECheckBoxState::Checked;
else
return ECheckBoxState::Unchecked;
}
else if ( bKeepWorldTransform )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportAllLODs( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bExportAllLODs == bState )
return;
// Record undo information.
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Input Export all LODs changed."),
PrimaryObject);
Modify();
bExportAllLODs = bState;
// Changing the export of LODs changes the StaticMesh!
if ( HasLODs() )
bStaticMeshChanged = true;
// Mark this parameter as changed.
MarkChanged();
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportAllLODs() const
{
if ( bExportAllLODs )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedExportSockets( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bExportSockets == bState )
return;
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input export sockets changed." ),
PrimaryObject );
Modify();
bExportSockets = bState;
// Changing the export of LODs changes the StaticMesh!
if ( HasSockets() )
bStaticMeshChanged = true;
// Mark this parameter as changed.
MarkChanged();
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedExportSockets() const
{
if (bExportSockets)
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedUpdateInputLandscape( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bUpdateInputLandscape == bState )
return;
// Record undo information.
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Input Update Input Landscape changed."),
PrimaryObject);
Modify();
UHoudiniAssetComponent* ParentComponent = GetHoudiniAssetComponent();
if ( InputLandscapeProxy && ParentComponent )
{
if ( bState )
{
// Build the backup file name
FString BackupBaseName = ParentComponent->GetTempCookFolder().ToString()
+ TEXT("/")
+ InputLandscapeProxy->GetName()
+ TEXT("_")
+ ParentComponent->GetComponentGuid().ToString().Left(FHoudiniEngineUtils::PackageGUIDComponentNameLength);
// We need to cache the input landscape to a file
//FString BaseName = TEXT("/Game/HoudiniEngine/Temp/LandscapeBak");
FHoudiniLandscapeUtils::BackupLandscapeToFile( BackupBaseName, InputLandscapeProxy.Get());
InputLandscapeTransform = InputLandscapeProxy->ActorToWorld();
}
else
{
// Detach the input landscape from the HDA
InputLandscapeProxy->DetachFromActor( FDetachmentTransformRules::KeepWorldTransform );
// Clear the landscape map to avoid reusing the input landscape
ParentComponent->ClearLandscapes();
// Restore the input landscape's backup data
FHoudiniLandscapeUtils::RestoreLandscapeFromFile( InputLandscapeProxy.Get());
// Reapply the source Landscape's transform
InputLandscapeProxy->SetActorTransform(InputLandscapeTransform);
}
}
bUpdateInputLandscape = bState;
// Mark this parameter as changed.
MarkChanged();
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedUpdateInputLandscape() const
{
if ( bUpdateInputLandscape )
return ECheckBoxState::Checked;
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::CheckStateChangedPackBeforeMerge( ECheckBoxState NewState )
{
int32 bState = ( NewState == ECheckBoxState::Checked );
if ( bPackBeforeMerge == bState )
return;
// Record undo information.
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Pack Before Merge changed." ),
PrimaryObject );
Modify();
bPackBeforeMerge = bState;
// Mark this parameter as changed.
MarkChanged( true );
}
ECheckBoxState
UHoudiniAssetInput::IsCheckedPackBeforeMerge() const
{
if ( bPackBeforeMerge )
return ECheckBoxState::Checked;
else
return ECheckBoxState::Unchecked;
}
void
UHoudiniAssetInput::OnInsertGeo( int32 AtIndex )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Geometry Change" ),
PrimaryObject );
Modify();
InputObjects.Insert( nullptr, AtIndex );
InputTransforms.Insert( FTransform::Identity, AtIndex );
TransformUIExpanded.Insert( false, AtIndex );
bStaticMeshChanged = true;
MarkChanged();
OnParamStateChanged();
}
void
UHoudiniAssetInput::OnDeleteGeo( int32 AtIndex )
{
if ( ensure( InputObjects.IsValidIndex( AtIndex ) && InputTransforms.IsValidIndex( AtIndex ) ) )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Geometry Change" ),
PrimaryObject );
Modify();
InputObjects.RemoveAt( AtIndex );
InputTransforms.RemoveAt( AtIndex );
TransformUIExpanded.RemoveAt( AtIndex );
bStaticMeshChanged = true;
MarkChanged();
OnParamStateChanged();
}
}
void
UHoudiniAssetInput::OnDuplicateGeo( int32 AtIndex )
{
if ( ensure( InputObjects.IsValidIndex( AtIndex ) ) && InputTransforms.IsValidIndex( AtIndex ) )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Geometry Change" ),
PrimaryObject );
Modify();
UObject* Dupe = InputObjects[AtIndex];
InputObjects.Insert( Dupe, AtIndex );
FTransform DupeTransform = InputTransforms[AtIndex];
InputTransforms.Insert( DupeTransform, AtIndex );
bool DupeUIExpanded = TransformUIExpanded[AtIndex];
TransformUIExpanded.Insert( DupeUIExpanded, AtIndex );
bStaticMeshChanged = true;
MarkChanged();
OnParamStateChanged();
}
}
FReply
UHoudiniAssetInput::OnButtonClickRecommit()
{
// There's no undo operation for button.
MarkChanged();
return FReply::Handled();
}
void
UHoudiniAssetInput::StartWorldOutlinerTicking()
{
if ( InputOutlinerMeshArray.Num() > 0 && !WorldOutlinerTimerDelegate.IsBound() && GEditor )
{
WorldOutlinerTimerDelegate = FTimerDelegate::CreateUObject( this, &UHoudiniAssetInput::TickWorldOutlinerInputs );
// We need to register delegate with the timer system.
static const float TickTimerDelay = 0.5f;
GEditor->GetTimerManager()->SetTimer( WorldOutlinerTimerHandle, WorldOutlinerTimerDelegate, TickTimerDelay, true );
}
}
void
UHoudiniAssetInput::StopWorldOutlinerTicking()
{
if ( InputOutlinerMeshArray.Num() <= 0 && WorldOutlinerTimerDelegate.IsBound() && GEditor )
{
GEditor->GetTimerManager()->ClearTimer( WorldOutlinerTimerHandle );
WorldOutlinerTimerDelegate.Unbind();
}
}
void UHoudiniAssetInput::InvalidateNodeIds()
{
ConnectedAssetId = -1;
for (auto& OutlinerInputMesh : InputOutlinerMeshArray)
{
OutlinerInputMesh.AssetId = -1;
}
}
void UHoudiniAssetInput::DuplicateCurves(UHoudiniAssetInput * OriginalInput)
{
if (!InputCurve || InputCurve->IsPendingKill() )
return;
if (!OriginalInput || OriginalInput->IsPendingKill())
return;
USceneComponent* RootComp = GetHoudiniAssetComponent();
if( !RootComp || RootComp->IsPendingKill() )
return;
if ( !RootComp->GetOwner() || RootComp->GetOwner()->IsPendingKill() )
return;
// The previous call to DuplicateObject did not duplicate the curves properly
// Both the original and duplicated Inputs now share the same InputCurve, so we
// need to create a proper copy of that curve
// Keep the original pointer to the curve, as we need to duplicate its data
UHoudiniSplineComponent* OriginalCurve = InputCurve;
// Creates a new Curve
InputCurve = NewObject< UHoudiniSplineComponent >(
RootComp->GetOwner(), UHoudiniSplineComponent::StaticClass(),
NAME_None, RF_Public | RF_Transactional);
// Attach curve component to asset.
InputCurve->AttachToComponent( RootComp, FAttachmentTransformRules::KeepRelativeTransform);
InputCurve->RegisterComponent();
InputCurve->SetVisibility(true);
// The new curve need do know that it is connected to this Input
InputCurve->SetHoudiniAssetInput(this);
// The call to DuplicateObject has actually modified the original object's Input
// so we need to fix that as well.
OriginalCurve->SetHoudiniAssetInput(OriginalInput);
// "Copy" the old curves parameters to the new one
InputCurve->CopyFrom(OriginalCurve);
// to force rebuild...
bSwitchedToCurve = true;
}
void
UHoudiniAssetInput::RemoveWorldOutlinerInput( int32 AtIndex )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini World Outliner Input Change" ),
PrimaryObject );
Modify();
bStaticMeshChanged = true;
InputOutlinerMeshArray.RemoveAt( AtIndex );
MarkChanged();
}
void
UHoudiniAssetInput::UpdateWorldOutlinerTransforms( FHoudiniAssetInputOutlinerMesh& OutlinerMesh )
{
// Update to the new Transforms
if ( OutlinerMesh.ActorPtr.IsValid() )
OutlinerMesh.ActorTransform = OutlinerMesh.ActorPtr->GetTransform();
if ( OutlinerMesh.StaticMeshComponent && !OutlinerMesh.StaticMeshComponent->IsPendingKill() )
{
OutlinerMesh.ComponentTransform = OutlinerMesh.StaticMeshComponent->GetComponentTransform();
// Handle instances here
UInstancedStaticMeshComponent * InstancedStaticMeshComponent = Cast< UInstancedStaticMeshComponent >(OutlinerMesh.StaticMeshComponent);
if (InstancedStaticMeshComponent)
{
FTransform InstanceTransform;
if (InstancedStaticMeshComponent->GetInstanceTransform(OutlinerMesh.InstanceIndex, InstanceTransform, true))
OutlinerMesh.ComponentTransform = InstanceTransform;
}
}
else if (OutlinerMesh.SplineComponent && !OutlinerMesh.SplineComponent->IsPendingKill() )
{
OutlinerMesh.ComponentTransform = OutlinerMesh.SplineComponent->GetComponentTransform();
}
OutlinerMesh.KeepWorldTransform = bKeepWorldTransform;
}
void
UHoudiniAssetInput::UpdateWorldOutlinerMaterials(FHoudiniAssetInputOutlinerMesh& OutlinerMesh)
{
OutlinerMesh.MeshComponentsMaterials.Empty();
if ( !OutlinerMesh.StaticMeshComponent || OutlinerMesh.StaticMeshComponent->IsPendingKill() )
return;
// Keep track of the materials used by the SMC
for ( int32 n = 0; n < OutlinerMesh.StaticMeshComponent->GetNumMaterials(); n++ )
{
UMaterialInterface* mi = OutlinerMesh.StaticMeshComponent->GetMaterial( n );
if ( !mi || mi->IsPendingKill() )
OutlinerMesh.MeshComponentsMaterials.Add( FString() );
else
OutlinerMesh.MeshComponentsMaterials.Add( mi->GetPathName() );
}
}
void UHoudiniAssetInput::OnAddToInputObjects()
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Geometry Change" ),
PrimaryObject );
Modify();
InputObjects.Add( nullptr );
InputTransforms.Add( FTransform::Identity );
TransformUIExpanded.Add( false );
MarkChanged();
bStaticMeshChanged = true;
OnParamStateChanged();
}
void UHoudiniAssetInput::OnEmptyInputObjects()
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Geometry Change" ),
PrimaryObject );
Modify();
// Empty the arrays
InputObjects.Empty();
InputTransforms.Empty();
TransformUIExpanded.Empty();
// To avoid displaying 0 elements when there's one (empty), initialize the array
InputObjects.Add( nullptr );
InputTransforms.Add( FTransform::Identity );
TransformUIExpanded.Add( false );
MarkChanged();
bStaticMeshChanged = true;
OnParamStateChanged();
}
void UHoudiniAssetInput::OnAddToSkeletonInputObjects()
{
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Input Geometry Change"),
PrimaryObject);
Modify();
SkeletonInputObjects.Add(nullptr);
//InputTransforms.Add(FTransform::Identity);
//TransformUIExpanded.Add(false);
MarkChanged();
bStaticMeshChanged = true;
OnParamStateChanged();
}
void UHoudiniAssetInput::OnEmptySkeletonInputObjects()
{
FScopedTransaction Transaction(
TEXT(HOUDINI_MODULE_RUNTIME),
LOCTEXT("HoudiniInputChange", "Houdini Input Geometry Change"),
PrimaryObject);
Modify();
// Empty the arrays
SkeletonInputObjects.Empty();
//InputTransforms.Empty();
//TransformUIExpanded.Empty();
// To avoid displaying 0 elements when there's one (empty), initialize the array
SkeletonInputObjects.Add(nullptr);
//InputTransforms.Add(FTransform::Identity);
//TransformUIExpanded.Add(false);
MarkChanged();
bStaticMeshChanged = true;
OnParamStateChanged();
}
TOptional< float >
UHoudiniAssetInput::GetSplineResolutionValue() const
{
if (UnrealSplineResolution != -1.0f)
return UnrealSplineResolution;
const UHoudiniRuntimeSettings * HoudiniRuntimeSettings = GetDefault< UHoudiniRuntimeSettings >();
if (HoudiniRuntimeSettings)
return HoudiniRuntimeSettings->MarshallingSplineResolution;
else
return HAPI_UNREAL_PARAM_SPLINE_RESOLUTION_DEFAULT;
}
void
UHoudiniAssetInput::SetSplineResolutionValue(float InValue)
{
if (InValue < 0)
OnResetSplineResolutionClicked();
else
UnrealSplineResolution = FMath::Clamp< float >(InValue, 0.0f, 10000.0f);
}
bool
UHoudiniAssetInput::IsSplineResolutionEnabled() const
{
if (ChoiceIndex != EHoudiniAssetInputType::WorldInput)
return false;
for (int32 n = 0; n < InputOutlinerMeshArray.Num(); n++)
{
if (InputOutlinerMeshArray[n].SplineComponent && !InputOutlinerMeshArray[n].SplineComponent->IsPendingKill() )
return true;
}
return false;
}
FReply
UHoudiniAssetInput::OnResetSplineResolutionClicked()
{
const UHoudiniRuntimeSettings * HoudiniRuntimeSettings = GetDefault< UHoudiniRuntimeSettings >();
if (HoudiniRuntimeSettings)
UnrealSplineResolution = HoudiniRuntimeSettings->MarshallingSplineResolution;
else
UnrealSplineResolution = HAPI_UNREAL_PARAM_SPLINE_RESOLUTION_DEFAULT;
return FReply::Handled();
}
FText
UHoudiniAssetInput::GetCurrentSelectionText() const
{
FText CurrentSelectionText;
if ( ChoiceIndex == EHoudiniAssetInputType::AssetInput )
{
if ( InputAssetComponent && !InputAssetComponent->IsPendingKill() && InputAssetComponent->GetHoudiniAssetActorOwner() )
{
CurrentSelectionText = FText::FromString( InputAssetComponent->GetHoudiniAssetActorOwner()->GetName() );
}
}
else if ( ChoiceIndex == EHoudiniAssetInputType::LandscapeInput )
{
if ( InputLandscapeProxy && !InputLandscapeProxy->IsPendingKill() )
{
CurrentSelectionText = FText::FromString( InputLandscapeProxy->GetName() );
}
}
return CurrentSelectionText;
}
#endif
FArchive &
operator<<( FArchive & Ar, FHoudiniAssetInputOutlinerMesh & HoudiniAssetInputOutlinerMesh )
{
HoudiniAssetInputOutlinerMesh.Serialize( Ar );
return Ar;
}
FBox
UHoudiniAssetInput::GetInputBounds( const FVector& ParentLocation ) const
{
FBox Bounds( ForceInitToZero );
if ( IsCurveAssetConnected() && InputCurve && !InputCurve->IsPendingKill() )
{
// Houdini Curves are expressed locally so we need to add the parent component's transform
TArray<FVector> CurvePositions;
InputCurve->GetCurvePositions( CurvePositions );
for ( int32 n = 0; n < CurvePositions.Num(); n++ )
Bounds += ( ParentLocation + CurvePositions[ n ] );
}
if ( IsWorldInputAssetConnected() )
{
for (int32 n = 0; n < InputOutlinerMeshArray.Num(); n++)
{
if ( !InputOutlinerMeshArray[ n ].ActorPtr.IsValid() )
continue;
FVector Origin, Extent;
InputOutlinerMeshArray[ n ].ActorPtr->GetActorBounds( false, Origin, Extent );
Bounds += FBox::BuildAABB( Origin, Extent );
}
}
if ( IsInputAssetConnected() && InputAssetComponent && !InputAssetComponent->IsPendingKill() )
{
Bounds += InputAssetComponent->GetAssetBounds();
}
if ( IsLandscapeAssetConnected() && InputLandscapeProxy && !InputLandscapeProxy->IsPendingKill() )
{
FVector Origin, Extent;
InputLandscapeProxy->GetActorBounds( false, Origin, Extent );
Bounds += FBox::BuildAABB( Origin, Extent );
}
return Bounds;
}
void UHoudiniAssetInput::SetDefaultInputTypeFromLabel()
{
#if WITH_EDITOR
// Look if we can find an input type prefix in the input name
FString inputName = GetParameterLabel();
// We'll try to find these magic words to try to detect the default input type
//FString geoPrefix = TEXT("geo");
FString curvePrefix = TEXT( "curve" );
FString landscapePrefix = TEXT( "landscape" );
FString landscapePrefix2 = TEXT( "terrain" );
FString landscapePrefix3 = TEXT( "heightfield" );
FString worldPrefix = TEXT( "world" );
FString worldPrefix2 = TEXT( "outliner" );
FString assetPrefix = TEXT( "asset" );
FString assetPrefix2 = TEXT( "hda" );
// By default, geometry input is chosen.
EHoudiniAssetInputType::Enum newInputType = EHoudiniAssetInputType::GeometryInput;
if ( inputName.Contains( curvePrefix, ESearchCase::IgnoreCase ) )
newInputType = EHoudiniAssetInputType::CurveInput;
else if ( ( inputName.Contains( landscapePrefix, ESearchCase::IgnoreCase ) )
|| ( inputName.Contains( landscapePrefix2, ESearchCase::IgnoreCase ) )
|| ( inputName.Contains( landscapePrefix3, ESearchCase::IgnoreCase ) ) )
newInputType = EHoudiniAssetInputType::LandscapeInput;
else if ( ( inputName.Contains( worldPrefix, ESearchCase::IgnoreCase ) )
|| ( inputName.Contains( worldPrefix2, ESearchCase::IgnoreCase ) ) )
newInputType = EHoudiniAssetInputType::WorldInput;
else if ( ( inputName.Contains( assetPrefix, ESearchCase::IgnoreCase ) )
|| ( inputName.Contains( assetPrefix2, ESearchCase::IgnoreCase ) ) )
newInputType = EHoudiniAssetInputType::AssetInput;
if ( ChoiceIndex != newInputType )
ChangeInputType( newInputType, false );
#else
ChoiceIndex = EHoudiniAssetInputType::GeometryInput;
#endif
}
bool
UHoudiniAssetInput::HasChanged() const
{
// Inputs should be considered changed after being loaded
return bChanged || bLoadedParameter || ( !bInputAssetConnectedInHoudini && ChoiceIndex == EHoudiniAssetInputType::AssetInput );
}
#if WITH_EDITOR
bool
UHoudiniAssetInput::UpdateInputOulinerArray()
{
bool NeedsUpdate = false;
// See if some outliner inputs need to be updated, or removed
// If an input's Actor is no longer valid, then when need to remove that input.
// If an input's Components needs to be updated (are no longer valid), then all the components
// from the same actor needs to be updated as well.
// This can happen for example when a blueprint actor is modified/serialized etc..
TArray<AActor *> ActorToUpdateArray;
for ( int32 n = InputOutlinerMeshArray.Num() - 1; n >= 0; n-- )
{
FHoudiniAssetInputOutlinerMesh& OutlinerInput = InputOutlinerMeshArray[ n ];
if (OutlinerInput.ActorPtr.IsStale()) // || !OutlinerInput.ActorPtr.IsValid());
{
// If our ActorPtr is stale, try to find an updated version of it by pathname
// This can happen when a blueprint is updated or recompiled...
OutlinerInput.TryToUpdateActorPtrFromActorPathName();
}
if ( !OutlinerInput.ActorPtr.IsValid() )
{
// This input has an invalid actor: destroy it and its asset
NeedsUpdate = true;
// Destroy Houdini asset
if ( FHoudiniEngineUtils::IsValidNodeId( OutlinerInput.AssetId ) )
{
FHoudiniEngineUtils::DestroyHoudiniAsset( OutlinerInput.AssetId );
OutlinerInput.AssetId = -1;
}
// Remove that input
InputOutlinerMeshArray.RemoveAt( n );
}
else
{
// This input has a valid actor, see if its component needs to be updated
if ( !OutlinerInput.NeedsComponentUpdate() )
continue;
if ( ActorToUpdateArray.Contains( OutlinerInput.ActorPtr.Get() ) )
continue;
ActorToUpdateArray.Add( OutlinerInput.ActorPtr.Get() );
NeedsUpdate = true;
}
}
// Creates the inputs from the actors
for ( auto & CurrentActor : ActorToUpdateArray )
UpdateInputOulinerArrayFromActor( CurrentActor, true );
return NeedsUpdate;
}
void
UHoudiniAssetInput::UpdateInputOulinerArrayFromActor( AActor * Actor, const bool& NeedCleanUp )
{
if ( !Actor )
return;
// Don't allow selection of ourselves. Bad things happen if we do.
if ( GetHoudiniAssetComponent() && ( Actor == GetHoudiniAssetComponent()->GetOwner() ) )
return;
// Destroy previous outliner inputs linked to this actor if needed
if (NeedCleanUp)
{
for (int32 n = InputOutlinerMeshArray.Num() - 1; n >= 0; n--)
{
if (InputOutlinerMeshArray[n].ActorPtr.Get() != Actor)
continue;
// Remove from the input array.
// No need to destroy the houdini nodes since this will be taken care of
// when we update the input. (see bug 95415)
InputOutlinerMeshArray.RemoveAt(n);
}
}
// Looking for StaticMeshes
TArray< UStaticMeshComponent* > AllSMComponents;
Actor->GetComponents<UStaticMeshComponent>(AllSMComponents);
for (UStaticMeshComponent * StaticMeshComponent : AllSMComponents)
{
if ( !StaticMeshComponent || StaticMeshComponent->ComponentHasTag( NAME_HoudiniNoUpload ) )
continue;
UStaticMesh * StaticMesh = StaticMeshComponent->GetStaticMesh();
if ( !StaticMesh )
continue;
UInstancedStaticMeshComponent * InstancedStaticMeshComponent = Cast< UInstancedStaticMeshComponent >(StaticMeshComponent);
if (InstancedStaticMeshComponent)
{
// Handle ISM separately
// We'll create an Outliner Mesh for each of the instances..
for (int32 n = 0; n < InstancedStaticMeshComponent->GetInstanceCount(); n++)
{
// Add the mesh to the array
FHoudiniAssetInputOutlinerMesh OutlinerMesh;
OutlinerMesh.ActorPtr = Actor;
OutlinerMesh.ActorPathName = Actor->GetPathName();
OutlinerMesh.StaticMeshComponent = InstancedStaticMeshComponent;
OutlinerMesh.StaticMesh = StaticMesh;
OutlinerMesh.SplineComponent = nullptr;
OutlinerMesh.AssetId = -1;
OutlinerMesh.InstanceIndex = n;
UpdateWorldOutlinerTransforms(OutlinerMesh);
UpdateWorldOutlinerMaterials(OutlinerMesh);
InputOutlinerMeshArray.Add(OutlinerMesh);
}
}
else
{
// Add the Static Mesh to the array
FHoudiniAssetInputOutlinerMesh OutlinerMesh;
OutlinerMesh.ActorPtr = Actor;
OutlinerMesh.ActorPathName = Actor->GetPathName();
OutlinerMesh.StaticMeshComponent = StaticMeshComponent;
OutlinerMesh.StaticMesh = StaticMesh;
OutlinerMesh.SplineComponent = nullptr;
OutlinerMesh.AssetId = -1;
UpdateWorldOutlinerTransforms(OutlinerMesh);
UpdateWorldOutlinerMaterials(OutlinerMesh);
InputOutlinerMeshArray.Add(OutlinerMesh);
}
}
// Looking for Splines
TArray< USplineComponent* > AllSplineComponents;
Actor->GetComponents<USplineComponent>(AllSplineComponents);
for (USplineComponent * SplineComponent : AllSplineComponents)
{
if ( !SplineComponent || SplineComponent->ComponentHasTag( NAME_HoudiniNoUpload ) )
continue;
// Add the spline to the array
FHoudiniAssetInputOutlinerMesh OutlinerMesh;
OutlinerMesh.ActorPtr = Actor;
OutlinerMesh.ActorPathName = Actor->GetPathName();
OutlinerMesh.StaticMeshComponent = nullptr;
OutlinerMesh.StaticMesh = nullptr;
OutlinerMesh.SplineComponent = SplineComponent;
OutlinerMesh.AssetId = -1;
UpdateWorldOutlinerTransforms( OutlinerMesh );
// Updating the OutlinerMesh's struct infos
OutlinerMesh.SplineResolution = UnrealSplineResolution;
OutlinerMesh.SplineLength = SplineComponent->GetSplineLength();
OutlinerMesh.NumberOfSplineControlPoints = SplineComponent->GetNumberOfSplinePoints();
InputOutlinerMeshArray.Add( OutlinerMesh );
}
}
FReply
UHoudiniAssetInput::OnExpandInputTransform( int32 AtIndex )
{
if ( TransformUIExpanded.IsValidIndex( AtIndex ) )
{
TransformUIExpanded[ AtIndex ] = !TransformUIExpanded[ AtIndex ];
OnParamStateChanged();
}
return FReply::Handled();
}
TOptional< float >
UHoudiniAssetInput::GetPositionX( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.GetLocation().X;
}
TOptional< float >
UHoudiniAssetInput::GetPositionY( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.GetLocation().Y;
}
TOptional< float >
UHoudiniAssetInput::GetPositionZ( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.GetLocation().Z;
}
void
UHoudiniAssetInput::SetPositionX( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FVector Position = InputTransforms[ AtIndex ].GetLocation();
if ( Position.X == Value )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Position.X = Value;
InputTransforms[ AtIndex ].SetLocation( Position );
MarkChanged( true );
bStaticMeshChanged = true;
}
void
UHoudiniAssetInput::SetPositionY( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FVector Position = InputTransforms[ AtIndex ].GetLocation();
if ( Position.Y == Value )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Position.Y = Value;
InputTransforms[ AtIndex ].SetLocation( Position );
MarkChanged( true );
bStaticMeshChanged = true;
}
void
UHoudiniAssetInput::SetPositionZ( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FVector Position = InputTransforms[ AtIndex ].GetLocation();
if ( Position.Z == Value )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Position.Z = Value;
InputTransforms[ AtIndex ].SetLocation( Position );
MarkChanged( true );
bStaticMeshChanged = true;
}
TOptional< float >
UHoudiniAssetInput::GetRotationRoll( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.Rotator().Roll;
}
TOptional< float >
UHoudiniAssetInput::GetRotationPitch( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.Rotator().Pitch;
}
TOptional< float >
UHoudiniAssetInput::GetRotationYaw( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.Rotator().Yaw;
}
void
UHoudiniAssetInput::SetRotationRoll( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FRotator Rotator = InputTransforms[ AtIndex ].Rotator();
if ( FMath::IsNearlyEqual( Rotator.Roll, Value, SMALL_NUMBER ) )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Rotator.Roll = Value;
InputTransforms[ AtIndex ].SetRotation( Rotator.Quaternion() );
MarkChanged( true );
bStaticMeshChanged = true;
}
void
UHoudiniAssetInput::SetRotationPitch( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FRotator Rotator = InputTransforms[ AtIndex ].Rotator();
if ( FMath::IsNearlyEqual( Rotator.Pitch, Value, SMALL_NUMBER ) )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this);
Modify();
Rotator.Pitch = Value;
InputTransforms[ AtIndex ].SetRotation( Rotator.Quaternion() );
MarkChanged( true );
bStaticMeshChanged = true;
}
void
UHoudiniAssetInput::SetRotationYaw( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FRotator Rotator = InputTransforms[ AtIndex ].Rotator();
if ( FMath::IsNearlyEqual( Rotator.Yaw, Value, SMALL_NUMBER ) )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Rotator.Yaw = Value;
InputTransforms[ AtIndex ].SetRotation( Rotator.Quaternion() );
MarkChanged( true );
bStaticMeshChanged = true;
}
/** Returns the input's transform scale values **/
TOptional< float >
UHoudiniAssetInput::GetScaleX( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.GetScale3D().X;
}
TOptional< float >
UHoudiniAssetInput::GetScaleY( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.GetScale3D().Y;
}
TOptional< float >
UHoudiniAssetInput::GetScaleZ( int32 AtIndex ) const
{
FTransform transform = FTransform::Identity;
if ( InputTransforms.IsValidIndex( AtIndex ) )
transform = InputTransforms[ AtIndex ];
return transform.GetScale3D().Z;
}
void
UHoudiniAssetInput::SetScaleX( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FVector Scale = InputTransforms[ AtIndex ].GetScale3D();
if ( Scale.X == Value )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Scale.X = Value;
InputTransforms[ AtIndex ].SetScale3D( Scale );
MarkChanged( true );
bStaticMeshChanged = true;
}
void
UHoudiniAssetInput::SetScaleY( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FVector Scale = InputTransforms[ AtIndex ].GetScale3D();
if ( Scale.Y == Value )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Scale.Y = Value;
InputTransforms[ AtIndex ].SetScale3D( Scale );
MarkChanged( true );
bStaticMeshChanged = true;
}
void
UHoudiniAssetInput::SetScaleZ( float Value, int32 AtIndex )
{
if ( !InputTransforms.IsValidIndex( AtIndex ) )
return;
FVector Scale = InputTransforms[ AtIndex ].GetScale3D();
if ( Scale.Z == Value )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInputChange", "Houdini Input Change" ),
this );
Modify();
Scale.Z = Value;
InputTransforms[ AtIndex ].SetScale3D( Scale );
MarkChanged( true );
bStaticMeshChanged = true;
}
bool
UHoudiniAssetInput::AddInputObject( UObject* ObjectToAdd )
{
if ( !ObjectToAdd || ObjectToAdd->IsPendingKill() )
return false;
// Fix for the bug due to the first (but null) geometry input mesh
int32 IndexToAdd = InputObjects.Num();
if ( IndexToAdd == 1 && ( InputObjects[ 0 ] == nullptr ) )
IndexToAdd = 0;
if ( UStaticMesh* StaticMesh = Cast< UStaticMesh >( ObjectToAdd ) )
{
ForceSetInputObject( ObjectToAdd, IndexToAdd, true );
return true;
}
else if ( AActor* WorldActor = Cast< AActor >( ObjectToAdd ) )
{
ForceSetInputObject( ObjectToAdd, IndexToAdd, true );
return true;
}
//if ( InputAssetComponent )
// InputAssetComponent->bEditorPropertiesNeedFullUpdate = true;
return false;
}
#endif
ALandscapeProxy*
UHoudiniAssetInput::GetLandscapeInput()
{
if (!InputLandscapeProxy || InputLandscapeProxy->IsPendingKill())
return nullptr;
return InputLandscapeProxy->GetLandscapeActor();
}
bool
UHoudiniAssetInput::HasLODs() const
{
switch ( ChoiceIndex )
{
case EHoudiniAssetInputType::GeometryInput:
{
if ( !InputObjects.Num() )
return false;
for ( int32 Idx = 0; Idx < InputObjects.Num(); Idx++ )
{
UStaticMesh* SM = Cast<UStaticMesh>( InputObjects[ Idx ] );
if ( !SM || SM->IsPendingKill() )
continue;
if ( SM->GetNumLODs() > 1 )
return true;
}
}
break;
case EHoudiniAssetInputType::WorldInput:
{
if ( !InputOutlinerMeshArray.Num() )
return false;
for ( int32 Idx = 0; Idx < InputOutlinerMeshArray.Num(); Idx++ )
{
UStaticMesh* SM = InputOutlinerMeshArray[ Idx ].StaticMesh;
if ( !SM || SM->IsPendingKill() )
continue;
if ( SM->GetNumLODs() > 1 )
return true;
}
}
break;
}
return false;
}
bool
UHoudiniAssetInput::HasSockets() const
{
switch ( ChoiceIndex )
{
case EHoudiniAssetInputType::GeometryInput:
{
if ( !InputObjects.Num() )
return false;
for ( int32 Idx = 0; Idx < InputObjects.Num(); Idx++ )
{
UStaticMesh* SM = Cast<UStaticMesh>( InputObjects[ Idx ] );
if ( !SM )
continue;
if ( SM->Sockets.Num() > 0 )
return true;
}
}
break;
case EHoudiniAssetInputType::WorldInput:
{
if ( !InputOutlinerMeshArray.Num() )
return false;
for ( int32 Idx = 0; Idx < InputOutlinerMeshArray.Num(); Idx++ )
{
UStaticMesh* SM = InputOutlinerMeshArray[ Idx ].StaticMesh;
if ( !SM )
continue;
if ( SM->Sockets.Num() > 1 )
return true;
}
}
break;
}
return false;
}
bool
UHoudiniAssetInput::IsLandscapeUpdateNeededOnTransformChange() const
{
if ( GetChoiceIndex() != EHoudiniAssetInputType::LandscapeInput )
return false;
if ( !bLandscapeAutoSelectComponent || !bLandscapeExportSelectionOnly )
return false;
return true;
}
bool
UHoudiniAssetInput::SetDefaultAssetFromHDA()
{
#if WITH_EDITOR
// We just handle geo inputs
if (EHoudiniAssetInputType::GeometryInput != ChoiceIndex)
return false;
// There is a default slot, don't add if slot is already filled
if (InputObjects.Num() > 1)
return false;
// Make sure we're linked to a valid parameter
if (ParmId < 0)
return false;
// Get our ParmInfo
HAPI_ParmInfo FoundParamInfo;
FHoudiniApi::ParmInfo_Init(&FoundParamInfo);
if ( HAPI_RESULT_SUCCESS != FHoudiniApi::GetParmInfo(
FHoudiniEngine::Get().GetSession(),
NodeId, ParmId, &FoundParamInfo) )
{
return false;
}
// Get our string value
HAPI_StringHandle StringHandle;
if (FHoudiniApi::GetParmStringValues(
FHoudiniEngine::Get().GetSession(), NodeId, false,
&StringHandle, FoundParamInfo.stringValuesIndex, 1) == HAPI_RESULT_SUCCESS)
{
FString OutValue;
FHoudiniEngineString HoudiniEngineString(StringHandle);
if (HoudiniEngineString.ToFString(OutValue))
{
// Set default object on the HDA instance - will override the parameter string
// and apply the object input local-path thing for the HDA cook.
if (OutValue.Len() > 0)
{
UObject * pObject = LoadObject<UObject>(nullptr, *OutValue);
if (pObject)
{
return AddInputObject(pObject);
}
}
}
}
#endif //WITH EDITOR
return false;
}
#undef LOCTEXT_NAMESPACE