// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "EntitySystem/MovieScenePropertySystemTypes.h" #include "EntitySystem/MovieScenePropertyMetaData.h" #include "EntitySystem/MovieSceneOperationalTypeConversions.h" #include "EntitySystem/MovieSceneIntermediatePropertyValue.h" #include "EntitySystem/MovieSceneVariantPropertyTypeIndex.h" #include "EntitySystem/MovieScenePropertySupport.h" #include "Channels/MovieSceneUnpackedChannelValues.h" #include "MovieSceneCommonHelpers.h" #include "Misc/GeneratedTypeName.h" #include "UObject/Class.h" #include "UObject/UnrealType.h" #include "UObject/TextProperty.h" #include "UObject/NoExportTypes.h" class UMovieSceneTrack; namespace UE { namespace MovieScene { template struct TVariantPropertyComponentHandler; template void UnpackChannelsFromOperational(T&&, const FProperty& Property, FUnpackedChannelValues&) { static_assert(!std::is_same_v, "Please implement this function for the necessary types to support unpacking the property type into its constituent channels"); } struct IPropertyTraits { using MetaDataType = TPropertyMetaData<>; virtual ~IPropertyTraits() { } virtual TSubclassOf GetTrackClass(const FProperty* InProperty) const { return nullptr; } virtual bool InitializeTrackFromProperty(UMovieSceneTrack* InTrack, const FProperty* InProperty) const { return false; } }; template struct TSupportedPropertyTypes { }; template struct TCompositeChannelMapping { }; /** * Property accessor traits that talk directly to the reflected UObject property type */ template struct TPropertyTraits : IPropertyTraits { static constexpr bool bIsComposite = bInIsComposite; static constexpr bool bNeedsConversion = !std::is_same_v; using TraitsType = TPropertyTraits; using StorageType = InMemoryType; using StorageTypeParam = typename TCallTraits::ParamType; /** IPropertyTraits interface */ FORCEINLINE static bool SupportsProperty(const FProperty& InProperty) { return TPropertyMatch::SupportsProperty(InProperty); } FORCEINLINE static FIntermediatePropertyValue CoercePropertyValue(const FProperty& InProperty, const FSourcePropertyValue& InPropertyValue) { StorageType Value; ConvertOperationalProperty(*InPropertyValue.Cast(), Value); return FIntermediatePropertyValue::FromValue(Value); } FORCEINLINE static void UnpackChannels(const InMemoryType& Value, const FProperty& Property, FUnpackedChannelValues& OutUnpackedValues) { UnpackChannelsFromOperational(Value, Property, OutUnpackedValues); } /** Property Value Getters */ FORCEINLINE static void GetObjectPropertyValue(const UObject* InObject, const FCustomPropertyAccessor& BaseCustomAccessor, StorageType& OutValue) { const TCustomPropertyAccessor& CustomAccessor = static_cast&>(BaseCustomAccessor); OutValue = (*CustomAccessor.Functions.Getter)(InObject); } FORCEINLINE static void GetObjectPropertyValue(const UObject* InObject, uint16 PropertyOffset, StorageType& OutValue) { const UObjectPropertyType* PropertyAddress = reinterpret_cast( reinterpret_cast(InObject) + PropertyOffset ); if constexpr (bNeedsConversion) { ConvertOperationalProperty(*PropertyAddress, OutValue); } else { OutValue = *PropertyAddress; } } FORCEINLINE static void GetObjectPropertyValue(const UObject* InObject, FTrackInstancePropertyBindings* PropertyBindings, StorageType& OutValue) { if constexpr (bNeedsConversion) { UObjectPropertyType Value = PropertyBindings->GetCurrentValue(*InObject); ConvertOperationalProperty(Value, OutValue); } else { OutValue = PropertyBindings->GetCurrentValue(*InObject); } } FORCEINLINE static void GetObjectPropertyValue(const UObject* InObject, const FName& PropertyPath, StorageType& OutValue) { TOptional Property = FTrackInstancePropertyBindings::StaticValue(InObject, *PropertyPath.ToString()); if (Property) { if constexpr (bNeedsConversion) { ConvertOperationalProperty(Property.GetValue(), OutValue); } else { OutValue = Property.GetValue(); } } } /** Property Value Setters */ FORCEINLINE static void SetObjectPropertyValue(UObject* InObject, const FCustomPropertyAccessor& BaseCustomAccessor, StorageTypeParam InValue) { const TCustomPropertyAccessor& CustomAccessor = static_cast&>(BaseCustomAccessor); (*CustomAccessor.Functions.Setter)(InObject, InValue); } FORCEINLINE static void SetObjectPropertyValue(UObject* InObject, uint16 PropertyOffset, StorageTypeParam InValue) { UObjectPropertyType* PropertyAddress = reinterpret_cast( reinterpret_cast(InObject) + PropertyOffset ); if constexpr (bNeedsConversion) { ConvertOperationalProperty(InValue, *PropertyAddress); } else { *PropertyAddress = InValue; } } FORCEINLINE static void SetObjectPropertyValue(UObject* InObject, FTrackInstancePropertyBindings* PropertyBindings, StorageTypeParam InValue) { if constexpr (bNeedsConversion) { UObjectPropertyType NewValue{}; ConvertOperationalProperty(InValue, NewValue); PropertyBindings->CallFunction(*InObject, NewValue); } else { PropertyBindings->CallFunction(*InObject, InValue); } } template FORCEINLINE static StorageType CombineComposites(T&&... InComposites) { return StorageType{ Forward(InComposites)... }; } }; template struct TDynamicVariantTraitsBase { using StorageTypeParam = typename TCallTraits::ParamType; using CastToOperationalPtr = void(*)(const void*, StorageType&); using CastToFinalPtr = void(*)(StorageTypeParam, void*); using RetrieveSlowPropertyPtr = void(*)(const UObject*, FTrackInstancePropertyBindings*, StorageType&); using ApplySlowPropertyPtr = void(*)(UObject*, FTrackInstancePropertyBindings*, StorageTypeParam); using InitializeNewTrackPtr = void(*)(UMovieSceneTrack*, const FProperty*); UScriptStruct* Struct = nullptr; TSubclassOf TrackClass = nullptr; CastToOperationalPtr CastToOperational = nullptr; CastToFinalPtr CastToFinal = nullptr; RetrieveSlowPropertyPtr RetrieveSlowProperty= nullptr; ApplySlowPropertyPtr ApplySlowProperty = nullptr; InitializeNewTrackPtr InitializeNewTrack = nullptr; }; template struct TDynamicVariantTraits : TDynamicVariantTraitsBase { using StorageTypeParam = typename TCallTraits::ParamType; using InitializeNewTrackPtr = void(*)(UMovieSceneTrack*, const FProperty*); TDynamicVariantTraits() { this->Struct = TBaseStructure::Get(); this->CastToOperational = CastToOperationalImpl; this->CastToFinal = CastToFinalImpl; this->RetrieveSlowProperty = RetrieveSlowPropertyImpl; this->ApplySlowProperty = ApplySlowPropertyImpl; } TDynamicVariantTraits(UScriptStruct* InStruct) { this->Struct = InStruct; this->CastToOperational = CastToOperationalImpl; this->CastToFinal = CastToFinalImpl; this->RetrieveSlowProperty = RetrieveSlowPropertyImpl; this->ApplySlowProperty = ApplySlowPropertyImpl; } TDynamicVariantTraits& SetTrackClass(TSubclassOf InTrackClass) { this->TrackClass = InTrackClass; return *this; } TDynamicVariantTraits& SetTrackInitializer(InitializeNewTrackPtr InFunc) { this->InitializeNewTrack = InFunc; return *this; } static void CastToOperationalImpl(const void* In, StorageType& OutValue) { const UObjectPropertyType* PropertyAddress = static_cast(In); ConvertOperationalProperty(*PropertyAddress, OutValue); } static void CastToFinalImpl(StorageTypeParam InValue, void* Out) { UObjectPropertyType* PropertyAddress = static_cast(Out); ConvertOperationalProperty(InValue, *PropertyAddress); } static void RetrieveSlowPropertyImpl(const UObject* InObject, FTrackInstancePropertyBindings* PropertyBindings, StorageType& OutValue) { UObjectPropertyType Value = PropertyBindings->GetCurrentValue(*InObject); ConvertOperationalProperty(Value, OutValue); } static void ApplySlowPropertyImpl(UObject* InObject, FTrackInstancePropertyBindings* PropertyBindings, StorageTypeParam InValue) { UObjectPropertyType NewValue{}; ConvertOperationalProperty(InValue, NewValue); PropertyBindings->CallFunction(*InObject, NewValue); } }; template struct TVariantPropertyTraits : IPropertyTraits { static constexpr int32 CompileTimeNum = sizeof...(UObjectPropertyTypes); static constexpr bool bIsComposite = true; using StorageType = InMemoryType; using StorageTypeParam = typename TCallTraits::ParamType; using MetaDataType = TPropertyMetaData; using PublicMetaData = TPropertyMetaData<>; using TraitsType = TVariantPropertyTraits; static bool SupportsProperty(const FProperty& InProperty) { FVariantPropertyTypeIndex Unused; return ComputeVariantIndex(InProperty, Unused); } static bool ComputeVariantIndex(const FProperty& Property, FVariantPropertyTypeIndex& OutTypeIndex) { using FuncDef = bool (*)(const FProperty& InProperty); static constexpr FuncDef Funcs[] = { &TPropertyMatch::SupportsProperty... }; for (int32 Index = 0; Index < CompileTimeNum; ++Index) { if (Funcs[Index](Property)) { OutTypeIndex.Index = Index; return true; } } return false; } static FIntermediatePropertyValue CoercePropertyValue(const FProperty& InProperty, const FSourcePropertyValue& InPropertyValue) { FVariantPropertyTypeIndex VariantIndex; const bool bIsSupported = ComputeVariantIndex(InProperty, VariantIndex); check(bIsSupported); using FuncDef = FIntermediatePropertyValue (*)(const FProperty& InProperty, const FSourcePropertyValue& InPropertyValue); static constexpr FuncDef Funcs[] = { &TPropertyTraits::CoercePropertyValue... }; return CoercePropertyValueChecked(InProperty, VariantIndex, InPropertyValue); } static FIntermediatePropertyValue CoercePropertyValueChecked(const FProperty& InProperty, FVariantPropertyTypeIndex VariantIndex, const FSourcePropertyValue& InPropertyValue) { using FuncDef = FIntermediatePropertyValue (*)(const FProperty& InProperty, const FSourcePropertyValue& InPropertyValue); static constexpr FuncDef Funcs[] = { &TPropertyTraits::CoercePropertyValue... }; return Funcs[VariantIndex.Index](InProperty, InPropertyValue); } static void UnpackChannels(const InMemoryType& Value, const FProperty& Property, FUnpackedChannelValues& OutUnpackedValues) { UnpackChannelsFromOperational(Value, Property, OutUnpackedValues); } template static TVariantPropertyComponentHandler MakeHandler() { return TVariantPropertyComponentHandler(); } static bool NeedsMetaData() { return sizeof...(UObjectPropertyTypes) > 1; } static void GetObjectPropertyValue(const UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, const FCustomPropertyAccessor& BaseCustomAccessor, StorageType& OutValue) { const TCustomPropertyAccessor& CustomAccessor = static_cast&>(BaseCustomAccessor); OutValue = (*CustomAccessor.Functions.Getter)(InObject); } static void GetObjectPropertyValue(const UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, uint16 PropertyOffset, StorageType& OutValue) { using FuncDef = void (*)(const UObject*, uint16, StorageType&); static constexpr FuncDef Funcs[] = { &TPropertyTraits::GetObjectPropertyValue... }; if (VariantTypeIndex.Index >= 0 && VariantTypeIndex.Index < CompileTimeNum) { Funcs[VariantTypeIndex.Index](InObject, PropertyOffset, OutValue); } } static void GetObjectPropertyValue(const UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, FTrackInstancePropertyBindings* PropertyBindings, StorageType& OutValue) { using FuncDef = void (*)(const UObject*, FTrackInstancePropertyBindings*, StorageType&); static constexpr FuncDef Funcs[] = { &TPropertyTraits::GetObjectPropertyValue... }; if (VariantTypeIndex.Index >= 0 && VariantTypeIndex.Index < CompileTimeNum) { Funcs[VariantTypeIndex.Index](InObject, PropertyBindings, OutValue); } } static void SetObjectPropertyValue(UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, const FCustomPropertyAccessor& BaseCustomAccessor, StorageTypeParam InValue) { const TCustomPropertyAccessor& CustomAccessor = static_cast&>(BaseCustomAccessor); (*CustomAccessor.Functions.Setter)(InObject, InValue); } static void SetObjectPropertyValue(UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, uint16 PropertyOffset, StorageTypeParam InValue) { using FuncDef = void (*)(UObject*, uint16, StorageTypeParam); static constexpr FuncDef Funcs[] = { &TPropertyTraits::SetObjectPropertyValue... }; if (VariantTypeIndex.Index >= 0 && VariantTypeIndex.Index < CompileTimeNum) { Funcs[VariantTypeIndex.Index](InObject, PropertyOffset, InValue); } } static void SetObjectPropertyValue(UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, FTrackInstancePropertyBindings* PropertyBindings, StorageTypeParam InValue) { using FuncDef = void (*)(UObject*, FTrackInstancePropertyBindings*, StorageTypeParam); static constexpr FuncDef Funcs[] = { &TPropertyTraits::SetObjectPropertyValue... }; if (VariantTypeIndex.Index >= 0 && VariantTypeIndex.Index < CompileTimeNum) { Funcs[VariantTypeIndex.Index](InObject, PropertyBindings, InValue); } } template static StorageType CombineComposites(FVariantPropertyTypeIndex VariantTypeIndex, T&&... InComposites) { return StorageType{ Forward(InComposites)... }; } }; template struct TDynamicVariantPropertyTraits : IPropertyTraits { using StaticTraits = TVariantPropertyTraits; using TraitsType = TDynamicVariantPropertyTraits; using StorageType = InMemoryType; using StorageTypeParam = typename TCallTraits::ParamType; using MetaDataType = TPropertyMetaData; using PublicMetaData = TPropertyMetaData<>; static constexpr bool bIsComposite = true; TArray> DynamicTraits; bool NeedsMetaData() const { return sizeof...(UObjectPropertyTypes) + DynamicTraits.Num() > 1; } static void UnpackChannels(const InMemoryType& Value, const FProperty& Property, FUnpackedChannelValues& OutUnpackedValues) { StaticTraits::UnpackChannels(Value, Property, OutUnpackedValues); } template static TVariantPropertyComponentHandler MakeHandler() { return TVariantPropertyComponentHandler(); } virtual TSubclassOf GetTrackClass(const FProperty* InProperty) const override { if (const FStructProperty* StructProperty = CastField(InProperty)) { const TDynamicVariantTraitsBase* Variant = Algo::FindBy(DynamicTraits, StructProperty->Struct, &TDynamicVariantTraitsBase::Struct); if (Variant) { return Variant->TrackClass; } } return nullptr; } virtual bool InitializeTrackFromProperty(UMovieSceneTrack* InTrack, const FProperty* InProperty) const override { if (const FStructProperty* StructProperty = CastField(InProperty)) { const TDynamicVariantTraitsBase* Variant = Algo::FindBy(DynamicTraits, StructProperty->Struct, &TDynamicVariantTraitsBase::Struct); if (Variant && Variant->InitializeNewTrack) { (*Variant->InitializeNewTrack)(InTrack, InProperty); return true; } } return false; } bool ComputeVariantIndex(const FProperty& InProperty, FVariantPropertyTypeIndex& OutTypeIndex) const { if (StaticTraits::ComputeVariantIndex(InProperty, OutTypeIndex)) { return true; } if (const FStructProperty* StructProperty = CastField(&InProperty)) { for (int32 DynamicIndex = 0; DynamicIndex < DynamicTraits.Num(); ++DynamicIndex) { if (StructProperty->Struct == DynamicTraits[DynamicIndex].Struct) { OutTypeIndex.Index = StaticTraits::CompileTimeNum + DynamicIndex; return true; } } } return false; } bool SupportsProperty(const FProperty& InProperty) const { FVariantPropertyTypeIndex Unused; return ComputeVariantIndex(InProperty, Unused); } FIntermediatePropertyValue CoercePropertyValue(const FProperty& InProperty, const FSourcePropertyValue& InPropertyValue) const { FVariantPropertyTypeIndex StaticVariantIndex; if (StaticTraits::ComputeVariantIndex(InProperty, StaticVariantIndex)) { return StaticTraits::CoercePropertyValueChecked(InProperty, StaticVariantIndex, InPropertyValue); } check(DynamicTraits.Num() > 0); const FStructProperty* StructProperty = CastFieldChecked(&InProperty); for (int32 DynamicIndex = 0; DynamicIndex < DynamicTraits.Num()-1; ++DynamicIndex) { if (StructProperty->Struct == DynamicTraits[DynamicIndex].Struct) { StorageType NewValue; DynamicTraits[DynamicIndex].CastToOperational(InPropertyValue.Get(), NewValue); return FIntermediatePropertyValue::FromValue(NewValue); } } check(StructProperty->Struct == DynamicTraits.Last().Struct); StorageType NewValue; DynamicTraits.Last().CastToOperational(InPropertyValue.Get(), NewValue); return FIntermediatePropertyValue::FromValue(NewValue); } void AddDynamicType(const TDynamicVariantTraitsBase& Variant) { DynamicTraits.Emplace(Variant); } void GetObjectPropertyValue(const UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, uint16 PropertyOffset, StorageType& OutValue) const { if (VariantTypeIndex.Index >= StaticTraits::CompileTimeNum) { const int32 DynamicIndex = int32(VariantTypeIndex.Index) - StaticTraits::CompileTimeNum; if (DynamicTraits.IsValidIndex(DynamicIndex)) { const void* PropertyAddress = reinterpret_cast(InObject) + PropertyOffset; DynamicTraits[DynamicIndex].CastToOperational(PropertyAddress, OutValue); } } else { StaticTraits::GetObjectPropertyValue(InObject, VariantTypeIndex, PropertyOffset, OutValue); } } void GetObjectPropertyValue(const UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, FTrackInstancePropertyBindings* PropertyBindings, StorageType& OutValue) const { if (VariantTypeIndex.Index >= StaticTraits::CompileTimeNum) { const int32 DynamicIndex = int32(VariantTypeIndex.Index) - StaticTraits::CompileTimeNum; if (DynamicTraits.IsValidIndex(DynamicIndex)) { DynamicTraits[DynamicIndex].RetrieveSlowProperty(InObject, PropertyBindings, OutValue); } } else { StaticTraits::GetObjectPropertyValue(InObject, VariantTypeIndex, PropertyBindings, OutValue); } } static void GetObjectPropertyValue(const UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, const FCustomPropertyAccessor& BaseCustomAccessor, StorageType& OutValue) { const TCustomPropertyAccessor& CustomAccessor = static_cast&>(BaseCustomAccessor); OutValue = (*CustomAccessor.Functions.Getter)(InObject); } void SetObjectPropertyValue(UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, uint16 PropertyOffset, StorageTypeParam InValue) const { if (VariantTypeIndex.Index >= StaticTraits::CompileTimeNum) { const int32 DynamicIndex = int32(VariantTypeIndex.Index) - StaticTraits::CompileTimeNum; if (DynamicTraits.IsValidIndex(DynamicIndex)) { void* PropertyAddress = reinterpret_cast(InObject) + PropertyOffset; DynamicTraits[DynamicIndex].CastToFinal(InValue, PropertyAddress); } } else { StaticTraits::SetObjectPropertyValue(InObject, VariantTypeIndex, PropertyOffset, InValue); } } void SetObjectPropertyValue(UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, FTrackInstancePropertyBindings* PropertyBindings, StorageTypeParam InValue) const { if (VariantTypeIndex.Index >= StaticTraits::CompileTimeNum) { const int32 DynamicIndex = int32(VariantTypeIndex.Index) - StaticTraits::CompileTimeNum; if (DynamicTraits.IsValidIndex(DynamicIndex)) { DynamicTraits[DynamicIndex].ApplySlowProperty(InObject, PropertyBindings, InValue); } } else { StaticTraits::SetObjectPropertyValue(InObject, VariantTypeIndex, PropertyBindings, InValue); } } static void SetObjectPropertyValue(UObject* InObject, FVariantPropertyTypeIndex VariantTypeIndex, const FCustomPropertyAccessor& BaseCustomAccessor, StorageTypeParam InValue) { const TCustomPropertyAccessor& CustomAccessor = static_cast&>(BaseCustomAccessor); (*CustomAccessor.Functions.Setter)(InObject, InValue); } template static StorageType CombineComposites(FVariantPropertyTypeIndex VariantTypeIndex, T&&... InComposites) { return StorageType{ Forward(InComposites)... }; } }; /** * Property accessor traits that do not know the underlying UObjectPropertyType until runtime */ template struct TRuntimePropertyTraits { using StorageType = RuntimeType; using StorageTypeParam = typename TCallTraits::ParamType; using MetaDataType = TPropertyMetaData; /** Property Value Getters */ static void GetObjectPropertyValue(const UObject* InObject, const FCustomPropertyAccessor& BaseCustomAccessor, typename TCallTraits::ParamType... MetaData, StorageType& OutValue) {} static void GetObjectPropertyValue(const UObject* InObject, uint16 PropertyOffset, typename TCallTraits::ParamType... MetaData, StorageType& OutValue) {} static void GetObjectPropertyValue(const UObject* InObject, FTrackInstancePropertyBindings* PropertyBindings, typename TCallTraits::ParamType... MetaData, StorageType& OutValue) {} static void GetObjectPropertyValue(const UObject* InObject, const FName& PropertyPath, StorageType& OutValue) {} /** Property Value Setters */ static void SetObjectPropertyValue(UObject* InObject, const FCustomPropertyAccessor& BaseCustomAccessor, typename TCallTraits::ParamType... MetaData, StorageTypeParam InValue) {} static void SetObjectPropertyValue(UObject* InObject, uint16 PropertyOffset, typename TCallTraits::ParamType... MetaData, StorageTypeParam InValue) {} static void SetObjectPropertyValue(UObject* InObject, FTrackInstancePropertyBindings* PropertyBindings, typename TCallTraits::ParamType... MetaData, StorageTypeParam InValue) {} }; template using TDirectPropertyTraits = TPropertyTraits; template struct UE_DEPRECATED(5.4, "Please use TPropertyTraits directly.") TIndirectPropertyTraits : TPropertyTraits {}; } // namespace MovieScene } // namespace UE