Files

1408 lines
57 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 "HoudiniAssetInstanceInput.h"
#include "HoudiniApi.h"
#include "HoudiniEngineRuntimePrivatePCH.h"
#include "HoudiniEngineUtils.h"
#include "HoudiniAssetComponent.h"
#include "HoudiniAssetInstanceInputField.h"
#include "HoudiniEngine.h"
#include "HoudiniEngineString.h"
#include "HoudiniInstancedActorComponent.h"
#include "HoudiniMeshSplitInstancerComponent.h"
#include "Components/AudioComponent.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Components/HierarchicalInstancedStaticMeshComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Sound/SoundBase.h"
#include "Internationalization/Internationalization.h"
#include "HoudiniEngineBakeUtils.h"
#define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE
UHoudiniAssetInstanceInput::UHoudiniAssetInstanceInput( const FObjectInitializer& ObjectInitializer )
: Super( ObjectInitializer )
, ObjectToInstanceId( -1 )
{
Flags.HoudiniAssetInstanceInputFlagsPacked = 0;
TupleSize = 0;
}
UHoudiniAssetInstanceInput *
UHoudiniAssetInstanceInput::Create(
UHoudiniAssetComponent * InPrimaryObject,
const FHoudiniGeoPartObject & InHoudiniGeoPartObject )
{
if (!InPrimaryObject || InPrimaryObject->IsPendingKill())
return nullptr;
UHoudiniAssetInstanceInput * NewInstanceInput = nullptr;
// Get object to be instanced.
HAPI_NodeId ObjectToInstance = InHoudiniGeoPartObject.HapiObjectGetToInstanceId();
auto Flags = GetInstancerFlags(InHoudiniGeoPartObject);
// This is invalid combination, no object to instance and input is not an attribute instancer.
if ( !Flags.bIsAttributeInstancer && !Flags.bAttributeInstancerOverride && ObjectToInstance == -1 && !Flags.bIsPackedPrimitiveInstancer )
return nullptr;
NewInstanceInput = NewObject< UHoudiniAssetInstanceInput >(
InPrimaryObject,
UHoudiniAssetInstanceInput::StaticClass(),
NAME_None, RF_Public | RF_Transactional );
if ( !NewInstanceInput || NewInstanceInput->IsPendingKill() )
return nullptr;
NewInstanceInput->PrimaryObject = InPrimaryObject;
NewInstanceInput->HoudiniGeoPartObject = InHoudiniGeoPartObject;
NewInstanceInput->SetNameAndLabel( InHoudiniGeoPartObject.ObjectName );
NewInstanceInput->ObjectToInstanceId = ObjectToInstance;
NewInstanceInput->Flags = Flags;
return NewInstanceInput;
}
UHoudiniAssetInstanceInput::FHoudiniAssetInstanceInputFlags
UHoudiniAssetInstanceInput::GetInstancerFlags(const FHoudiniGeoPartObject & InHoudiniGeoPartObject)
{
FHoudiniAssetInstanceInputFlags Flags;
Flags.HoudiniAssetInstanceInputFlagsPacked = 0;
// Get object to be instanced.
HAPI_NodeId ObjectToInstance = InHoudiniGeoPartObject.HapiObjectGetToInstanceId();
Flags.bIsPackedPrimitiveInstancer = InHoudiniGeoPartObject.IsPackedPrimitiveInstancer();
// If this is an attribute instancer, see if attribute exists.
Flags.bIsAttributeInstancer = InHoudiniGeoPartObject.IsAttributeInstancer();
// Check if this is an attribute override instancer (on detail or point).
Flags.bAttributeInstancerOverride = InHoudiniGeoPartObject.IsAttributeOverrideInstancer();
// Check if this is a No-Instancers ( unreal_split_instances )
Flags.bIsSplitMeshInstancer =
InHoudiniGeoPartObject.HapiCheckAttributeExistance(
HAPI_UNREAL_ATTRIB_SPLIT_INSTANCES, HAPI_ATTROWNER_DETAIL );
return Flags;
}
UHoudiniAssetInstanceInput *
UHoudiniAssetInstanceInput::Create(
UHoudiniAssetComponent * InPrimaryObject,
const UHoudiniAssetInstanceInput * OtherInstanceInput )
{
if (!InPrimaryObject || InPrimaryObject->IsPendingKill() )
return nullptr;
UHoudiniAssetInstanceInput * HoudiniAssetInstanceInput =
DuplicateObject( OtherInstanceInput, InPrimaryObject );
if (!HoudiniAssetInstanceInput || HoudiniAssetInstanceInput->IsPendingKill())
return nullptr;
// We need to duplicate field objects manually
HoudiniAssetInstanceInput->InstanceInputFields.Empty();
for ( const auto& OtherField : OtherInstanceInput->InstanceInputFields )
{
UHoudiniAssetInstanceInputField * NewField =
UHoudiniAssetInstanceInputField::Create( InPrimaryObject, OtherField );
if (!NewField || NewField->IsPendingKill())
continue;
HoudiniAssetInstanceInput->InstanceInputFields.Add( NewField );
}
// Fix the back-reference to the component
HoudiniAssetInstanceInput->PrimaryObject = InPrimaryObject;
return HoudiniAssetInstanceInput;
}
bool
UHoudiniAssetInstanceInput::CreateInstanceInput()
{
if ( !PrimaryObject || PrimaryObject->IsPendingKill() )
return false;
HAPI_NodeId AssetId = GetAssetId();
// Retrieve instance transforms (for each point).
TArray< FTransform > AllTransforms;
HoudiniGeoPartObject.HapiGetInstanceTransforms( AssetId, AllTransforms );
// List of new fields. Reused input fields will also be placed here.
TArray< UHoudiniAssetInstanceInputField * > NewInstanceInputFields;
if ( Flags.bIsPackedPrimitiveInstancer )
{
// This is using packed primitives
HAPI_PartInfo PartInfo;
FHoudiniApi::PartInfo_Init(&PartInfo);
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetPartInfo(
FHoudiniEngine::Get().GetSession(), HoudiniGeoPartObject.GeoId, HoudiniGeoPartObject.PartId,
&PartInfo ), false );
// Retrieve part name.
//FString PartName;
//FHoudiniEngineString HoudiniEngineStringPartName( PartInfo.nameSH );
//HoudiniEngineStringPartName.ToFString( PartName );
//HOUDINI_LOG_MESSAGE( TEXT( "Part Instancer (%s): IPC=%d, IC=%d" ), *PartName, PartInfo.instancedPartCount, PartInfo.instanceCount );
// Get transforms for each instance
TArray<HAPI_Transform> InstancerPartTransforms;
InstancerPartTransforms.SetNumUninitialized( PartInfo.instanceCount );
for (int32 Idx = 0; Idx < InstancerPartTransforms.Num(); Idx++)
FHoudiniApi::Transform_Init(&(InstancerPartTransforms[Idx]));
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetInstancerPartTransforms(
FHoudiniEngine::Get().GetSession(), HoudiniGeoPartObject.GeoId, PartInfo.id,
HAPI_RSTORDER_DEFAULT, InstancerPartTransforms.GetData(), 0, PartInfo.instanceCount ), false );
// Get the part ids for parts being instanced
TArray<HAPI_PartId> InstancedPartIds;
InstancedPartIds.SetNumZeroed( PartInfo.instancedPartCount );
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetInstancedPartIds(
FHoudiniEngine::Get().GetSession(), HoudiniGeoPartObject.GeoId, PartInfo.id,
InstancedPartIds.GetData(), 0, PartInfo.instancedPartCount ), false );
for ( auto InstancedPartId : InstancedPartIds )
{
HAPI_PartInfo InstancedPartInfo;
HOUDINI_CHECK_ERROR_RETURN(
FHoudiniApi::GetPartInfo(
FHoudiniEngine::Get().GetSession(), HoudiniGeoPartObject.GeoId, InstancedPartId,
&InstancedPartInfo ), false );
TArray<FTransform> ObjectTransforms;
ObjectTransforms.SetNumUninitialized( InstancerPartTransforms.Num() );
for ( int32 InstanceIdx = 0; InstanceIdx < InstancerPartTransforms.Num(); ++InstanceIdx )
{
const auto& InstanceTransform = InstancerPartTransforms[InstanceIdx];
FHoudiniEngineUtils::TranslateHapiTransform( InstanceTransform, ObjectTransforms[InstanceIdx] );
//HOUDINI_LOG_MESSAGE( TEXT( "Instance %d:%d Transform: %s for Part Id %d" ), HoudiniGeoPartObject.PartId, InstanceIdx, *ObjectTransforms[ InstanceIdx ].ToString(), InstancedPartId );
}
// Create this instanced input field for this instanced part
//
FHoudiniGeoPartObject InstancedPart( HoudiniGeoPartObject.AssetId, HoudiniGeoPartObject.ObjectId, HoudiniGeoPartObject.GeoId, InstancedPartId );
InstancedPart.TransformMatrix = HoudiniGeoPartObject.TransformMatrix;
InstancedPart.UpdateCustomName();
CreateInstanceInputField( InstancedPart, ObjectTransforms, InstanceInputFields, NewInstanceInputFields );
}
}
else if ( Flags.bIsAttributeInstancer )
{
int32 NumPoints = HoudiniGeoPartObject.HapiPartGetPointCount();
TArray< HAPI_NodeId > InstancedObjectIds;
InstancedObjectIds.SetNumUninitialized( NumPoints );
HOUDINI_CHECK_ERROR_RETURN( FHoudiniApi::GetInstancedObjectIds(
FHoudiniEngine::Get().GetSession(), HoudiniGeoPartObject.GeoId,
InstancedObjectIds.GetData(),
0, NumPoints), false );
// Find the set of instanced object ids and locate the corresponding parts
TSet< int32 > UniqueInstancedObjectIds( InstancedObjectIds );
TArray< FTransform > InstanceTransforms;
for ( int32 InstancedObjectId : UniqueInstancedObjectIds )
{
UHoudiniAssetComponent* Comp = GetHoudiniAssetComponent();
if( Comp && !Comp->IsPendingKill() )
{
TArray< FHoudiniGeoPartObject > PartsToInstance;
if( Comp->LocateStaticMeshes( InstancedObjectId, PartsToInstance ) )
{
// copy out the transforms for this instance id
InstanceTransforms.Empty();
for( int32 Ix = 0; Ix < InstancedObjectIds.Num(); ++Ix )
{
if( ( InstancedObjectIds[ Ix ] == InstancedObjectId ) && ( AllTransforms.IsValidIndex( Ix ) ) )
{
InstanceTransforms.Add( AllTransforms[ Ix ] );
}
}
// Locate or create an instance input field for each part for this instanced object id
for( FHoudiniGeoPartObject& Part : PartsToInstance )
{
// Change the transform of the part being instanced to match the instancer
Part.TransformMatrix = HoudiniGeoPartObject.TransformMatrix;
Part.UpdateCustomName();
CreateInstanceInputField( Part, InstanceTransforms, InstanceInputFields, NewInstanceInputFields );
}
}
}
}
}
else if ( Flags.bAttributeInstancerOverride )
{
// This is an attribute override. Unreal mesh is specified through an attribute and we use points.
std::string MarshallingAttributeInstanceOverride = HAPI_UNREAL_ATTRIB_INSTANCE_OVERRIDE;
UHoudiniRuntimeSettings::GetSettingsValue(
TEXT( "MarshallingAttributeInstanceOverride" ),
MarshallingAttributeInstanceOverride );
HAPI_AttributeInfo ResultAttributeInfo;
FHoudiniApi::AttributeInfo_Init(&ResultAttributeInfo);
//FMemory::Memzero< HAPI_AttributeInfo >( ResultAttributeInfo );
if ( !HoudiniGeoPartObject.HapiGetAttributeInfo(
AssetId, MarshallingAttributeInstanceOverride, ResultAttributeInfo ) )
{
// We had an error while retrieving the attribute info.
return false;
}
if ( !ResultAttributeInfo.exists )
{
// Attribute does not exist.
return false;
}
if ( ResultAttributeInfo.owner == HAPI_ATTROWNER_DETAIL )
{
// Attribute is on detail, this means it gets applied to all points.
TArray< FString > DetailInstanceValues;
if ( !HoudiniGeoPartObject.HapiGetAttributeDataAsString(
AssetId, MarshallingAttributeInstanceOverride,
HAPI_ATTROWNER_DETAIL, ResultAttributeInfo, DetailInstanceValues ) )
{
// This should not happen - attribute exists, but there was an error retrieving it.
return false;
}
if ( DetailInstanceValues.Num() <= 0 )
{
// No values specified.
return false;
}
// Attempt to load specified asset.
const FString & AssetName = DetailInstanceValues[ 0 ];
UObject * AttributeObject =
StaticLoadObject(
UObject::StaticClass(), nullptr, *AssetName, nullptr, LOAD_None, nullptr );
if ( AttributeObject && !AttributeObject->IsPendingKill() )
{
CreateInstanceInputField(
AttributeObject, AllTransforms, InstanceInputFields, NewInstanceInputFields );
}
else
{
return false;
}
}
else if ( ResultAttributeInfo.owner == HAPI_ATTROWNER_POINT )
{
TArray< FString > PointInstanceValues;
if ( !HoudiniGeoPartObject.HapiGetAttributeDataAsString(
AssetId, MarshallingAttributeInstanceOverride,
HAPI_ATTROWNER_POINT, ResultAttributeInfo, PointInstanceValues ) )
{
// This should not happen - attribute exists, but there was an error retrieving it.
return false;
}
// Attribute is on points, number of points must match number of transforms.
if ( PointInstanceValues.Num() != AllTransforms.Num() )
{
// This should not happen, we have mismatch between number of instance values and transforms.
return false;
}
// If instance attribute exists on points, we need to get all unique values.
TMap< FString, UObject * > ObjectsToInstance;
for ( auto Iter : PointInstanceValues )
{
const FString & UniqueName = *Iter;
if ( !ObjectsToInstance.Contains( UniqueName ) )
{
UObject * AttributeObject = StaticLoadObject(
UObject::StaticClass(), nullptr, *UniqueName, nullptr, LOAD_None, nullptr );
if ( AttributeObject && !AttributeObject->IsPendingKill() )
ObjectsToInstance.Add( UniqueName, AttributeObject );
}
}
bool Success = false;
for( auto Iter : ObjectsToInstance )
{
const FString & InstancePath = Iter.Key;
UObject * AttributeObject = Iter.Value;
if ( AttributeObject && !AttributeObject->IsPendingKill() )
{
TArray< FTransform > ObjectTransforms;
GetPathInstaceTransforms( InstancePath, PointInstanceValues, AllTransforms, ObjectTransforms );
CreateInstanceInputField( AttributeObject, ObjectTransforms, InstanceInputFields, NewInstanceInputFields );
Success = true;
}
}
if ( !Success )
return false;
}
else
{
// We don't support this attribute on other owners.
return false;
}
}
else
{
// This is a standard object type instancer.
// Locate all geo objects requiring instancing (can be multiple if geo / part / object split took place).
TArray< FHoudiniGeoPartObject > ObjectsToInstance;
if( UHoudiniAssetComponent* Comp = GetHoudiniAssetComponent() )
{
if ( !Comp->IsPendingKill() )
Comp->LocateStaticMeshes( ObjectToInstanceId, ObjectsToInstance );
}
// Process each existing detected object that needs to be instanced.
for ( int32 GeoIdx = 0; GeoIdx < ObjectsToInstance.Num(); ++GeoIdx )
{
FHoudiniGeoPartObject & ItemHoudiniGeoPartObject = ObjectsToInstance[ GeoIdx ];
// Change the transform of the part being instanced to match the instancer
ItemHoudiniGeoPartObject.TransformMatrix = HoudiniGeoPartObject.TransformMatrix;
ItemHoudiniGeoPartObject.UpdateCustomName();
// Locate or create an input field.
CreateInstanceInputField(
ItemHoudiniGeoPartObject, AllTransforms, InstanceInputFields, NewInstanceInputFields );
}
}
// Sort and store new fields.
NewInstanceInputFields.Sort( FHoudiniAssetInstanceInputFieldSortPredicate() );
CleanInstanceInputFields( InstanceInputFields );
InstanceInputFields = NewInstanceInputFields;
return true;
}
UHoudiniAssetInstanceInputField *
UHoudiniAssetInstanceInput::LocateInputField(
const FHoudiniGeoPartObject & GeoPartObject)
{
UHoudiniAssetInstanceInputField * FoundHoudiniAssetInstanceInputField = nullptr;
for ( int32 FieldIdx = 0; FieldIdx < InstanceInputFields.Num(); ++FieldIdx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ FieldIdx ];
if (!HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill())
continue;
if ( HoudiniAssetInstanceInputField->GetHoudiniGeoPartObject().GetNodePath() == GeoPartObject.GetNodePath() )
{
FoundHoudiniAssetInstanceInputField = HoudiniAssetInstanceInputField;
break;
}
}
return FoundHoudiniAssetInstanceInputField;
}
void
UHoudiniAssetInstanceInput::CleanInstanceInputFields( TArray< UHoudiniAssetInstanceInputField * > & InInstanceInputFields )
{
UHoudiniAssetInstanceInputField * FoundHoudiniAssetInstanceInputField = nullptr;
for ( int32 FieldIdx = 0; FieldIdx < InstanceInputFields.Num(); ++FieldIdx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ FieldIdx ];
if ( HoudiniAssetInstanceInputField && !HoudiniAssetInstanceInputField->IsPendingKill() )
HoudiniAssetInstanceInputField->ConditionalBeginDestroy();
}
InInstanceInputFields.Empty();
}
UHoudiniAssetComponent*
UHoudiniAssetInstanceInput::GetHoudiniAssetComponent()
{
return Cast<UHoudiniAssetComponent>( PrimaryObject );
}
const UHoudiniAssetComponent*
UHoudiniAssetInstanceInput::GetHoudiniAssetComponent() const
{
return Cast<const UHoudiniAssetComponent>( PrimaryObject );
}
HAPI_NodeId
UHoudiniAssetInstanceInput::GetAssetId() const
{
if( const UHoudiniAssetComponent* Comp = GetHoudiniAssetComponent() )
return Comp->GetAssetId();
return -1;
}
void
UHoudiniAssetInstanceInput::CreateInstanceInputField(
const FHoudiniGeoPartObject & InHoudiniGeoPartObject,
const TArray< FTransform > & ObjectTransforms,
const TArray< UHoudiniAssetInstanceInputField * > & OldInstanceInputFields,
TArray<UHoudiniAssetInstanceInputField * > & NewInstanceInputFields)
{
UHoudiniAssetComponent* Comp = GetHoudiniAssetComponent();
UStaticMesh * StaticMesh = nullptr;
if ( Comp && !Comp->IsPendingKill() )
StaticMesh = Comp->LocateStaticMesh(InHoudiniGeoPartObject, false);
// Locate static mesh for this geo part.
if ( StaticMesh && !StaticMesh->IsPendingKill() )
{
// Locate corresponding input field.
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField =
LocateInputField( InHoudiniGeoPartObject );
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
{
// Input field does not exist, we need to create it.
HoudiniAssetInstanceInputField = UHoudiniAssetInstanceInputField::Create(
PrimaryObject, this, InHoudiniGeoPartObject );
if (HoudiniAssetInstanceInputField && !HoudiniAssetInstanceInputField->IsPendingKill())
{
// Assign original and static mesh.
HoudiniAssetInstanceInputField->OriginalObject = StaticMesh;
HoudiniAssetInstanceInputField->AddInstanceVariation(StaticMesh, 0);
}
}
else
{
// refresh the geo part
HoudiniAssetInstanceInputField->SetGeoPartObject( InHoudiniGeoPartObject );
// Remove item from old list.
InstanceInputFields.RemoveSingleSwap( HoudiniAssetInstanceInputField, false );
TArray< int > MatchingIndices;
HoudiniAssetInstanceInputField->FindObjectIndices(
HoudiniAssetInstanceInputField->GetOriginalObject(),
MatchingIndices );
for ( int Idx = 0; Idx < MatchingIndices.Num(); Idx++ )
{
int ReplacementIndex = MatchingIndices[ Idx ];
HoudiniAssetInstanceInputField->ReplaceInstanceVariation( StaticMesh, ReplacementIndex );
}
HoudiniAssetInstanceInputField->OriginalObject = StaticMesh;
}
if ( HoudiniAssetInstanceInputField && !HoudiniAssetInstanceInputField->IsPendingKill() )
{
// Set transforms for this input.
HoudiniAssetInstanceInputField->SetInstanceTransforms(ObjectTransforms);
HoudiniAssetInstanceInputField->UpdateInstanceUPropertyAttributes();
// Add field to list of fields.
NewInstanceInputFields.Add(HoudiniAssetInstanceInputField);
}
}
else if ( InHoudiniGeoPartObject.IsPackedPrimitiveInstancer() )
{
HAPI_Result Result = HAPI_RESULT_SUCCESS;
// We seem to be instancing a PP instancer, we need to get the transforms
HAPI_PartInfo PartInfo;
FHoudiniApi::PartInfo_Init(&PartInfo);
HOUDINI_CHECK_ERROR( &Result, FHoudiniApi::GetPartInfo(
FHoudiniEngine::Get().GetSession(), InHoudiniGeoPartObject.GeoId, InHoudiniGeoPartObject.PartId,
&PartInfo ) );
// Get transforms for each instance
TArray<HAPI_Transform> InstancerPartTransforms;
InstancerPartTransforms.SetNumUninitialized( PartInfo.instanceCount );
for (int32 Idx = 0; Idx < InstancerPartTransforms.Num(); Idx++)
FHoudiniApi::Transform_Init(&(InstancerPartTransforms[Idx]));
HOUDINI_CHECK_ERROR( &Result, FHoudiniApi::GetInstancerPartTransforms(
FHoudiniEngine::Get().GetSession(), InHoudiniGeoPartObject.GeoId, PartInfo.id,
HAPI_RSTORDER_DEFAULT, InstancerPartTransforms.GetData(), 0, PartInfo.instanceCount ) );
// Get the part ids for parts being instanced
TArray<HAPI_PartId> InstancedPartIds;
InstancedPartIds.SetNumZeroed( PartInfo.instancedPartCount );
HOUDINI_CHECK_ERROR( &Result, FHoudiniApi::GetInstancedPartIds(
FHoudiniEngine::Get().GetSession(), InHoudiniGeoPartObject.GeoId, PartInfo.id,
InstancedPartIds.GetData(), 0, PartInfo.instancedPartCount ) );
for ( auto InstancedPartId : InstancedPartIds )
{
HAPI_PartInfo InstancedPartInfo;
FHoudiniApi::PartInfo_Init(&InstancedPartInfo);
HOUDINI_CHECK_ERROR( &Result,
FHoudiniApi::GetPartInfo(
FHoudiniEngine::Get().GetSession(), InHoudiniGeoPartObject.GeoId, InstancedPartId,
&InstancedPartInfo ) );
TArray<FTransform> PPObjectTransforms;
PPObjectTransforms.SetNumUninitialized( InstancerPartTransforms.Num() );
for ( int32 InstanceIdx = 0; InstanceIdx < InstancerPartTransforms.Num(); ++InstanceIdx )
{
const auto& InstanceTransform = InstancerPartTransforms[InstanceIdx];
FHoudiniEngineUtils::TranslateHapiTransform( InstanceTransform, PPObjectTransforms[InstanceIdx] );
}
// Create this instanced input field for this instanced part
// find static mesh for this instancer
FHoudiniGeoPartObject TempInstancedPart( InHoudiniGeoPartObject.AssetId, InHoudiniGeoPartObject.ObjectId, InHoudiniGeoPartObject.GeoId, InstancedPartId );
UStaticMesh* FoundStaticMesh = Comp->LocateStaticMesh(TempInstancedPart, false);
if ( FoundStaticMesh && !FoundStaticMesh->IsPendingKill() )
{
// Build the list of transforms for this instancer
TArray< FTransform > AllTransforms;
AllTransforms.Empty( PPObjectTransforms.Num() * ObjectTransforms.Num() );
for ( const FTransform& ObjectTransform : ObjectTransforms )
{
for ( const FTransform& PPTransform : PPObjectTransforms )
{
AllTransforms.Add( PPTransform * ObjectTransform );
}
}
CreateInstanceInputField( FoundStaticMesh, AllTransforms, InstanceInputFields, NewInstanceInputFields );
}
else
{
HOUDINI_LOG_WARNING(
TEXT( "CreateInstanceInputField for Packed Primitive: Could not find static mesh for object [%d %s], geo %d, part %d]" ), InHoudiniGeoPartObject.ObjectId, *InHoudiniGeoPartObject.ObjectName, InHoudiniGeoPartObject.GeoId, InstancedPartId );
}
}
}
else
{
HOUDINI_LOG_WARNING(
TEXT( "CreateInstanceInputField: Could not find static mesh for object [%d %s], geo %d, part %d]" ), InHoudiniGeoPartObject.ObjectId, *InHoudiniGeoPartObject.ObjectName, InHoudiniGeoPartObject.GeoId, InHoudiniGeoPartObject.PartId );
}
}
void
UHoudiniAssetInstanceInput::CreateInstanceInputField(
UObject * InstancedObject,
const TArray< FTransform > & ObjectTransforms,
const TArray< UHoudiniAssetInstanceInputField * > & OldInstanceInputFields,
TArray< UHoudiniAssetInstanceInputField * > & NewInstanceInputFields )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = nullptr;
// Locate field which have this static mesh set as original mesh.
// Use FindByPredicate instead of FilterByPredicate,
// this fixes the order of the Houdini Instanced Inputs array's randomly changing after a param update.
UHoudiniAssetInstanceInputField** FoundField = InstanceInputFields.FindByPredicate([&](const UHoudiniAssetInstanceInputField* Field)
{
return Field->GetOriginalObject() == InstancedObject;
});
if ( FoundField )
{
HoudiniAssetInstanceInputField = *FoundField;
InstanceInputFields.RemoveSingleSwap( HoudiniAssetInstanceInputField, false );
TArray< int32 > MatchingIndices;
HoudiniAssetInstanceInputField->FindObjectIndices(
HoudiniAssetInstanceInputField->GetOriginalObject(),
MatchingIndices );
for ( int32 ReplacementIndex : MatchingIndices )
{
HoudiniAssetInstanceInputField->ReplaceInstanceVariation( InstancedObject, ReplacementIndex );
}
HoudiniAssetInstanceInputField->OriginalObject = InstancedObject;
// refresh the geo part
FHoudiniGeoPartObject RefreshedGeoPart = HoudiniAssetInstanceInputField->GetHoudiniGeoPartObject();
RefreshedGeoPart.TransformMatrix = HoudiniGeoPartObject.TransformMatrix;
HoudiniAssetInstanceInputField->SetGeoPartObject( RefreshedGeoPart );
// Update component transformation.
HoudiniAssetInstanceInputField->UpdateRelativeTransform();
}
else
{
// Create a dummy part for this field
FHoudiniGeoPartObject InstancedPart;
InstancedPart.TransformMatrix = HoudiniGeoPartObject.TransformMatrix;
HoudiniAssetInstanceInputField = UHoudiniAssetInstanceInputField::Create(
PrimaryObject, this, InstancedPart );
// Assign original and static mesh.
HoudiniAssetInstanceInputField->OriginalObject = InstancedObject;
HoudiniAssetInstanceInputField->AddInstanceVariation( InstancedObject, 0 );
}
// Set transforms for this input.
HoudiniAssetInstanceInputField->SetInstanceTransforms( ObjectTransforms );
// Add field to list of fields.
NewInstanceInputFields.Add( HoudiniAssetInstanceInputField );
}
void
UHoudiniAssetInstanceInput::RecreateRenderStates()
{
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ Idx ];
HoudiniAssetInstanceInputField->RecreateRenderState();
}
}
void
UHoudiniAssetInstanceInput::RecreatePhysicsStates()
{
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ Idx ];
HoudiniAssetInstanceInputField->RecreatePhysicsState();
}
}
void UHoudiniAssetInstanceInput::SetGeoPartObject( const FHoudiniGeoPartObject& InGeoPartObject )
{
HoudiniGeoPartObject = InGeoPartObject;
if ( ObjectToInstanceId == -1 )
{
ObjectToInstanceId = InGeoPartObject.HapiObjectGetToInstanceId();
}
}
bool
UHoudiniAssetInstanceInput::CreateParameter(
UObject * InPrimaryObject,
UHoudiniAssetParameter * InParentParameter,
HAPI_NodeId InNodeId, const HAPI_ParmInfo & ParmInfo)
{
// This implementation is not a true parameter. This method should not be called.
check( false );
return false;
}
#if WITH_EDITOR
void
UHoudiniAssetInstanceInput::OnAddInstanceVariation( UHoudiniAssetInstanceInputField * InstanceInputField, int32 Index )
{
InstanceInputField->AddInstanceVariation( InstanceInputField->GetInstanceVariation( Index ), Index );
OnParamStateChanged();
}
void
UHoudiniAssetInstanceInput::OnRemoveInstanceVariation( UHoudiniAssetInstanceInputField * InstanceInputField, int32 Index )
{
InstanceInputField->RemoveInstanceVariation( Index );
OnParamStateChanged();
}
FString UHoudiniAssetInstanceInput::GetFieldLabel( int32 FieldIdx, int32 VariationIdx ) const
{
FString FieldNameText = FString();
if (!InstanceInputFields.IsValidIndex(FieldIdx))
return FieldNameText;
UHoudiniAssetInstanceInputField * Field = InstanceInputFields[ FieldIdx ];
if ( !Field || Field->IsPendingKill() )
return FieldNameText;
if ( Flags.bIsPackedPrimitiveInstancer )
{
// If the packed prim itself has a cutsom name, use it
// else, use the instancer's custom name
if ( Field->GetHoudiniGeoPartObject().HasCustomName() )
FieldNameText = Field->GetHoudiniGeoPartObject().PartName;
else if( HoudiniGeoPartObject.HasCustomName() )
FieldNameText = HoudiniGeoPartObject.PartName;
else
FieldNameText = Field->GetHoudiniGeoPartObject().GetNodePath();
}
else if ( Flags.bAttributeInstancerOverride )
{
if ( HoudiniGeoPartObject.HasCustomName() )
FieldNameText = HoudiniGeoPartObject.PartName;
else
FieldNameText = HoudiniGeoPartObject.GetNodePath() + TEXT( "/Override_" ) + FString::FromInt( FieldIdx );
}
else
{
// For object-instancers we use the instancer's name as well
if (HoudiniGeoPartObject.HasCustomName())
FieldNameText = HoudiniGeoPartObject.PartName;
else
FieldNameText = HoudiniGeoPartObject.GetNodePath() + TEXT( "/" ) + Field->GetHoudiniGeoPartObject().ObjectName;
}
if ( Field->InstanceVariationCount() > 1 )
FieldNameText += FString::Printf( TEXT( " [%d]" ), VariationIdx );
return FieldNameText;
}
#endif
void
UHoudiniAssetInstanceInput::BeginDestroy()
{
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
InstanceInputFields[ Idx ]->ConditionalBeginDestroy();
InstanceInputFields.Empty();
Super::BeginDestroy();
}
void
UHoudiniAssetInstanceInput::SetHoudiniAssetComponent( UHoudiniAssetComponent * InComponent )
{
UHoudiniAssetParameter::SetHoudiniAssetComponent( InComponent );
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
{
InstanceInputFields[ Idx ]->HoudiniAssetComponent = InComponent;
InstanceInputFields[ Idx ]->HoudiniAssetInstanceInput = this;
}
}
void
UHoudiniAssetInstanceInput::Serialize( FArchive & Ar )
{
// Call base implementation.
Super::Serialize( Ar );
Ar.UsingCustomVersion( FHoudiniCustomSerializationVersion::GUID );
Ar << Flags.HoudiniAssetInstanceInputFlagsPacked;
Ar << HoudiniGeoPartObject;
Ar << ObjectToInstanceId;
// Object id is transient
if ( Ar.IsLoading() && !Ar.IsTransacting() )
ObjectToInstanceId = -1;
// Serialize fields.
Ar << InstanceInputFields;
}
void
UHoudiniAssetInstanceInput::AddReferencedObjects( UObject * InThis, FReferenceCollector & Collector )
{
UHoudiniAssetInstanceInput * HoudiniAssetInstanceInput = Cast< UHoudiniAssetInstanceInput >( InThis );
if ( HoudiniAssetInstanceInput && !HoudiniAssetInstanceInput->IsPendingKill() )
{
// Add references to all used fields.
for ( int32 Idx = 0; Idx < HoudiniAssetInstanceInput->InstanceInputFields.Num(); ++Idx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField =
HoudiniAssetInstanceInput->InstanceInputFields[ Idx ];
if ( HoudiniAssetInstanceInputField && !HoudiniAssetInstanceInputField->IsPendingKill() )
Collector.AddReferencedObject( HoudiniAssetInstanceInputField, InThis );
}
}
// Call base implementation.
Super::AddReferencedObjects( InThis, Collector );
}
#if WITH_EDITOR
void
UHoudiniAssetInstanceInput::CloneComponentsAndAttachToActor( AActor * Actor )
{
if (!Actor || Actor->IsPendingKill())
return;
USceneComponent * RootComponent = Actor->GetRootComponent();
TMap< const UStaticMesh*, UStaticMesh* > OriginalToBakedMesh;
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ Idx ];
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
continue;
for ( int32 VariationIdx = 0;
VariationIdx < HoudiniAssetInstanceInputField->InstanceVariationCount(); VariationIdx++ )
{
UInstancedStaticMeshComponent * ISMC =
Cast<UInstancedStaticMeshComponent>( HoudiniAssetInstanceInputField->GetInstancedComponent( VariationIdx ) );
UHoudiniMeshSplitInstancerComponent * MSIC =
Cast<UHoudiniMeshSplitInstancerComponent>( HoudiniAssetInstanceInputField->GetInstancedComponent( VariationIdx ) );
if ( ( !ISMC || ISMC->IsPendingKill() ) && ( !MSIC || MSIC->IsPendingKill() ) )
{
UHoudiniInstancedActorComponent* IAC = Cast<UHoudiniInstancedActorComponent>(HoudiniAssetInstanceInputField->GetInstancedComponent(VariationIdx));
if ( !IAC || IAC->IsPendingKill() || !IAC->InstancedAsset )
continue;
UClass* ObjectClass = IAC->InstancedAsset->GetClass();
if ( !ObjectClass || ObjectClass->IsPendingKill() )
continue;
TSubclassOf<AActor> ActorClass;
if( ObjectClass->IsChildOf<AActor>() )
{
ActorClass = ObjectClass;
}
else if( ObjectClass->IsChildOf<UBlueprint>() )
{
UBlueprint* BlueprintObj = StaticCast<UBlueprint*>( IAC->InstancedAsset );
if ( BlueprintObj && !BlueprintObj->IsPendingKill() )
ActorClass = *BlueprintObj->GeneratedClass;
}
if( *ActorClass )
{
for( AActor* InstancedActor : IAC->Instances )
{
if( !InstancedActor || InstancedActor->IsPendingKill() )
continue;
UChildActorComponent* CAC = NewObject< UChildActorComponent >( Actor, UChildActorComponent::StaticClass(), NAME_None, RF_Public );
if ( !CAC || CAC->IsPendingKill() )
continue;
Actor->AddInstanceComponent( CAC );
CAC->SetChildActorClass( ActorClass );
CAC->RegisterComponent();
CAC->SetWorldTransform( InstancedActor->GetTransform() );
if ( RootComponent && !RootComponent->IsPendingKill() )
CAC->AttachToComponent( RootComponent, FAttachmentTransformRules::KeepWorldTransform );
}
}
else if( ObjectClass->IsChildOf<UParticleSystem>() )
{
for( AActor* InstancedActor : IAC->Instances )
{
if( InstancedActor && !InstancedActor->IsPendingKill() )
{
UParticleSystemComponent* PSC = NewObject< UParticleSystemComponent >( Actor, UParticleSystemComponent::StaticClass(), NAME_None, RF_Public );
if ( !PSC || PSC->IsPendingKill() )
continue;
Actor->AddInstanceComponent( PSC );
PSC->SetTemplate( StaticCast<UParticleSystem*>( IAC->InstancedAsset ) );
PSC->RegisterComponent();
PSC->SetWorldTransform( InstancedActor->GetTransform() );
if ( RootComponent && !RootComponent->IsPendingKill() )
PSC->AttachToComponent( RootComponent, FAttachmentTransformRules::KeepWorldTransform );
}
}
}
else if( ObjectClass->IsChildOf<USoundBase>() )
{
for( AActor* InstancedActor : IAC->Instances )
{
if( InstancedActor && !InstancedActor->IsPendingKill() )
{
UAudioComponent* AC = NewObject< UAudioComponent >( Actor, UAudioComponent::StaticClass(), NAME_None, RF_Public );
if ( !AC || AC->IsPendingKill() )
continue;
Actor->AddInstanceComponent( AC );
AC->SetSound( StaticCast<USoundBase*>( IAC->InstancedAsset ) );
AC->RegisterComponent();
AC->SetWorldTransform( InstancedActor->GetTransform() );
if ( RootComponent && !RootComponent->IsPendingKill() )
AC->AttachToComponent( RootComponent, FAttachmentTransformRules::KeepWorldTransform );
}
}
}
else
{
// Oh no, the asset is not something we know. We will need to handle each asset type case by case.
// for example we could create a bunch of ParticleSystemComponent if given an emitter asset
HOUDINI_LOG_ERROR( TEXT( "Can not bake instanced actor component for asset type %s" ), *ObjectClass->GetName() );
}
}
// If original static mesh is used, then we need to bake it (once) and use that one instead.
UStaticMesh* OutStaticMesh = Cast<UStaticMesh>( HoudiniAssetInstanceInputField->GetInstanceVariation( VariationIdx ) );
if ( HoudiniAssetInstanceInputField->IsOriginalObjectUsed( VariationIdx ) )
{
UStaticMesh* OriginalSM = Cast<UStaticMesh>(HoudiniAssetInstanceInputField->GetOriginalObject());
if( OriginalSM && !OriginalSM->IsPendingKill() )
{
auto Comp = GetHoudiniAssetComponent();
if ( Comp && !Comp->IsPendingKill() )
{
auto&& ItemGeoPartObject = Comp->LocateGeoPartObject(OutStaticMesh);
FHoudiniEngineBakeUtils::CheckedBakeStaticMesh(Comp, OriginalToBakedMesh, ItemGeoPartObject, OriginalSM);
OutStaticMesh = OriginalToBakedMesh[OutStaticMesh];
}
}
}
if( ISMC && !ISMC->IsPendingKill() )
{
// Do we need a Hierarchical ISMC ?
UHierarchicalInstancedStaticMeshComponent * HISMC =
Cast<UHierarchicalInstancedStaticMeshComponent>( HoudiniAssetInstanceInputField->GetInstancedComponent( VariationIdx ) );
UInstancedStaticMeshComponent* DuplicatedComponent = nullptr;
if ( HISMC && !HISMC->IsPendingKill() )
DuplicatedComponent = NewObject< UHierarchicalInstancedStaticMeshComponent >(
Actor, UHierarchicalInstancedStaticMeshComponent::StaticClass(), NAME_None, RF_Public );
else
DuplicatedComponent = NewObject< UInstancedStaticMeshComponent >(
Actor, UInstancedStaticMeshComponent::StaticClass(), NAME_None, RF_Public );
if (DuplicatedComponent && !DuplicatedComponent->IsPendingKill())
{
Actor->AddInstanceComponent(DuplicatedComponent);
DuplicatedComponent->SetStaticMesh(OutStaticMesh);
// Reapply the uproperties modified by attributes on the duplicated component
FHoudiniEngineUtils::UpdateUPropertyAttributesOnObject(DuplicatedComponent, HoudiniGeoPartObject);
TArray<FTransform> ProcessedTransforms;
HoudiniAssetInstanceInputField->GetProcessedTransforms(ProcessedTransforms, VariationIdx);
// Set component instances.
UHoudiniInstancedActorComponent::UpdateInstancerComponentInstances(
DuplicatedComponent, ProcessedTransforms, HoudiniAssetInstanceInputField->GetInstancedColors( VariationIdx ) );
// Copy visibility.
DuplicatedComponent->SetVisibility(ISMC->IsVisible());
if ( RootComponent && !RootComponent->IsPendingKill() )
DuplicatedComponent->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
DuplicatedComponent->RegisterComponent();
DuplicatedComponent->GetBodyInstance()->bAutoWeld = false;
}
}
else if ( MSIC && !MSIC->IsPendingKill() )
{
for( UStaticMeshComponent* OtherSMC : MSIC->GetInstances() )
{
if ( !OtherSMC || OtherSMC->IsPendingKill() )
continue;
FString CompName = OtherSMC->GetName();
CompName.ReplaceInline( TEXT("StaticMeshComponent"), TEXT("StaticMesh") );
UStaticMeshComponent* NewSMC = DuplicateObject< UStaticMeshComponent >(OtherSMC, Actor, *CompName);
if( NewSMC && !NewSMC->IsPendingKill() )
{
if ( RootComponent && !RootComponent->IsPendingKill() )
NewSMC->SetupAttachment( RootComponent );
NewSMC->SetStaticMesh( OutStaticMesh );
Actor->AddInstanceComponent( NewSMC );
NewSMC->SetWorldTransform( OtherSMC->GetComponentTransform() );
NewSMC->RegisterComponent();
}
}
}
}
}
}
#endif
void
UHoudiniAssetInstanceInput::GetPathInstaceTransforms(
const FString & ObjectInstancePath,
const TArray< FString > & PointInstanceValues, const TArray< FTransform > & Transforms,
TArray< FTransform > & OutTransforms)
{
OutTransforms.Empty();
for ( int32 Idx = 0; Idx < PointInstanceValues.Num(); ++Idx )
{
if ( ObjectInstancePath.Equals( PointInstanceValues[ Idx ] ) )
OutTransforms.Add( Transforms[ Idx ] );
}
}
#if WITH_EDITOR
void
UHoudiniAssetInstanceInput::OnStaticMeshDropped(
UObject * InObject,
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField,
int32 Idx, int32 VariationIdx )
{
if ( !InObject || InObject->IsPendingKill() )
return;
ULevel* CurrentLevel = GWorld->GetCurrentLevel();
ULevel* MyLevel = GetHoudiniAssetComponent()->GetOwner()->GetLevel();
if( MyLevel != CurrentLevel )
{
HOUDINI_LOG_ERROR( TEXT( "%s: Could not spawn instanced actor because the actor's level is not the Current Level." ), *GetHoudiniAssetComponent()->GetOwner()->GetName() );
FHoudiniEngineUtils::CreateSlateNotification( TEXT( "Could not spawn instanced actor because the actor's level is not the Current Level." ) );
return;
}
UObject * UsedObj = HoudiniAssetInstanceInputField->GetInstanceVariation( VariationIdx );
if ( UsedObj != InObject )
{
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
HoudiniAssetInstanceInputField->ReplaceInstanceVariation( InObject, VariationIdx );
OnParamStateChanged();
}
}
FReply
UHoudiniAssetInstanceInput::OnThumbnailDoubleClick(
const FGeometry & InMyGeometry, const FPointerEvent & InMouseEvent, UObject * Object )
{
if ( Object && GEditor )
GEditor->EditObject( Object );
return FReply::Handled();
}
void UHoudiniAssetInstanceInput::OnInstancedObjectBrowse( UObject* InstancedObject )
{
if ( GEditor )
{
TArray< UObject * > Objects;
Objects.Add( InstancedObject );
GEditor->SyncBrowserToObjects( Objects );
}
}
FReply
UHoudiniAssetInstanceInput::OnResetStaticMeshClicked(
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField,
int32 Idx, int32 VariationIdx )
{
UObject * Obj = HoudiniAssetInstanceInputField->GetOriginalObject();
OnStaticMeshDropped( Obj, HoudiniAssetInstanceInputField, Idx, VariationIdx );
return FReply::Handled();
}
void
UHoudiniAssetInstanceInput::CloseStaticMeshComboButton(
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField,
int32 Idx, int32 VariationIdx )
{
// Do nothing.
}
void
UHoudiniAssetInstanceInput::ChangedStaticMeshComboButton(
bool bOpened, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 Idx, int32 VariationIdx )
{
if ( !bOpened )
{
// If combo button has been closed, update the UI.
OnParamStateChanged();
}
}
TOptional< float >
UHoudiniAssetInstanceInput::GetRotationRoll(
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx ) const
{
const FRotator & Rotator = HoudiniAssetInstanceInputField->GetRotationOffset( VariationIdx );
return Rotator.Roll;
}
TOptional< float >
UHoudiniAssetInstanceInput::GetRotationPitch(
UHoudiniAssetInstanceInputField* HoudiniAssetInstanceInputField, int32 VariationIdx ) const
{
const FRotator & Rotator = HoudiniAssetInstanceInputField->GetRotationOffset( VariationIdx );
return Rotator.Pitch;
}
TOptional< float >
UHoudiniAssetInstanceInput::GetRotationYaw(
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx ) const
{
const FRotator& Rotator = HoudiniAssetInstanceInputField->GetRotationOffset( VariationIdx );
return Rotator.Yaw;
}
void
UHoudiniAssetInstanceInput::SetRotationRoll(
float Value, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx )
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
FRotator Rotator = HoudiniAssetInstanceInputField->GetRotationOffset( VariationIdx );
Rotator.Roll = Value;
HoudiniAssetInstanceInputField->SetRotationOffset( Rotator, VariationIdx );
HoudiniAssetInstanceInputField->UpdateInstanceTransforms( false );
}
void
UHoudiniAssetInstanceInput::SetRotationPitch(
float Value, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx )
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
FRotator Rotator = HoudiniAssetInstanceInputField->GetRotationOffset( VariationIdx );
Rotator.Pitch = Value;
HoudiniAssetInstanceInputField->SetRotationOffset( Rotator, VariationIdx );
HoudiniAssetInstanceInputField->UpdateInstanceTransforms( false );
}
void
UHoudiniAssetInstanceInput::SetRotationYaw(
float Value, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx )
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
FRotator Rotator = HoudiniAssetInstanceInputField->GetRotationOffset( VariationIdx );
Rotator.Yaw = Value;
HoudiniAssetInstanceInputField->SetRotationOffset( Rotator, VariationIdx );
HoudiniAssetInstanceInputField->UpdateInstanceTransforms( false );
}
TOptional< float >
UHoudiniAssetInstanceInput::GetScaleX(
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx ) const
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return 1.0f;
const FVector & Scale3D = HoudiniAssetInstanceInputField->GetScaleOffset( VariationIdx );
return Scale3D.X;
}
TOptional< float >
UHoudiniAssetInstanceInput::GetScaleY(
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx ) const
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return 1.0f;
const FVector & Scale3D = HoudiniAssetInstanceInputField->GetScaleOffset( VariationIdx );
return Scale3D.Y;
}
TOptional< float >
UHoudiniAssetInstanceInput::GetScaleZ(
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx ) const
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return 1.0f;
const FVector & Scale3D = HoudiniAssetInstanceInputField->GetScaleOffset( VariationIdx );
return Scale3D.Z;
}
void
UHoudiniAssetInstanceInput::SetScaleX(
float Value, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx )
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
FVector Scale3D = HoudiniAssetInstanceInputField->GetScaleOffset( VariationIdx );
Scale3D.X = Value;
if ( HoudiniAssetInstanceInputField->AreOffsetsScaledLinearly( VariationIdx ) )
{
Scale3D.Y = Value;
Scale3D.Z = Value;
}
HoudiniAssetInstanceInputField->SetScaleOffset( Scale3D, VariationIdx );
HoudiniAssetInstanceInputField->UpdateInstanceTransforms( false );
}
void
UHoudiniAssetInstanceInput::SetScaleY(
float Value, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx)
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
FVector Scale3D = HoudiniAssetInstanceInputField->GetScaleOffset( VariationIdx );
Scale3D.Y = Value;
if ( HoudiniAssetInstanceInputField->AreOffsetsScaledLinearly( VariationIdx ) )
{
Scale3D.X = Value;
Scale3D.Z = Value;
}
HoudiniAssetInstanceInputField->SetScaleOffset( Scale3D, VariationIdx );
HoudiniAssetInstanceInputField->UpdateInstanceTransforms( false );
}
void
UHoudiniAssetInstanceInput::SetScaleZ(
float Value, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx)
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
FVector Scale3D = HoudiniAssetInstanceInputField->GetScaleOffset( VariationIdx );
Scale3D.Z = Value;
if ( HoudiniAssetInstanceInputField->AreOffsetsScaledLinearly( VariationIdx ) )
{
Scale3D.Y = Value;
Scale3D.X = Value;
}
HoudiniAssetInstanceInputField->SetScaleOffset( Scale3D, VariationIdx );
HoudiniAssetInstanceInputField->UpdateInstanceTransforms( false );
}
void
UHoudiniAssetInstanceInput::CheckStateChanged(
bool IsChecked, UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField, int32 VariationIdx )
{
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
return;
FScopedTransaction Transaction(
TEXT( HOUDINI_MODULE_RUNTIME ),
LOCTEXT( "HoudiniInstanceInputChange", "Houdini Instance Input Change" ),
PrimaryObject );
HoudiniAssetInstanceInputField->Modify();
HoudiniAssetInstanceInputField->SetLinearOffsetScale( IsChecked, VariationIdx );
}
#endif
bool
UHoudiniAssetInstanceInput::CollectAllInstancedStaticMeshComponents(
TArray< UInstancedStaticMeshComponent * > & Components, const UStaticMesh * StaticMesh )
{
bool bCollected = false;
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ Idx ];
if ( HoudiniAssetInstanceInputField && !HoudiniAssetInstanceInputField->IsPendingKill() )
{
UStaticMesh * OriginalStaticMesh = Cast< UStaticMesh >( HoudiniAssetInstanceInputField->GetOriginalObject() );
if ( OriginalStaticMesh == StaticMesh )
{
for ( int32 IdxMesh = 0; IdxMesh < HoudiniAssetInstanceInputField->InstancedObjects.Num(); ++IdxMesh )
{
UStaticMesh * UsedStaticMesh = Cast< UStaticMesh >( HoudiniAssetInstanceInputField->InstancedObjects[ IdxMesh ] );
if ( UsedStaticMesh == StaticMesh )
{
if ( !HoudiniAssetInstanceInputField->InstancerComponents.IsValidIndex(IdxMesh) )
continue;
UInstancedStaticMeshComponent* ISMC = Cast< UInstancedStaticMeshComponent >( HoudiniAssetInstanceInputField->InstancerComponents[ IdxMesh ] );
if ( !ISMC || ISMC->IsPendingKill() )
continue;
Components.Add( ISMC );
bCollected = true;
}
}
}
}
}
return bCollected;
}
bool
UHoudiniAssetInstanceInput::GetMaterialReplacementMeshes(
UMaterialInterface * Material,
TMap< UStaticMesh *, int32 > & MaterialReplacementsMap )
{
bool bResult = false;
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
{
UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ Idx ];
if ( HoudiniAssetInstanceInputField && !HoudiniAssetInstanceInputField->IsPendingKill() )
bResult |= HoudiniAssetInstanceInputField->GetMaterialReplacementMeshes( Material, MaterialReplacementsMap );
}
return bResult;
}
#undef LOCTEXT_NAMESPACE