2021-11-26 15:48:13 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SmartObjectDefinition.h"
2022-03-02 13:25:37 -05:00
# include "SmartObjectSettings.h"
2023-08-29 07:48:32 -04:00
# include "Misc/EnumerateRange.h"
2022-11-01 15:11:25 -04:00
# if WITH_EDITOR
# include "UObject/ObjectSaveContext.h"
2023-01-18 10:52:51 -05:00
# include "WorldConditions/WorldCondition_SmartObjectActorTagQuery.h"
# include "WorldConditions/SmartObjectWorldConditionObjectTagQuery.h"
2023-03-27 08:22:19 -04:00
# include "SmartObjectUserComponent.h"
# include "Engine/SCS_Node.h"
2024-03-07 08:03:24 -05:00
# include "Misc/Crc.h"
2023-04-27 17:53:18 -04:00
# include "Misc/DataValidation.h"
2023-08-29 07:48:32 -04:00
# include "SmartObjectPropertyHelpers.h"
2023-12-20 08:17:55 -05:00
# include "Interfaces/ITargetPlatform.h"
2022-11-01 15:11:25 -04:00
# endif
2024-01-29 06:26:17 -05:00
# include "Serialization/ObjectAndNameAsStringProxyArchive.h"
# include "Serialization/MemoryWriter.h"
# include "UObject/Package.h"
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectDefinition)
2023-01-23 18:48:38 -05:00
# define LOCTEXT_NAMESPACE "SmartObjectDefinition"
2022-01-07 14:28:06 -05:00
namespace UE : : SmartObject
{
const FVector DefaultSlotSize ( 40 , 40 , 90 ) ;
2024-01-29 06:26:17 -05:00
namespace Delegates
{
# if WITH_EDITOR
FOnParametersChanged OnParametersChanged ;
# endif
} // Delegates
} // UE::SmartObject
const FSmartObjectDefinitionDataHandle FSmartObjectDefinitionDataHandle : : Invalid ( INDEX_NONE ) ;
const FSmartObjectDefinitionDataHandle FSmartObjectDefinitionDataHandle : : Root ( RootIndex ) ;
const FSmartObjectDefinitionDataHandle FSmartObjectDefinitionDataHandle : : Parameters ( ParametersIndex ) ;
2022-01-07 14:28:06 -05:00
2022-03-02 13:25:37 -05:00
USmartObjectDefinition : : USmartObjectDefinition ( const FObjectInitializer & ObjectInitializer ) : UDataAsset ( ObjectInitializer )
{
2022-03-02 22:33:53 -05:00
UserTagsFilteringPolicy = GetDefault < USmartObjectSettings > ( ) - > DefaultUserTagsFilteringPolicy ;
ActivityTagsMergingPolicy = GetDefault < USmartObjectSettings > ( ) - > DefaultActivityTagsMergingPolicy ;
2022-11-17 07:44:24 -05:00
WorldConditionSchemaClass = GetDefault < USmartObjectSettings > ( ) - > DefaultWorldConditionSchemaClass ;
2022-03-02 13:25:37 -05:00
}
2023-01-23 18:48:38 -05:00
# if WITH_EDITOR
2023-04-27 17:53:18 -04:00
EDataValidationResult USmartObjectDefinition : : IsDataValid ( FDataValidationContext & Context ) const
2023-01-23 18:48:38 -05:00
{
2023-04-27 17:53:18 -04:00
const EDataValidationResult Result = Super : : IsDataValid ( Context ) ;
2023-01-23 18:48:38 -05:00
2023-04-27 17:53:18 -04:00
TArray < FText > ValidationErrors ;
2023-01-23 18:48:38 -05:00
Validate ( & ValidationErrors ) ;
2023-04-27 17:53:18 -04:00
for ( const FText & Error : ValidationErrors )
{
Context . AddError ( Error ) ;
}
2023-01-23 18:48:38 -05:00
2023-08-16 14:15:36 -04:00
return CombineDataValidationResults ( Result , bValid . GetValue ( ) ? EDataValidationResult : : Valid : EDataValidationResult : : Invalid ) ;
2023-01-23 18:48:38 -05:00
}
2023-03-27 08:22:19 -04:00
TSubclassOf < USmartObjectSlotValidationFilter > USmartObjectDefinition : : GetPreviewValidationFilterClass ( ) const
{
if ( PreviewData . UserActorClass . IsValid ( ) )
{
if ( const UClass * UserActorClass = PreviewData . UserActorClass . Get ( ) )
{
// Try to get smart object user component added in the BP.
if ( const UBlueprintGeneratedClass * UserBlueprintClass = Cast < UBlueprintGeneratedClass > ( UserActorClass ) )
{
const TArray < USCS_Node * > & Nodes = UserBlueprintClass - > SimpleConstructionScript - > GetAllNodes ( ) ;
for ( USCS_Node * Node : Nodes )
{
UActorComponent * Component = Node - > GetActualComponentTemplate ( const_cast < UBlueprintGeneratedClass * > ( UserBlueprintClass ) ) ;
if ( const USmartObjectUserComponent * UserComponent = Cast < USmartObjectUserComponent > ( Component ) )
{
return UserComponent - > GetValidationFilter ( ) ;
}
}
}
// Try to get the component from the CDO (e.g. added as default object in C++).
if ( const AActor * UserActor = Cast < AActor > ( UserActorClass - > GetDefaultObject ( ) ) )
{
if ( const USmartObjectUserComponent * UserComponent = UserActor - > GetComponentByClass < USmartObjectUserComponent > ( ) )
{
return UserComponent - > GetValidationFilter ( ) ;
}
}
}
return nullptr ;
}
if ( PreviewData . UserValidationFilterClass . IsValid ( ) )
{
return PreviewData . UserValidationFilterClass . Get ( ) ;
}
return nullptr ;
}
2023-01-23 18:48:38 -05:00
# endif // WITH_EDITOR
bool USmartObjectDefinition : : Validate ( TArray < FText > * ErrorsToReport ) const
2021-11-26 15:48:13 -05:00
{
bValid = false ;
// Detect null entries in default definitions
int32 NullEntryIndex ;
if ( DefaultBehaviorDefinitions . Find ( nullptr , NullEntryIndex ) )
{
2023-01-23 18:48:38 -05:00
if ( ErrorsToReport )
{
ErrorsToReport - > Emplace ( FText : : Format ( LOCTEXT ( " NullDefaultBehaviorEntryError " , " Null entry found at index {0} in default behavior definition list " ) , NullEntryIndex ) ) ;
}
else
{
return false ;
}
2021-11-26 15:48:13 -05:00
}
// Detect null entries in slot definitions
for ( int i = 0 ; i < Slots . Num ( ) ; + + i )
{
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotDefinition & Slot = Slots [ i ] ;
2021-11-26 15:48:13 -05:00
if ( Slot . BehaviorDefinitions . Find ( nullptr , NullEntryIndex ) )
{
2023-01-23 18:48:38 -05:00
if ( ErrorsToReport )
{
ErrorsToReport - > Emplace ( FText : : Format ( LOCTEXT ( " NullSlotBehaviorEntryError " , " Null entry found at index {0} in default behavior definition list " ) , NullEntryIndex ) ) ;
}
else
{
return false ;
}
2021-11-26 15:48:13 -05:00
}
}
// Detect missing definitions in slots if no default one are provided
if ( DefaultBehaviorDefinitions . Num ( ) = = 0 )
{
for ( int i = 0 ; i < Slots . Num ( ) ; + + i )
{
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotDefinition & Slot = Slots [ i ] ;
2021-11-26 15:48:13 -05:00
if ( Slot . BehaviorDefinitions . Num ( ) = = 0 )
{
2023-01-23 18:48:38 -05:00
if ( ErrorsToReport )
{
ErrorsToReport - > Emplace ( FText : : Format ( LOCTEXT ( " MissingSlotBehaviorError " , " Slot at index {0} needs to provide a behavior definition since there is no default one in the SmartObject definition " ) , i ) ) ;
}
else
{
return false ;
}
2021-11-26 15:48:13 -05:00
}
}
}
2023-08-16 14:15:36 -04:00
bValid = ErrorsToReport = = nullptr | | ErrorsToReport - > IsEmpty ( ) ;
return bValid . GetValue ( ) ;
2021-11-26 15:48:13 -05:00
}
2022-03-02 22:33:53 -05:00
FBox USmartObjectDefinition : : GetBounds ( ) const
{
FBox BoundingBox ( ForceInitToZero ) ;
for ( const FSmartObjectSlotDefinition & Slot : GetSlots ( ) )
{
2023-07-18 08:42:51 -04:00
BoundingBox + = FVector ( Slot . Offset ) + UE : : SmartObject : : DefaultSlotSize ;
BoundingBox + = FVector ( Slot . Offset ) - UE : : SmartObject : : DefaultSlotSize ;
2022-03-02 22:33:53 -05:00
}
return BoundingBox ;
}
2023-07-18 08:42:51 -04:00
void USmartObjectDefinition : : GetSlotActivityTags ( const int32 SlotIndex , FGameplayTagContainer & OutActivityTags ) const
2022-03-02 22:33:53 -05:00
{
if ( ensureMsgf ( Slots . IsValidIndex ( SlotIndex ) , TEXT ( " Requesting activity tags for an out of range slot index: %s " ) , * LexToString ( SlotIndex ) ) )
{
GetSlotActivityTags ( Slots [ SlotIndex ] , OutActivityTags ) ;
}
}
void USmartObjectDefinition : : GetSlotActivityTags ( const FSmartObjectSlotDefinition & SlotDefinition , FGameplayTagContainer & OutActivityTags ) const
{
OutActivityTags = ActivityTags ;
if ( ActivityTagsMergingPolicy = = ESmartObjectTagMergingPolicy : : Combine )
{
OutActivityTags . AppendTags ( SlotDefinition . ActivityTags ) ;
}
else if ( ActivityTagsMergingPolicy = = ESmartObjectTagMergingPolicy : : Override & & ! SlotDefinition . ActivityTags . IsEmpty ( ) )
{
OutActivityTags = SlotDefinition . ActivityTags ;
}
}
2023-07-18 08:42:51 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
2022-03-02 22:33:53 -05:00
TOptional < FTransform > USmartObjectDefinition : : GetSlotTransform ( const FTransform & OwnerTransform , const FSmartObjectSlotIndex SlotIndex ) const
{
TOptional < FTransform > Transform ;
if ( ensureMsgf ( Slots . IsValidIndex ( SlotIndex ) , TEXT ( " Requesting slot transform for an out of range index: %s " ) , * LexToString ( SlotIndex ) ) )
{
const FSmartObjectSlotDefinition & Slot = Slots [ SlotIndex ] ;
2023-07-18 08:42:51 -04:00
Transform = FTransform ( FRotator ( Slot . Rotation ) , FVector ( Slot . Offset ) ) * OwnerTransform ;
2022-03-02 22:33:53 -05:00
}
return Transform ;
}
2023-07-18 08:42:51 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2022-03-02 22:33:53 -05:00
2023-07-18 08:42:51 -04:00
FTransform USmartObjectDefinition : : GetSlotWorldTransform ( const int32 SlotIndex , const FTransform & OwnerTransform ) const
{
if ( ensureMsgf ( Slots . IsValidIndex ( SlotIndex ) , TEXT ( " Requesting slot transform for an out of range index: %s " ) , * LexToString ( SlotIndex ) ) )
{
const FSmartObjectSlotDefinition & Slot = Slots [ SlotIndex ] ;
return FTransform ( FRotator ( Slot . Rotation ) , FVector ( Slot . Offset ) ) * OwnerTransform ;
}
return OwnerTransform ;
}
2022-03-02 22:33:53 -05:00
2023-07-18 08:42:51 -04:00
const USmartObjectBehaviorDefinition * USmartObjectDefinition : : GetBehaviorDefinition ( const int32 SlotIndex ,
2022-03-02 13:25:37 -05:00
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass ) const
2021-11-26 15:48:13 -05:00
{
const USmartObjectBehaviorDefinition * Definition = nullptr ;
if ( Slots . IsValidIndex ( SlotIndex ) )
{
Definition = GetBehaviorDefinitionByType ( Slots [ SlotIndex ] . BehaviorDefinitions , DefinitionClass ) ;
}
if ( Definition = = nullptr )
{
Definition = GetBehaviorDefinitionByType ( DefaultBehaviorDefinitions , DefinitionClass ) ;
}
return Definition ;
}
const USmartObjectBehaviorDefinition * USmartObjectDefinition : : GetBehaviorDefinitionByType ( const TArray < USmartObjectBehaviorDefinition * > & BehaviorDefinitions ,
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
{
2023-01-18 10:52:51 -05:00
USmartObjectBehaviorDefinition * const * BehaviorDefinition = BehaviorDefinitions . FindByPredicate ( [ & DefinitionClass ] ( const USmartObjectBehaviorDefinition * SlotBehaviorDefinition )
2021-11-26 15:48:13 -05:00
{
return SlotBehaviorDefinition ! = nullptr & & SlotBehaviorDefinition - > GetClass ( ) - > IsChildOf ( * DefinitionClass ) ;
} ) ;
return BehaviorDefinition ! = nullptr ? * BehaviorDefinition : nullptr ;
}
2022-11-01 15:11:25 -04:00
# if WITH_EDITOR
int32 USmartObjectDefinition : : FindSlotByID ( const FGuid ID ) const
{
const int32 Slot = Slots . IndexOfByPredicate ( [ & ID ] ( const FSmartObjectSlotDefinition & Slot ) { return Slot . ID = = ID ; } ) ;
return Slot ;
}
2022-09-28 01:06:15 -04:00
2023-08-29 07:48:32 -04:00
bool USmartObjectDefinition : : FindSlotAndDefinitionDataIndexByID ( const FGuid ID , int32 & OutSlotIndex , int32 & OutDefinitionDataIndex ) const
{
OutSlotIndex = INDEX_NONE ;
OutDefinitionDataIndex = INDEX_NONE ;
// First try to find direct match on a slot.
for ( TConstEnumerateRef < const FSmartObjectSlotDefinition > SlotDefinition : EnumerateRange ( Slots ) )
{
if ( SlotDefinition - > ID = = ID )
{
OutSlotIndex = SlotDefinition . GetIndex ( ) ;
return true ;
}
// Next try to find slot index based on definition data.
2023-12-04 07:36:52 -05:00
const int32 DefinitionDataIndex = SlotDefinition - > DefinitionData . IndexOfByPredicate ( [ & ID ] ( const FSmartObjectDefinitionDataProxy & DataProxy )
2023-08-29 07:48:32 -04:00
{
return DataProxy . ID = = ID ;
} ) ;
if ( DefinitionDataIndex ! = INDEX_NONE )
{
OutSlotIndex = SlotDefinition . GetIndex ( ) ;
OutDefinitionDataIndex = DefinitionDataIndex ;
return true ;
}
}
return false ;
}
2022-11-01 15:11:25 -04:00
void USmartObjectDefinition : : PostEditChangeChainProperty ( FPropertyChangedChainEvent & PropertyChangedEvent )
{
Super : : PostEditChangeChainProperty ( PropertyChangedEvent ) ;
2023-08-29 07:48:32 -04:00
const FSmartObjectEditPropertyPath ChangePropertyPath ( PropertyChangedEvent ) ;
2024-01-29 06:26:17 -05:00
static const FSmartObjectEditPropertyPath ParametersPath ( USmartObjectDefinition : : StaticClass ( ) , TEXT ( " Parameters " ) ) ;
2023-08-29 07:48:32 -04:00
static const FSmartObjectEditPropertyPath SlotsPath ( USmartObjectDefinition : : StaticClass ( ) , TEXT ( " Slots " ) ) ;
static const FSmartObjectEditPropertyPath WorldConditionSchemaClassPath ( USmartObjectDefinition : : StaticClass ( ) , TEXT ( " WorldConditionSchemaClass " ) ) ;
static const FSmartObjectEditPropertyPath SlotsDefinitionDataPath ( USmartObjectDefinition : : StaticClass ( ) , TEXT ( " Slots.DefinitionData " ) ) ;
2022-11-01 15:11:25 -04:00
// Ensure unique Slot ID on added or duplicated items.
if ( PropertyChangedEvent . ChangeType = = EPropertyChangeType : : ArrayAdd
| | PropertyChangedEvent . ChangeType = = EPropertyChangeType : : Duplicate )
{
2023-08-29 07:48:32 -04:00
if ( ChangePropertyPath . IsPathExact ( SlotsPath ) )
2022-11-01 15:11:25 -04:00
{
2023-08-29 07:48:32 -04:00
const int32 SlotIndex = ChangePropertyPath . GetPropertyArrayIndex ( SlotsPath ) ;
if ( Slots . IsValidIndex ( SlotIndex ) )
2022-11-01 15:11:25 -04:00
{
2023-08-29 07:48:32 -04:00
FSmartObjectSlotDefinition & SlotDefinition = Slots [ SlotIndex ] ;
SlotDefinition . ID = FGuid : : NewGuid ( ) ;
SlotDefinition . SelectionPreconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
// Set new IDs to all duplicated data too
2023-12-04 07:36:52 -05:00
for ( FSmartObjectDefinitionDataProxy & DataProxy : SlotDefinition . DefinitionData )
2023-08-29 07:48:32 -04:00
{
DataProxy . ID = FGuid : : NewGuid ( ) ;
}
}
}
if ( ChangePropertyPath . IsPathExact ( SlotsDefinitionDataPath ) )
{
const int32 SlotIndex = ChangePropertyPath . GetPropertyArrayIndex ( SlotsPath ) ;
if ( Slots . IsValidIndex ( SlotIndex ) )
{
FSmartObjectSlotDefinition & SlotDefinition = Slots [ SlotIndex ] ;
const int32 DataIndex = ChangePropertyPath . GetPropertyArrayIndex ( SlotsDefinitionDataPath ) ;
if ( SlotDefinition . DefinitionData . IsValidIndex ( DataIndex ) )
{
2023-12-04 07:36:52 -05:00
FSmartObjectDefinitionDataProxy & DataProxy = SlotDefinition . DefinitionData [ DataIndex ] ;
2023-08-29 07:48:32 -04:00
DataProxy . ID = FGuid : : NewGuid ( ) ;
}
2022-11-01 15:11:25 -04:00
}
}
}
2024-01-29 06:26:17 -05:00
// Anything in the parameters change, notify.
if ( ChangePropertyPath . ContainsPath ( ParametersPath ) )
{
UpdateBindingPaths ( ) ;
UE : : SmartObject : : Delegates : : OnParametersChanged . Broadcast ( * this ) ;
}
2022-11-01 15:11:25 -04:00
// Anything in the slots changed, update references.
2023-08-29 07:48:32 -04:00
if ( ChangePropertyPath . ContainsPath ( SlotsPath ) )
2022-11-01 15:11:25 -04:00
{
UpdateSlotReferences ( ) ;
}
2022-11-04 06:56:50 -04:00
2022-11-17 07:44:24 -05:00
// If schema changes, update preconditions too.
2023-08-29 07:48:32 -04:00
if ( ChangePropertyPath . IsPathExact ( WorldConditionSchemaClassPath ) )
2022-11-01 15:11:25 -04:00
{
2022-11-17 07:44:24 -05:00
for ( FSmartObjectSlotDefinition & Slot : Slots )
2022-11-01 15:11:25 -04:00
{
2023-01-26 06:15:02 -05:00
Slot . SelectionPreconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
2023-03-02 12:12:47 -05:00
Slot . SelectionPreconditions . Initialize ( this ) ;
2022-11-01 15:11:25 -04:00
}
}
2022-11-04 06:56:50 -04:00
2024-01-29 06:26:17 -05:00
# if WITH_EDITOR
UpdateBindingDataHandles ( ) ;
# endif
2022-11-04 06:56:50 -04:00
Validate ( ) ;
2022-11-01 15:11:25 -04:00
}
void USmartObjectDefinition : : PreSave ( FObjectPreSaveContext SaveContext )
{
2022-11-17 07:44:24 -05:00
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
2023-03-02 12:12:47 -05:00
Slot . SelectionPreconditions . Initialize ( this ) ;
2022-11-17 07:44:24 -05:00
}
2022-11-01 15:11:25 -04:00
UpdateSlotReferences ( ) ;
Super : : PreSave ( SaveContext ) ;
2023-12-20 08:17:55 -05:00
# if WITH_EDITOR
2024-01-29 06:26:17 -05:00
UpdateBindingDataHandles ( ) ;
2023-12-20 08:17:55 -05:00
if ( SaveContext . IsCooking ( )
& & SaveContext . GetTargetPlatform ( ) - > IsClientOnly ( )
& & GetDefault < USmartObjectSettings > ( ) - > bShouldExcludePreConditionsOnDedicatedClient
& & ! HasAnyFlags ( RF_ArchetypeObject | RF_ClassDefaultObject ) )
{
FObjectSaveOverride ObjSaveOverride ;
// Add path to the conditions within the main definition
FProperty * OverrideProperty = FindFProperty < FProperty > ( GetClass ( ) , GET_MEMBER_NAME_CHECKED ( USmartObjectDefinition , Preconditions ) ) ;
check ( OverrideProperty ) ;
FPropertySaveOverride PropOverride ;
PropOverride . PropertyPath = FFieldPath ( OverrideProperty ) ;
PropOverride . bMarkTransient = true ;
ObjSaveOverride . PropOverrides . Add ( PropOverride ) ;
// Add path to the conditions within the slot definition struct
OverrideProperty = FindFProperty < FProperty > ( FSmartObjectSlotDefinition : : StaticStruct ( ) , GET_MEMBER_NAME_CHECKED ( FSmartObjectSlotDefinition , SelectionPreconditions ) ) ;
check ( OverrideProperty ) ;
PropOverride . PropertyPath = FFieldPath ( OverrideProperty ) ;
ObjSaveOverride . PropOverrides . Add ( PropOverride ) ;
SaveContext . AddSaveOverride ( this , ObjSaveOverride ) ;
}
# endif // WITH_EDITOR
2022-11-01 15:11:25 -04:00
}
void USmartObjectDefinition : : UpdateSlotReferences ( )
{
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
2023-12-04 07:36:52 -05:00
for ( FSmartObjectDefinitionDataProxy & DataProxy : Slot . DefinitionData )
2022-11-01 15:11:25 -04:00
{
2023-08-29 07:48:32 -04:00
if ( ! DataProxy . Data . IsValid ( ) )
2022-11-01 15:11:25 -04:00
{
continue ;
}
2023-08-29 07:48:32 -04:00
const UScriptStruct * ScriptStruct = DataProxy . Data . GetScriptStruct ( ) ;
uint8 * Memory = DataProxy . Data . GetMutableMemory ( ) ;
2022-11-01 15:11:25 -04:00
for ( TFieldIterator < FProperty > It ( ScriptStruct ) ; It ; + + It )
{
2023-01-18 10:52:51 -05:00
if ( const FStructProperty * StructProp = CastField < FStructProperty > ( * It ) )
2022-11-01 15:11:25 -04:00
{
if ( StructProp - > Struct = = TBaseStructure < FSmartObjectSlotReference > : : Get ( ) )
{
FSmartObjectSlotReference & Ref = * StructProp - > ContainerPtrToValuePtr < FSmartObjectSlotReference > ( Memory ) ;
const int32 Index = FindSlotByID ( Ref . GetSlotID ( ) ) ;
Ref . SetIndex ( Index ) ;
}
}
}
}
}
}
2024-01-29 06:26:17 -05:00
void USmartObjectDefinition : : UpdateBindingPaths ( )
{
for ( auto It = PropertyBindings . CreateIterator ( ) ; It ; + + It )
{
if ( ! UpdateAndValidatePath ( It - > TargetPath )
| | ! UpdateAndValidatePath ( It - > SourcePath ) )
{
It . RemoveCurrentSwap ( ) ;
}
}
ApplyParameters ( ) ;
}
bool USmartObjectDefinition : : UpdateAndValidatePath ( FPropertyBindingPath & Path )
{
FPropertyBindingDataView DataView ;
if ( ! GetDataViewByID ( Path . GetStructID ( ) , DataView ) )
{
return false ;
}
if ( ! Path . UpdateSegmentsFromValue ( DataView ) )
{
return false ;
}
return true ;
}
2022-11-17 07:44:24 -05:00
# endif // WITH_EDITOR
2024-01-29 06:26:17 -05:00
void USmartObjectDefinition : : PostInitProperties ( )
{
Super : : PostInitProperties ( ) ;
# if WITH_EDITOR
EnsureValidGuids ( ) ;
# endif
}
2022-11-17 07:44:24 -05:00
void USmartObjectDefinition : : PostLoad ( )
{
Super : : PostLoad ( ) ;
// Fill in missing world condition schema for old data.
if ( ! WorldConditionSchemaClass )
{
WorldConditionSchemaClass = GetDefault < USmartObjectSettings > ( ) - > DefaultWorldConditionSchemaClass ;
}
2023-01-18 10:52:51 -05:00
2023-01-26 06:15:02 -05:00
if ( Preconditions . GetSchemaClass ( ) . Get ( ) = = nullptr )
2023-01-18 10:52:51 -05:00
{
2023-01-26 06:15:02 -05:00
Preconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
2023-01-18 10:52:51 -05:00
}
# if WITH_EDITOR
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if ( ! ObjectTagFilter . IsEmpty ( ) )
{
FWorldCondition_SmartObjectActorTagQuery NewActorTagQueryCondition ;
NewActorTagQueryCondition . TagQuery = ObjectTagFilter ;
2023-01-26 06:15:02 -05:00
Preconditions . AddCondition ( FWorldConditionEditable ( 0 , EWorldConditionOperator : : And , FConstStructView : : Make ( NewActorTagQueryCondition ) ) ) ;
2023-01-18 10:52:51 -05:00
ObjectTagFilter . Clear ( ) ;
UE_ASSET_LOG ( LogSmartObject , Log , this , TEXT ( " Deprecated object tag filter has been replaced by a %s precondition to validate tags on the smart object actor. "
" If the intent was to validate against instance runtime tags then the condition should be replaced by %s. " ) ,
* FWorldCondition_SmartObjectActorTagQuery : : StaticStruct ( ) - > GetName ( ) ,
* FSmartObjectWorldConditionObjectTagQuery : : StaticStruct ( ) - > GetName ( ) ) ;
}
2023-03-27 08:22:19 -04:00
if ( PreviewClass_DEPRECATED . IsValid ( ) )
{
PreviewData . ObjectActorClass = PreviewClass_DEPRECATED ;
PreviewClass_DEPRECATED . Reset ( ) ;
}
if ( PreviewMeshPath_DEPRECATED . IsValid ( ) )
{
PreviewData . ObjectMeshPath = PreviewMeshPath_DEPRECATED ;
PreviewMeshPath_DEPRECATED . Reset ( ) ;
}
2023-08-29 07:48:32 -04:00
2024-01-29 06:26:17 -05:00
for ( TEnumerateRef < FSmartObjectSlotDefinition > Slot : EnumerateRange ( Slots ) )
2023-08-29 07:48:32 -04:00
{
2024-01-29 06:26:17 -05:00
if ( Slot - > Data_DEPRECATED . Num ( ) > 0 )
2023-08-29 07:48:32 -04:00
{
2024-01-29 06:26:17 -05:00
Slot - > DefinitionData . Reserve ( Slot - > Data_DEPRECATED . Num ( ) ) ;
2023-08-29 07:48:32 -04:00
2024-01-29 06:26:17 -05:00
for ( TEnumerateRef < const FInstancedStruct > Data : EnumerateRange ( Slot - > Data_DEPRECATED ) )
2023-08-29 07:48:32 -04:00
{
2024-01-29 06:26:17 -05:00
FSmartObjectDefinitionDataProxy & DataProxy = Slot - > DefinitionData . AddDefaulted_GetRef ( ) ;
DataProxy . Data . InitializeAsScriptStruct ( Data - > GetScriptStruct ( ) , Data - > GetMemory ( ) ) ;
2024-01-24 09:54:41 -05:00
2024-01-29 06:26:17 -05:00
static FName DataProxyName ( TEXT ( " DataProxy " ) ) ;
const uint32 Hashes [ ] = {
GetTypeHash ( DataProxyName ) ,
GetTypeHash ( Slot . GetIndex ( ) ) ,
GetTypeHash ( Data . GetIndex ( ) )
} ;
const uint64 Hash = CityHash64 ( ( const char * ) Hashes , sizeof Hashes ) ;
DataProxy . ID = FGuid : : NewDeterministicGuid ( GetPathName ( ) , Hash ) ;
}
Slot - > Data_DEPRECATED . Reset ( ) ;
2023-08-29 07:48:32 -04:00
}
}
2023-01-18 10:52:51 -05:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
2024-01-29 06:26:17 -05:00
EnsureValidGuids ( ) ;
UpdateBindingDataHandles ( ) ;
2023-01-18 10:52:51 -05:00
# endif
2023-03-02 12:12:47 -05:00
Preconditions . Initialize ( this ) ;
2022-11-17 07:44:24 -05:00
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
# if WITH_EDITOR
// Fill in missing slot ID for old data.
if ( ! Slot . ID . IsValid ( ) )
{
Slot . ID = FGuid : : NewGuid ( ) ;
}
# endif
// Fill in missing world condition schema for old data.
2023-01-26 06:15:02 -05:00
if ( Slot . SelectionPreconditions . GetSchemaClass ( ) . Get ( ) = = nullptr )
2022-11-17 07:44:24 -05:00
{
2023-01-26 06:15:02 -05:00
Slot . SelectionPreconditions . SetSchemaClass ( WorldConditionSchemaClass ) ;
2022-11-17 07:44:24 -05:00
}
2023-03-02 12:12:47 -05:00
Slot . SelectionPreconditions . Initialize ( this ) ;
2022-11-17 07:44:24 -05:00
}
# if WITH_EDITOR
UpdateSlotReferences ( ) ;
2024-01-29 06:26:17 -05:00
UpdateBindingPaths ( ) ;
2024-04-10 08:22:36 -04:00
# endif // WITH_EDITOR
2022-11-17 07:44:24 -05:00
Validate ( ) ;
}
2023-01-23 18:48:38 -05:00
2024-01-29 06:26:17 -05:00
USmartObjectDefinition * USmartObjectDefinition : : GetAssetVariation ( const FInstancedPropertyBag & VariationParameters )
{
// If no parameters, return this asset.
if ( ! VariationParameters . IsValid ( ) )
{
return this ;
}
// Remove unused variations
for ( auto It = Variations . CreateIterator ( ) ; It ; + + It )
{
if ( ! It - > DefinitionAsset . IsValid ( ) )
{
It . RemoveCurrentSwap ( ) ;
}
}
// Expect correct bag if provided.
UPropertyBag * VariationParametersBag = const_cast < UPropertyBag * > ( VariationParameters . GetPropertyBagStruct ( ) ) ;
if ( ! VariationParametersBag | | VariationParametersBag ! = Parameters . GetPropertyBagStruct ( ) )
{
UE_LOG ( LogSmartObject , Error , TEXT ( " %hs %s: Expecting matching variation parameters. " ) , __FUNCTION__ , * GetFullNameSafe ( this ) ) ;
return nullptr ;
}
// Calculate hash of the parameters, will be used to look up an existing variation.
TArray < uint8 > Data ;
FMemoryWriter Writer ( Data ) ;
FObjectAndNameAsStringProxyArchive WriterProxy ( Writer , /*bInLoadIfFindFails*/ true ) ;
VariationParametersBag - > SerializeItem ( WriterProxy , const_cast < uint8 * > ( VariationParameters . GetValue ( ) . GetMemory ( ) ) , /* Defaults */ nullptr ) ;
const uint64 VariationParametersHash = CityHash64 ( ( const char * ) Data . GetData ( ) , Data . Num ( ) ) ;
const FSmartObjectDefinitionAssetVariation * ExistingVariation = Variations . FindByPredicate ( [ VariationParametersHash ] ( const FSmartObjectDefinitionAssetVariation & Variation )
{
return Variation . ParametersHash = = VariationParametersHash ;
} ) ;
if ( ExistingVariation )
{
return ExistingVariation - > DefinitionAsset . Get ( ) ;
}
// Not the same, create a new one.
const FName UniqueName = MakeUniqueObjectName (
GetTransientPackage ( ) ,
USmartObjectDefinition : : StaticClass ( ) ,
FName ( FString : : Printf ( TEXT ( " %s_Var%llx " ) , * GetNameSafe ( this ) , VariationParametersHash ) )
) ;
USmartObjectDefinition * AssetVariation = DuplicateObject ( this , GetTransientPackage ( ) , UniqueName ) ;
check ( AssetVariation ) ;
// Apply parameters
AssetVariation - > Parameters = VariationParameters ;
AssetVariation - > ApplyParameters ( ) ;
// Keep track of variations.
Variations . Emplace ( AssetVariation , VariationParametersHash ) ;
return AssetVariation ;
}
void USmartObjectDefinition : : ApplyParameters ( )
{
// Do property copies
for ( const FSmartObjectDefinitionPropertyBinding & Binding : PropertyBindings )
{
FPropertyBindingDataView SourceDataView ;
if ( ! GetDataView ( Binding . SourceDataHandle , SourceDataView ) )
{
UE_LOG ( LogSmartObject , Error , TEXT ( " %hs %s: Could not find data view for property copy source %s. " ) , __FUNCTION__ , * GetFullNameSafe ( this ) , * Binding . SourcePath . ToString ( ) ) ;
continue ;
}
FPropertyBindingDataView TargetDataView ;
if ( ! GetDataView ( Binding . TargetDataHandle , TargetDataView ) )
{
UE_LOG ( LogSmartObject , Error , TEXT ( " %hs %s: Could not find data view for property copy target %s. " ) , __FUNCTION__ , * GetFullNameSafe ( this ) , * Binding . TargetPath . ToString ( ) ) ;
continue ;
}
CopyProperty ( SourceDataView , Binding . SourcePath , TargetDataView , Binding . TargetPath ) ;
}
}
bool USmartObjectDefinition : : CopyProperty ( FPropertyBindingDataView SourceDataView , const FPropertyBindingPath & SourcePath , FPropertyBindingDataView TargetDataView , const FPropertyBindingPath & TargetPath )
{
TArray < FPropertyBindingPathIndirection > SourceIndirections ;
if ( ! SourcePath . ResolveIndirectionsWithValue ( SourceDataView , SourceIndirections ) )
{
return false ;
}
TArray < FPropertyBindingPathIndirection > TargetIndirections ;
if ( ! TargetPath . ResolveIndirectionsWithValue ( TargetDataView , TargetIndirections ) )
{
return false ;
}
const FProperty * SourceLeafProperty = SourceIndirections . Last ( ) . GetProperty ( ) ;
const FProperty * TargetLeafProperty = TargetIndirections . Last ( ) . GetProperty ( ) ;
if ( ! SourceLeafProperty
| | ! TargetLeafProperty
| | ! ArePropertiesCompatible ( SourceLeafProperty , TargetLeafProperty ) )
{
return false ;
}
const void * SourceAddress = SourceIndirections . Last ( ) . GetPropertyAddress ( ) ;
void * TargetAddress = TargetIndirections . Last ( ) . GetMutablePropertyAddress ( ) ;
if ( SourceAddress & & TargetAddress )
{
TargetLeafProperty - > CopyCompleteValue ( TargetAddress , SourceAddress ) ;
}
return true ;
}
bool USmartObjectDefinition : : ArePropertiesCompatible ( const FProperty * SourceProperty , const FProperty * TargetProperty )
{
if ( SourceProperty = = TargetProperty )
{
return true ;
}
if ( SourceProperty = = nullptr | | TargetProperty = = nullptr )
{
return true ;
}
// Special case for object properties since InPropertyA->SameType(InPropertyB) requires both properties to be of the exact same class.
// In our case we want to be able to bind a source property if its class is a child of the target property class.
const FObjectPropertyBase * SourceObjectProperty = CastField < const FObjectPropertyBase > ( SourceProperty ) ;
const FObjectPropertyBase * TargetObjectProperty = CastField < const FObjectPropertyBase > ( TargetProperty ) ;
if ( SourceObjectProperty & & TargetObjectProperty )
{
return SourceObjectProperty - > PropertyClass - > IsChildOf ( TargetObjectProperty - > PropertyClass ) ;
}
if ( SourceProperty - > SameType ( TargetProperty ) )
{
return true ;
}
return false ;
}
# if WITH_EDITOR
void USmartObjectDefinition : : EnsureValidGuids ( )
{
if ( ! RootID . IsValid ( ) )
{
2024-03-07 08:03:24 -05:00
RootID = FGuid : : NewDeterministicGuid ( GetPathName ( ) , FCrc : : StrCrc32 < TCHAR > ( TEXT ( " RootID " ) ) ) ;
2024-01-29 06:26:17 -05:00
}
if ( ! ParametersID . IsValid ( ) )
{
2024-03-07 08:03:24 -05:00
ParametersID = FGuid : : NewDeterministicGuid ( GetPathName ( ) , FCrc : : StrCrc32 < TCHAR > ( TEXT ( " ParametersID " ) ) ) ;
2024-01-29 06:26:17 -05:00
}
}
void USmartObjectDefinition : : UpdateBindingDataHandles ( )
{
for ( FSmartObjectDefinitionPropertyBinding & Binding : PropertyBindings )
{
Binding . SourceDataHandle = GetDataHandleByID ( Binding . SourcePath . GetStructID ( ) ) ;
Binding . TargetDataHandle = GetDataHandleByID ( Binding . TargetPath . GetStructID ( ) ) ;
}
}
void USmartObjectDefinition : : AddPropertyBinding ( const FPropertyBindingPath & SourcePath , const FPropertyBindingPath & TargetPath )
{
FPropertyBindingPath ValidatedSourcePath = SourcePath ;
if ( ! UpdateAndValidatePath ( ValidatedSourcePath ) )
{
return ;
}
FPropertyBindingPath ValidatedTargetPath = TargetPath ;
if ( ! UpdateAndValidatePath ( ValidatedSourcePath ) )
{
return ;
}
RemovePropertyBindings ( TargetPath ) ;
PropertyBindings . Emplace ( ValidatedSourcePath , ValidatedTargetPath ) ;
UpdateBindingPaths ( ) ;
UpdateBindingDataHandles ( ) ;
}
void USmartObjectDefinition : : RemovePropertyBindings ( const FPropertyBindingPath & TargetPath )
{
PropertyBindings . RemoveAll ( [ & TargetPath ] ( const FSmartObjectDefinitionPropertyBinding & Binding )
{
return Binding . GetTargetPath ( ) = = TargetPath ;
} ) ;
UpdateBindingDataHandles ( ) ;
}
const FPropertyBindingPath * USmartObjectDefinition : : GetPropertyBindingSource ( const FPropertyBindingPath & TargetPath )
{
const FSmartObjectDefinitionPropertyBinding * Binding = PropertyBindings . FindByPredicate ( [ & TargetPath ] ( const FSmartObjectDefinitionPropertyBinding & Binding )
{
return Binding . GetTargetPath ( ) = = TargetPath ;
} ) ;
return Binding ? & Binding - > GetSourcePath ( ) : nullptr ;
}
void USmartObjectDefinition : : GetAccessibleStructs ( const FGuid TargetStructID , TArray < FBindableStructDesc > & OutStructDescs )
{
FBindableStructDesc & ParametersDesc = OutStructDescs . AddDefaulted_GetRef ( ) ;
ParametersDesc . Name = FName ( TEXT ( " Parameters " ) ) ;
ParametersDesc . ID = ParametersID ;
ParametersDesc . Struct = Parameters . GetPropertyBagStruct ( ) ;
}
bool USmartObjectDefinition : : GetDataViewByID ( const FGuid StructID , FPropertyBindingDataView & OutDataView )
{
if ( StructID = = ParametersID )
{
OutDataView = FPropertyBindingDataView ( Parameters . GetMutableValue ( ) ) ;
return true ;
}
if ( StructID = = RootID )
{
OutDataView = FPropertyBindingDataView ( this ) ;
return true ;
}
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
if ( StructID = = Slot . ID )
{
OutDataView = FPropertyBindingDataView ( FStructView : : Make ( Slot ) ) ;
return true ;
}
for ( FSmartObjectDefinitionDataProxy & DataProxy : Slot . DefinitionData )
{
if ( StructID = = DataProxy . ID )
{
OutDataView = FPropertyBindingDataView ( DataProxy . Data . GetScriptStruct ( ) , DataProxy . Data . GetMutableMemory ( ) ) ;
return true ;
}
}
}
return false ;
}
bool USmartObjectDefinition : : GetStructDescByID ( const FGuid StructID , FBindableStructDesc & OutDesc )
{
if ( StructID = = ParametersID )
{
OutDesc = FBindableStructDesc ( FName ( TEXT ( " Parameters " ) ) , Parameters . GetMutableValue ( ) . GetScriptStruct ( ) , ParametersID ) ;
return true ;
}
if ( StructID = = RootID )
{
OutDesc = FBindableStructDesc ( FName ( TEXT ( " Root " ) ) , StaticClass ( ) , RootID ) ;
return true ;
}
for ( FSmartObjectSlotDefinition & Slot : Slots )
{
if ( StructID = = Slot . ID )
{
OutDesc = FBindableStructDesc ( Slot . Name , TBaseStructure < FSmartObjectSlotDefinition > : : Get ( ) , Slot . ID ) ;
return true ;
}
for ( FSmartObjectDefinitionDataProxy & DataProxy : Slot . DefinitionData )
{
if ( StructID = = DataProxy . ID )
{
FString DataName = Slot . Name . ToString ( ) ;
const UScriptStruct * ScriptStruct = DataProxy . Data . GetScriptStruct ( ) ;
if ( ScriptStruct )
{
DataName + = TEXT ( " " ) ;
DataName + = ScriptStruct - > GetDisplayNameText ( ) . ToString ( ) ;
}
OutDesc = FBindableStructDesc ( FName ( DataName ) , ScriptStruct , DataProxy . ID ) ;
return true ;
}
}
}
return false ;
}
FSmartObjectDefinitionDataHandle USmartObjectDefinition : : GetDataHandleByID ( const FGuid StructID )
{
if ( StructID = = ParametersID )
{
return FSmartObjectDefinitionDataHandle : : Parameters ;
}
if ( StructID = = RootID )
{
return FSmartObjectDefinitionDataHandle : : Root ;
}
for ( const TEnumerateRef < const FSmartObjectSlotDefinition > Slot : EnumerateRange ( Slots ) )
{
if ( StructID = = Slot - > ID )
{
return FSmartObjectDefinitionDataHandle ( Slot . GetIndex ( ) ) ;
}
for ( const TEnumerateRef < const FSmartObjectDefinitionDataProxy > DataProxy : EnumerateRange ( Slot - > DefinitionData ) )
{
if ( StructID = = DataProxy - > ID )
{
return FSmartObjectDefinitionDataHandle ( Slot . GetIndex ( ) , DataProxy . GetIndex ( ) ) ;
}
}
}
return { } ;
}
FGuid USmartObjectDefinition : : GetDataRootID ( ) const
{
return RootID ;
}
bool USmartObjectDefinition : : AddParameterAndBindingFromPropertyPath ( const FPropertyBindingPath & TargetPath )
{
if ( TargetPath . IsPathEmpty ( ) )
{
return false ;
}
FPropertyBindingDataView TargetDataView ;
if ( ! GetDataViewByID ( TargetPath . GetStructID ( ) , TargetDataView ) )
{
return false ;
}
FBindableStructDesc TargetDesc ;
if ( ! GetStructDescByID ( TargetPath . GetStructID ( ) , TargetDesc ) )
{
return false ;
}
TArray < FPropertyBindingPathIndirection > TargetIndirections ;
if ( ! TargetPath . ResolveIndirectionsWithValue ( TargetDataView , TargetIndirections ) )
{
return false ;
}
// Add new property
const FProperty * TargetLeafProperty = TargetIndirections . Last ( ) . GetProperty ( ) ;
const FString NewNameString = TargetDesc . Name . ToString ( ) + TEXT ( " " ) + TargetLeafProperty - > GetDisplayNameText ( ) . ToString ( ) ;
const FName NewPropertyName ( NewNameString ) ;
Parameters . AddProperty ( NewPropertyName , TargetLeafProperty ) ;
const FPropertyBindingPath SourcePath ( ParametersID , NewPropertyName ) ;
// Copy the current value to the newly created parameter.
FPropertyBindingDataView SourceDataView ;
if ( GetDataViewByID ( SourcePath . GetStructID ( ) , SourceDataView ) )
{
// Note: source/target reversed intentionally.
CopyProperty ( TargetDataView , TargetPath , SourceDataView , SourcePath ) ;
}
// Add binding
AddPropertyBinding ( SourcePath , TargetPath ) ;
// Update UI
UE : : SmartObject : : Delegates : : OnParametersChanged . Broadcast ( * this ) ;
return true ;
}
# endif // WITH_EDITOR
bool USmartObjectDefinition : : GetDataView ( const FSmartObjectDefinitionDataHandle DataHandle , FPropertyBindingDataView & OutDataView )
{
if ( ! DataHandle . IsSlotValid ( ) )
{
return false ;
}
if ( DataHandle . IsParameters ( ) )
{
OutDataView = FPropertyBindingDataView ( Parameters . GetMutableValue ( ) ) ;
return true ;
}
if ( DataHandle . IsRoot ( ) )
{
OutDataView = FPropertyBindingDataView ( this ) ;
return true ;
}
const int32 SlotIndex = DataHandle . GetSlotIndex ( ) ;
if ( Slots . IsValidIndex ( SlotIndex ) )
{
FSmartObjectSlotDefinition & Slot = Slots [ SlotIndex ] ;
if ( DataHandle . IsDataValid ( ) )
{
// Slot data definition
const int32 DataDefinitionIndex = DataHandle . GetDataIndex ( ) ;
if ( Slot . DefinitionData . IsValidIndex ( DataDefinitionIndex ) )
{
FSmartObjectDefinitionDataProxy & DataProxy = Slot . DefinitionData [ DataDefinitionIndex ] ;
OutDataView = FPropertyBindingDataView ( DataProxy . Data . GetScriptStruct ( ) , DataProxy . Data . GetMutableMemory ( ) ) ;
return true ;
}
}
else
{
// Just a slot
OutDataView = FPropertyBindingDataView ( FStructView : : Make ( Slot ) ) ;
return true ;
}
}
return false ;
}
2023-01-23 18:48:38 -05:00
# undef LOCTEXT_NAMESPACE