Files
UnrealEngine/Engine/Plugins/Runtime/SmartObjects/Source/SmartObjectsModule/Private/SmartObjectDefinitionReference.cpp
2025-05-18 13:04:45 +08:00

170 lines
5.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SmartObjectDefinitionReference.h"
#include "SmartObjectDefinition.h"
#include "VisualLogger/VisualLogger.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectDefinitionReference)
USmartObjectDefinition* FSmartObjectDefinitionReference::GetAssetVariation(UWorld* World) const
{
USmartObjectDefinition* BaseDefinitionAsset = const_cast<USmartObjectDefinition*>(GetSmartObjectDefinition());
return BaseDefinitionAsset != nullptr ? BaseDefinitionAsset->GetAssetVariation(GetParameters(), World) : nullptr;
}
void FSmartObjectDefinitionReference::SyncParameters()
{
if (SmartObjectDefinition == nullptr)
{
Parameters.Reset();
}
else
{
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Verbose, TEXT("%hs for '%s'")
, __FUNCTION__, *GetNameSafe(SmartObjectDefinition));
// In editor builds, sync with overrides.
Parameters.MigrateToNewBagInstanceWithOverrides(SmartObjectDefinition->GetDefaultParameters(), PropertyOverrides);
// Remove overrides that do not exist anymore
if (!PropertyOverrides.IsEmpty())
{
if (const UPropertyBag* Bag = Parameters.GetPropertyBagStruct())
{
for (TArray<FGuid>::TIterator It = PropertyOverrides.CreateIterator(); It; ++It)
{
if (!Bag->FindPropertyDescByID(*It))
{
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Verbose, TEXT("%hs removed override for Guid: '%s'")
, __FUNCTION__, *LexToString(*It));
It.RemoveCurrentSwap();
}
}
}
}
}
}
bool FSmartObjectDefinitionReference::RequiresParametersSync() const
{
bool bShouldSync = false;
if (SmartObjectDefinition)
{
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Log, TEXT("%hs for '%s'")
, __FUNCTION__, *GetNameSafe(SmartObjectDefinition));
const FInstancedPropertyBag& DefaultParameters = SmartObjectDefinition->GetDefaultParameters();
const UPropertyBag* DefaultParametersBag = DefaultParameters.GetPropertyBagStruct();
const UPropertyBag* ParametersBag = Parameters.GetPropertyBagStruct();
// Mismatching property bags, needs sync.
if (DefaultParametersBag != ParametersBag)
{
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Log, TEXT("%hs - sync required: mismatching property bags DefaultParameters '%s' vs Parameters '%s'")
, __FUNCTION__, *GetNameSafe(DefaultParametersBag), *GetNameSafe(ParametersBag));
bShouldSync = true;
}
else if (ParametersBag && DefaultParametersBag)
{
// Check if non-overridden parameters are not identical, needs sync.
const uint8* SourceAddress = DefaultParameters.GetValue().GetMemory();
const uint8* TargetAddress = Parameters.GetValue().GetMemory();
check(SourceAddress);
check(TargetAddress);
for (const FPropertyBagPropertyDesc& Desc : ParametersBag->GetPropertyDescs())
{
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Verbose, TEXT("%hs - processing property '%s'"), __FUNCTION__, *Desc.Name.ToString());
// Skip overridden
if (PropertyOverrides.Contains(Desc.ID))
{
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Verbose, TEXT("%hs - skipped since it is overridden"), __FUNCTION__, *Desc.Name.ToString());
continue;
}
const uint8* SourceValueAddress = SourceAddress + Desc.CachedProperty->GetOffset_ForInternal();
const uint8* TargetValueAddress = TargetAddress + Desc.CachedProperty->GetOffset_ForInternal();
if (!Desc.CachedProperty->Identical(SourceValueAddress, TargetValueAddress))
{
FString Details;
UE_SUPPRESS(LogSmartObject, Log,
{
FString SourceValueString;
FString TargetValueString;
Desc.CachedProperty->ExportTextItem_Direct(SourceValueString, SourceValueAddress, /*Default*/nullptr, /*Parent*/nullptr, PPF_None);
Desc.CachedProperty->ExportTextItem_Direct(TargetValueString, TargetValueAddress, /*Default*/nullptr, /*Parent*/nullptr, PPF_None);
Details = FString::Printf(TEXT(": '%s' vs '%s'"), *SourceValueString, *TargetValueString);
})
// Mismatching values, should sync.
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Log, TEXT("%hs - sync required: mismatching values for property '%s'%s")
, __FUNCTION__, *Desc.Name.ToString(), *Details);
bShouldSync = true;
break;
}
}
}
}
else
{
// Empty definition reference should not have parameters
bShouldSync = Parameters.IsValid();
}
return bShouldSync;
}
void FSmartObjectDefinitionReference::ConditionallySyncParameters() const
{
if (RequiresParametersSync())
{
FSmartObjectDefinitionReference* NonConstThis = const_cast<FSmartObjectDefinitionReference*>(this);
NonConstThis->SyncParameters();
UE_VLOG_UELOG(SmartObjectDefinition, LogSmartObject, Log, TEXT("%hs: Parameters for '%s' stored in SmartObjectDefinitionReference were auto-fixed to be usable at runtime.")
, __FUNCTION__, *GetNameSafe(SmartObjectDefinition));
}
}
void FSmartObjectDefinitionReference::SetPropertyOverridden(const FGuid PropertyID, const bool bIsOverridden)
{
if (bIsOverridden)
{
PropertyOverrides.AddUnique(PropertyID);
}
else
{
PropertyOverrides.Remove(PropertyID);
ConditionallySyncParameters();
}
}
bool FSmartObjectDefinitionReference::SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot)
{
// Serialize from an object pointer.
if (Tag.Type == NAME_ObjectProperty)
{
Slot << SmartObjectDefinition;
return true;
}
return false;
}
uint32 GetTypeHash(const FSmartObjectDefinitionReference& DefinitionReference)
{
const USmartObjectDefinition* Definition = DefinitionReference.GetSmartObjectDefinition();
if (Definition == nullptr)
{
return 0;
}
const uint32 AssetPathHash = GetTypeHash(FSoftObjectPath(Definition).GetAssetPathString());
const uint32 ParametersHash = GetTypeHash(USmartObjectDefinition::GetVariationParametersHash(DefinitionReference.GetParameters()));
const uint32 OverridesHash = GetTypeHash(DefinitionReference.PropertyOverrides);
return HashCombine(AssetPathHash, HashCombine(ParametersHash, OverridesHash));
}