/* * 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 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 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 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( PrimaryObject ); } const UHoudiniAssetComponent* UHoudiniAssetInstanceInput::GetHoudiniAssetComponent() const { return Cast( 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 & 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 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 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 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( HoudiniAssetInstanceInputField->GetInstancedComponent( VariationIdx ) ); UHoudiniMeshSplitInstancerComponent * MSIC = Cast( HoudiniAssetInstanceInputField->GetInstancedComponent( VariationIdx ) ); if ( ( !ISMC || ISMC->IsPendingKill() ) && ( !MSIC || MSIC->IsPendingKill() ) ) { UHoudiniInstancedActorComponent* IAC = Cast(HoudiniAssetInstanceInputField->GetInstancedComponent(VariationIdx)); if ( !IAC || IAC->IsPendingKill() || !IAC->InstancedAsset ) continue; UClass* ObjectClass = IAC->InstancedAsset->GetClass(); if ( !ObjectClass || ObjectClass->IsPendingKill() ) continue; TSubclassOf ActorClass; if( ObjectClass->IsChildOf() ) { ActorClass = ObjectClass; } else if( ObjectClass->IsChildOf() ) { UBlueprint* BlueprintObj = StaticCast( 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() ) { 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( IAC->InstancedAsset ) ); PSC->RegisterComponent(); PSC->SetWorldTransform( InstancedActor->GetTransform() ); if ( RootComponent && !RootComponent->IsPendingKill() ) PSC->AttachToComponent( RootComponent, FAttachmentTransformRules::KeepWorldTransform ); } } } else if( ObjectClass->IsChildOf() ) { 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( 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( HoudiniAssetInstanceInputField->GetInstanceVariation( VariationIdx ) ); if ( HoudiniAssetInstanceInputField->IsOriginalObjectUsed( VariationIdx ) ) { UStaticMesh* OriginalSM = Cast(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( 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 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