// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Delegates/DelegateCombinations.h" #include "EntitySystem/MovieSceneEntityIDs.h" #include "EntitySystem/MovieScenePropertyRegistry.h" #include "EntitySystem/MovieScenePartialProperties.inl" #include "EntitySystem/MovieSceneDecompositionQuery.h" #include "EntitySystem/MovieSceneBlenderSystem.h" #include "EntitySystem/MovieSceneInitialValueCache.h" #include "EntitySystem/MovieScenePropertySystemTypes.inl" #include "EntitySystem/MovieSceneOperationalTypeConversions.h" #include "EntitySystem/Interrogation/MovieSceneInterrogationExtension.h" #include "EntitySystem/Interrogation/MovieSceneInterrogationLinker.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedStateExtension.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedStateStorage.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedPropertyStorage.h" namespace UE { namespace MovieScene { template struct TPropertyComponentHandlerImpl; template struct TPropertyComponentHandler : TPropertyComponentHandlerImpl, TMakeIntegerSequence, CompositeTypes...> { }; template struct TInitialValueProcessorImpl; template struct TInitialValueProcessorImpl, TIntegerSequence> : IInitialValueProcessor { using StorageType = typename PropertyTraits::StorageType; TSortedMap ValuesByChannel; FBuiltInComponentTypes* BuiltInComponents; ICustomPropertyRegistration* CustomPropertyRegistration; TComponentTypeID InitialValueType; TTuple...> MetaDataComponents; FCustomAccessorView CustomAccessors; // Transient properties reset on each usage. IInterrogationExtension* Interrogation; TPropertyValueStorage* CacheStorage; FEntityAllocationWriteContext WriteContext; TInitialValueProcessorImpl( TComponentTypeID InInitialValueType, TArrayView InMetaDataComponents, ICustomPropertyRegistration* InCustomPropertyRegistration ) : BuiltInComponents(FBuiltInComponentTypes::Get()) , CustomPropertyRegistration(InCustomPropertyRegistration) , InitialValueType(InInitialValueType) , MetaDataComponents(InMetaDataComponents[MetaDataIndices].ReinterpretCast()...) , WriteContext(FEntityAllocationWriteContext::NewAllocation()) { Interrogation = nullptr; CacheStorage = nullptr; } virtual void Initialize(UMovieSceneEntitySystemLinker* Linker, FInitialValueCache* InitialValueCache) override { Interrogation = Linker->FindExtension(); WriteContext = FEntityAllocationWriteContext(Linker->EntityManager); if (CustomPropertyRegistration) { CustomAccessors = CustomPropertyRegistration->GetAccessors(); } if (InitialValueCache) { CacheStorage = InitialValueCache->GetStorage(InitialValueType); } } virtual void PopulateFilter(FEntityComponentFilter& OutFilter) const override { // These initializers must have bound objects or an interrogation key OutFilter.Any({ BuiltInComponents->BoundObject, BuiltInComponents->Interrogation.OutputKey }); } virtual void Process(const FEntityAllocation* Allocation, const FComponentMask& AllocationType) override { if (Interrogation && AllocationType.Contains(BuiltInComponents->Interrogation.OutputKey)) { VisitInterrogationAllocation(Allocation); } else if (CacheStorage) { VisitAllocationCached(Allocation); } else { VisitAllocation(Allocation); } } virtual void Finalize() override { ValuesByChannel.Empty(); Interrogation = nullptr; CacheStorage = nullptr; } void VisitAllocation(const FEntityAllocation* Allocation) { const int32 Num = Allocation->Num(); TComponentWriter InitialValues = Allocation->WriteComponents(InitialValueType, WriteContext); TComponentReader BoundObjects = Allocation->ReadComponents(BuiltInComponents->BoundObject); TTuple< TComponentReader... > MetaData( Allocation->ReadComponents(MetaDataComponents.template Get())... ); if (TOptionalComponentReader CustomIndices = Allocation->TryReadComponents(BuiltInComponents->CustomPropertyIndex)) { const FCustomPropertyIndex* RawIndices = CustomIndices.AsPtr(); for (int32 Index = 0; Index < Num; ++Index) { PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get()[Index]..., CustomAccessors[RawIndices[Index].Value], InitialValues[Index]); } } else if (TOptionalComponentReader FastOffsets = Allocation->TryReadComponents(BuiltInComponents->FastPropertyOffset)) { const uint16* RawOffsets = FastOffsets.AsPtr(); for (int32 Index = 0; Index < Num; ++Index) { PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get()[Index]..., RawOffsets[Index], InitialValues[Index]); } } else if (TOptionalComponentReader> SlowProperties = Allocation->TryReadComponents(BuiltInComponents->SlowProperty)) { const TSharedPtr* RawProperties = SlowProperties.AsPtr(); for (int32 Index = 0; Index < Num; ++Index) { PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get()[Index]..., RawProperties[Index].Get(), InitialValues[Index]); } } } void VisitAllocationCached(const FEntityAllocation* Allocation) { const int32 Num = Allocation->Num(); TComponentWriter InitialValueIndices = Allocation->WriteComponents(BuiltInComponents->InitialValueIndex, WriteContext); TComponentWriter InitialValues = Allocation->WriteComponents(InitialValueType, WriteContext); TComponentReader BoundObjects = Allocation->ReadComponents(BuiltInComponents->BoundObject); TTuple< TComponentReader... > MetaData( Allocation->ReadComponents(MetaDataComponents.template Get())... ); if (TOptionalComponentReader CustomIndices = Allocation->TryReadComponents(BuiltInComponents->CustomPropertyIndex)) { const FCustomPropertyIndex* RawIndices = CustomIndices.AsPtr(); for (int32 Index = 0; Index < Num; ++Index) { TOptional ExistingIndex = CacheStorage->FindPropertyIndex(BoundObjects[Index], RawIndices[Index]); if (ExistingIndex) { InitialValues[Index] = CacheStorage->GetCachedValue(ExistingIndex.GetValue()); } else { StorageType Value{}; PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get()[Index]..., CustomAccessors[RawIndices[Index].Value], Value); InitialValues[Index] = Value; InitialValueIndices[Index] = CacheStorage->AddInitialValue(BoundObjects[Index], Value, RawIndices[Index]); } } } else if (TOptionalComponentReader FastOffsets = Allocation->TryReadComponents(BuiltInComponents->FastPropertyOffset)) { const uint16* RawOffsets = FastOffsets.AsPtr(); for (int32 Index = 0; Index < Num; ++Index) { TOptional ExistingIndex = CacheStorage->FindPropertyIndex(BoundObjects[Index], FastOffsets[Index]); if (ExistingIndex) { InitialValues[Index] = CacheStorage->GetCachedValue(ExistingIndex.GetValue()); } else { StorageType Value{}; PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get()[Index]..., RawOffsets[Index], Value); InitialValues[Index] = Value; InitialValueIndices[Index] = CacheStorage->AddInitialValue(BoundObjects[Index], Value, RawOffsets[Index]); } } } else if (TOptionalComponentReader> SlowProperties = Allocation->TryReadComponents(BuiltInComponents->SlowProperty)) { const TSharedPtr* RawProperties = SlowProperties.AsPtr(); for (int32 Index = 0; Index < Num; ++Index) { TOptional ExistingIndex = CacheStorage->FindPropertyIndex(BoundObjects[Index], *RawProperties[Index]->GetPropertyPath()); if (ExistingIndex) { InitialValues[Index] = CacheStorage->GetCachedValue(ExistingIndex.GetValue()); } else { StorageType Value{}; PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get()[Index]..., RawProperties[Index].Get(), Value); InitialValues[Index] = Value; InitialValueIndices[Index] = CacheStorage->AddInitialValue(BoundObjects[Index], Value, RawProperties[Index].Get()); } } } } void VisitInterrogationAllocation(const FEntityAllocation* Allocation) { const int32 Num = Allocation->Num(); TComponentWriter InitialValues = Allocation->WriteComponents(InitialValueType, WriteContext); TComponentReader OutputKeys = Allocation->ReadComponents(BuiltInComponents->Interrogation.OutputKey); TTuple< TComponentReader... > MetaData( Allocation->ReadComponents(MetaDataComponents.template Get())... ); const FSparseInterrogationChannelInfo& SparseChannelInfo = Interrogation->GetSparseChannelInfo(); for (int32 Index = 0; Index < Num; ++Index) { FInterrogationChannel Channel = OutputKeys[Index].Channel; // Did we already cache this value? if (const StorageType* CachedValue = ValuesByChannel.Find(Channel)) { InitialValues[Index] = *CachedValue; continue; } const FInterrogationChannelInfo* ChannelInfo = SparseChannelInfo.Find(Channel); UObject* Object = ChannelInfo ? ChannelInfo->WeakObject.Get() : nullptr; if (!ChannelInfo || !Object || ChannelInfo->PropertyBinding.PropertyName.IsNone()) { continue; } TOptional< FResolvedFastProperty > Property = FPropertyRegistry::ResolveFastProperty(Object, ChannelInfo->PropertyBinding, CustomAccessors); // Retrieve a cached value if possible if (CacheStorage) { const StorageType* CachedValue = nullptr; if (!Property.IsSet()) { CachedValue = CacheStorage->FindCachedValue(Object, ChannelInfo->PropertyBinding.PropertyPath); } else if (const FCustomPropertyIndex* CustomIndex = Property->TryGet()) { CachedValue = CacheStorage->FindCachedValue(Object, *CustomIndex); } else { CachedValue = CacheStorage->FindCachedValue(Object, Property->Get()); } if (CachedValue) { InitialValues[Index] = *CachedValue; ValuesByChannel.Add(Channel, *CachedValue); continue; } } // No cached value available, must retrieve it now TOptional CurrentValue; if (!Property.IsSet()) { PropertyTraits::GetObjectPropertyValue(Object, MetaData.template Get()[Index]..., ChannelInfo->PropertyBinding.PropertyPath, CurrentValue.Emplace()); } else if (const FCustomPropertyIndex* Custom = Property->TryGet()) { PropertyTraits::GetObjectPropertyValue(Object, MetaData.template Get()[Index]..., CustomAccessors[Custom->Value], CurrentValue.Emplace()); } else { const uint16 FastPtrOffset = Property->Get(); PropertyTraits::GetObjectPropertyValue(Object, MetaData.template Get()[Index]..., FastPtrOffset, CurrentValue.Emplace()); } InitialValues[Index] = CurrentValue.GetValue(); ValuesByChannel.Add(Channel, CurrentValue.GetValue()); }; } }; template struct TInitialValueProcessor : TInitialValueProcessorImpl> { using Super = TInitialValueProcessorImpl>; TInitialValueProcessor() = delete; TInitialValueProcessor( TComponentTypeID InInitialValueType, TArrayView InMetaDataComponents, ICustomPropertyRegistration* InCustomPropertyRegistration ) : Super(InInitialValueType, InMetaDataComponents, InCustomPropertyRegistration) {} }; template constexpr bool IsCompositePropertyTraits(T*) { return T::bIsComposite; } constexpr bool IsCompositePropertyTraits(...) { return true; } template struct TPropertyComponentHandlerImpl, TIntegerSequence, TIntegerSequence, CompositeTypes...> : IPropertyComponentHandler { static constexpr bool bIsComposite = IsCompositePropertyTraits((PropertyTraits*)nullptr); using StorageType = typename PropertyTraits::StorageType; using CompleteSetterTask = std::conditional_t, TSetPropertyValues>; using PreAnimatedStorageType = TPreAnimatedPropertyStorage; TAutoRegisterPreAnimatedStorageID StorageID; TPropertyComponentHandlerImpl() { } virtual TSharedPtr GetPreAnimatedStateStorage(const FPropertyDefinition& Definition, FPreAnimatedStateExtension* Container) override { TSharedPtr Existing = Container->FindStorage(StorageID); if (!Existing) { Existing = MakeShared(Definition); Existing->Initialize(StorageID, Container); Container->AddStorage(StorageID, Existing); } return Existing; } virtual void ScheduleSetterTasks(const FPropertyDefinition& Definition, TArrayView Composites, const FPropertyStats& Stats, IEntitySystemScheduler* TaskScheduler, UMovieSceneEntitySystemLinker* Linker) { ScheduleSetterTasksImpl(Definition, Composites, Stats, TaskScheduler, Linker, FEntityComponentFilter()); } void ScheduleSetterTasksImpl(const FPropertyDefinition& Definition, TArrayView Composites, const FPropertyStats& Stats, IEntitySystemScheduler* TaskScheduler, UMovieSceneEntitySystemLinker* Linker, const FEntityComponentFilter& AdditionalFilter) { FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get(); FEntityTaskBuilder() .Read(BuiltInComponents->BoundObject) .ReadOneOf(BuiltInComponents->CustomPropertyIndex, BuiltInComponents->FastPropertyOffset, BuiltInComponents->SlowProperty) .ReadAllOf(Definition.GetMetaDataComponent(MetaDataIndices)...) .ReadAllOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast()...) .FilterAll({ Definition.PropertyType }) .FilterNone({ BuiltInComponents->Tags.Ignored }) .CombineFilter(AdditionalFilter) .SetStat(Definition.StatID) .SetDesiredThread(Linker->EntityManager.GetGatherThread()) .template Fork_PerAllocation(&Linker->EntityManager, TaskScheduler, Definition.CustomPropertyRegistration); if constexpr (bIsComposite) { if (Stats.NumPartialProperties > 0) { using PartialSetterTask = TSetPartialPropertyValues; FComponentMask CompletePropertyMask; for (const FPropertyCompositeDefinition& Composite : Composites) { CompletePropertyMask.Set(Composite.ComponentTypeID); } FEntityTaskBuilder() .Read(BuiltInComponents->BoundObject) .ReadOneOf(BuiltInComponents->CustomPropertyIndex, BuiltInComponents->FastPropertyOffset, BuiltInComponents->SlowProperty) .ReadAllOf(Definition.GetMetaDataComponent(MetaDataIndices)...) .ReadAnyOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast()...) .FilterAny({ CompletePropertyMask }) .FilterAll({ Definition.PropertyType }) .FilterNone({ BuiltInComponents->Tags.Ignored }) .CombineFilter(AdditionalFilter) .FilterOut(CompletePropertyMask) .SetStat(Definition.StatID) .SetDesiredThread(Linker->EntityManager.GetGatherThread()) .template Fork_PerAllocation(&Linker->EntityManager, TaskScheduler, Definition.CustomPropertyRegistration, Composites); } } } virtual void DispatchSetterTasks(const FPropertyDefinition& Definition, TArrayView Composites, const FPropertyStats& Stats, FSystemTaskPrerequisites& InPrerequisites, FSystemSubsequentTasks& Subsequents, UMovieSceneEntitySystemLinker* Linker) { FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get(); FEntityTaskBuilder() .Read(BuiltInComponents->BoundObject) .ReadOneOf(BuiltInComponents->CustomPropertyIndex, BuiltInComponents->FastPropertyOffset, BuiltInComponents->SlowProperty) .ReadAllOf(Definition.GetMetaDataComponent(MetaDataIndices)...) .ReadAllOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast()...) .FilterAll({ Definition.PropertyType }) .FilterNone({ BuiltInComponents->Tags.Ignored }) .SetStat(Definition.StatID) .SetDesiredThread(Linker->EntityManager.GetGatherThread()) .template Dispatch_PerAllocation(&Linker->EntityManager, InPrerequisites, &Subsequents, Definition.CustomPropertyRegistration); if constexpr (bIsComposite) { if (Stats.NumPartialProperties > 0) { using PartialSetterTask = TSetPartialPropertyValues; FComponentMask CompletePropertyMask; for (const FPropertyCompositeDefinition& Composite : Composites) { CompletePropertyMask.Set(Composite.ComponentTypeID); } FEntityTaskBuilder() .Read(BuiltInComponents->BoundObject) .ReadOneOf(BuiltInComponents->CustomPropertyIndex, BuiltInComponents->FastPropertyOffset, BuiltInComponents->SlowProperty) .ReadAllOf(Definition.GetMetaDataComponent(MetaDataIndices)...) .ReadAnyOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast()...) .FilterAny({ CompletePropertyMask }) .FilterAll({ Definition.PropertyType }) .FilterOut(CompletePropertyMask) .FilterNone({ BuiltInComponents->Tags.Ignored }) .SetStat(Definition.StatID) .SetDesiredThread(Linker->EntityManager.GetGatherThread()) .template Dispatch_PerAllocation(&Linker->EntityManager, InPrerequisites, &Subsequents, Definition.CustomPropertyRegistration, Composites); } } } virtual TSharedPtr MakeInitialValueProcessor(const FPropertyDefinition& Definition) override { return MakeShared>( Definition.InitialValueType.ReinterpretCast(), Definition.MetaDataTypes, Definition.CustomPropertyRegistration ); } virtual void RecomposeBlendOperational(const FPropertyDefinition& PropertyDefinition, TArrayView Composites, const FValueDecompositionParams& InParams, UMovieSceneBlenderSystem* Blender, FConstPropertyComponentView InCurrentValue, FPropertyComponentArrayView OutResult) override { RecomposeBlendImpl(PropertyDefinition, Composites, InParams, Blender, InCurrentValue.ReinterpretCast(), OutResult.ReinterpretCast()); } void RecomposeBlendImpl(const FPropertyDefinition& PropertyDefinition, TArrayView Composites, const FValueDecompositionParams& InParams, UMovieSceneBlenderSystem* Blender, const StorageType& InCurrentValue, TArrayView OutResults) { check(OutResults.Num() == InParams.Query.Entities.Num()); IMovieSceneValueDecomposer* ValueDecomposer = Cast(Blender); if (!ValueDecomposer) { return; } FEntityManager& EntityManager = Blender->GetLinker()->EntityManager; EntityManager.LockDown(); constexpr int32 NumComposites = sizeof...(CompositeTypes); check(Composites.Num() == NumComposites); FAlignedDecomposedValue AlignedOutputs[NumComposites]; FValueDecompositionParams LocalParams = InParams; FGraphEventArray Tasks; for (int32 Index = 0; Index < NumComposites; ++Index) { if ((PropertyDefinition.DoubleCompositeMask & (1 << Index)) == 0) { continue; } LocalParams.ResultComponentType = Composites[Index].ComponentTypeID; FGraphEventRef Task = ValueDecomposer->DispatchDecomposeTask(LocalParams, &AlignedOutputs[Index]); if (Task) { Tasks.Add(Task); } } if (Tasks.Num() != 0) { FTaskGraphInterface::Get().WaitUntilTasksComplete(Tasks, ENamedThreads::GameThread); } // Get the initial value in case we have a value without a full-weighted absolute channel. TOptionalComponentReader InitialValueComponent; if (InParams.PropertyEntityID) { TComponentTypeID InitialValueType = PropertyDefinition.InitialValueType.ReinterpretCast(); InitialValueComponent = EntityManager.ReadComponent(InParams.PropertyEntityID, InitialValueType); } for (int32 Index = 0; Index < LocalParams.Query.Entities.Num(); ++Index) { FMovieSceneEntityID EntityID = LocalParams.Query.Entities[Index]; uint8* Result = reinterpret_cast(&OutResults[Index]); for (int32 CompositeIndex = 0; CompositeIndex < NumComposites; ++CompositeIndex) { if ((PropertyDefinition.DoubleCompositeMask & ( 1 << CompositeIndex)) != 0) { const double* InitialValueComposite = nullptr; FAlignedDecomposedValue& AlignedOutput = AlignedOutputs[CompositeIndex]; if (InitialValueComponent) { const StorageType* InitialValuePtr = InitialValueComponent.AsPtr(); InitialValueComposite = reinterpret_cast(reinterpret_cast(InitialValuePtr) + Composites[CompositeIndex].CompositeOffset); } const double NewComposite = *reinterpret_cast(reinterpret_cast(&InCurrentValue) + Composites[CompositeIndex].CompositeOffset); double* RecomposedComposite = reinterpret_cast(Result + Composites[CompositeIndex].CompositeOffset); *RecomposedComposite = AlignedOutput.Value.Recompose(EntityID, NewComposite, InitialValueComposite); } } } EntityManager.ReleaseLockDown(); } virtual void RecomposeBlendChannel(const FPropertyDefinition& PropertyDefinition, TArrayView Composites, int32 CompositeIndex, const FValueDecompositionParams& InParams, UMovieSceneBlenderSystem* Blender, double InCurrentValue, TArrayView OutResults) override { check(OutResults.Num() == InParams.Query.Entities.Num()); constexpr int32 NumComposites = sizeof...(CompositeTypes); check(Composites.Num() == NumComposites); const FPropertyCompositeDefinition& Composite = Composites[CompositeIndex]; IMovieSceneValueDecomposer* ValueDecomposer = Cast(Blender); if (!ValueDecomposer) { return; } FEntityManager& EntityManager = Blender->GetLinker()->EntityManager; EntityManager.LockDown(); FAlignedDecomposedValue AlignedOutput; FValueDecompositionParams LocalParams = InParams; LocalParams.ResultComponentType = Composite.ComponentTypeID; FGraphEventRef Task = ValueDecomposer->DispatchDecomposeTask(LocalParams, &AlignedOutput); if (Task) { FTaskGraphInterface::Get().WaitUntilTaskCompletes(Task, ENamedThreads::GameThread); } // Get the initial value in case we have a value without a full-weighted absolute channel. TOptionalComponentReader InitialValueComponent; if (InParams.PropertyEntityID) { TComponentTypeID InitialValueType = PropertyDefinition.InitialValueType.ReinterpretCast(); InitialValueComponent = EntityManager.ReadComponent(InParams.PropertyEntityID, InitialValueType); } for (int32 Index = 0; Index < LocalParams.Query.Entities.Num(); ++Index) { FMovieSceneEntityID EntityID = LocalParams.Query.Entities[Index]; if ((PropertyDefinition.DoubleCompositeMask & (1 << CompositeIndex)) != 0) { const double* InitialValueComposite = nullptr; if (InitialValueComponent) { const StorageType* InitialValuePtr = InitialValueComponent.AsPtr(); InitialValueComposite = reinterpret_cast(reinterpret_cast(InitialValuePtr) + Composite.CompositeOffset); } const double RecomposedComposite = AlignedOutput.Value.Recompose(EntityID, InCurrentValue, InitialValueComposite); OutResults[Index] = RecomposedComposite; } } EntityManager.ReleaseLockDown(); } virtual void RebuildOperational(const FPropertyDefinition& PropertyDefinition, TArrayView Composites, const TArrayView& EntityIDs, UMovieSceneEntitySystemLinker* Linker, FPropertyComponentArrayView OutResult) override { TArrayView TypedResults = OutResult.ReinterpretCast(); constexpr int32 NumComposites = sizeof...(CompositeTypes); check(Composites.Num() == NumComposites); check(TypedResults.Num() == EntityIDs.Num()); FEntityManager& EntityManager = Linker->EntityManager; for (int32 Index = 0; Index < EntityIDs.Num(); ++Index) { FMovieSceneEntityID EntityID = EntityIDs[Index]; if (!EntityID) { continue; } FEntityDataLocation Location = EntityManager.GetEntity(EntityIDs[Index]).Data; PatchCompositeValue(Composites, &TypedResults[Index], Location.Allocation->TryReadComponents(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast()).ComponentAtIndex(Location.ComponentOffset)... ); } } }; template struct TPropertyDefinitionBuilder { TPropertyDefinitionBuilder& AddSoleChannel(TComponentTypeID InComponent) { checkf(Definition == &Registry->GetProperties().Last(), TEXT("Cannot re-define a property type after another has been added.")); checkf(Definition->CompositeSize == 0, TEXT("Property already has a composite.")); FPropertyCompositeDefinition NewChannel = { InComponent, 0 }; Registry->CompositeDefinitions.Add(NewChannel); Definition->CompositeSize = 1; static_assert(!std::is_same_v, "Please use double-precision composites"); if constexpr (std::is_same_v) { Definition->DoubleCompositeMask = 1; } return *this; } template TPropertyDefinitionBuilder& SetCustomAccessors(TCustomPropertyRegistration* InCustomAccessors) { Definition->CustomPropertyRegistration = InCustomAccessors; return *this; } TPropertyDefinitionBuilder& SetStat(TStatId InStatID) { Definition->StatID = InStatID; return *this; } template TPropertyDefinitionBuilder& SetBlenderSystem() { Definition->BlenderSystemClass = BlenderSystemType::StaticClass(); return *this; } TPropertyDefinitionBuilder& SetBlenderSystem(UClass* BlenderSystemClass) { Definition->BlenderSystemClass = BlenderSystemClass; return *this; } void Commit() { Definition->Handler = TPropertyComponentHandler(); Definition->SetupInitialValueProcessor(); } template void Commit(HandlerType&& InHandler) { Definition->Handler = Forward(InHandler); Definition->SetupInitialValueProcessor(); } protected: friend FPropertyRegistry; TPropertyDefinitionBuilder(FPropertyDefinition* InDefinition, FPropertyRegistry* InRegistry) : Definition(InDefinition), Registry(InRegistry) {} FPropertyDefinition* Definition; FPropertyRegistry* Registry; }; template struct TCompositePropertyDefinitionBuilder { using StorageType = typename PropertyTraits::StorageType; static_assert(sizeof...(Composites) <= 32, "More than 32 composites is not supported"); TCompositePropertyDefinitionBuilder(FPropertyDefinition* InDefinition, FPropertyRegistry* InRegistry) : Definition(InDefinition), Registry(InRegistry) {} template TCompositePropertyDefinitionBuilder AddComposite(TComponentTypeID InComponent, T StorageType::*DataPtr) { checkf(Definition == &Registry->GetProperties().Last(), TEXT("Cannot re-define a property type after another has been added.")); const PTRINT CompositeOffset = (PTRINT)&(((StorageType*)0)->*DataPtr); FPropertyCompositeDefinition NewChannel = { InComponent, static_cast(CompositeOffset) }; Registry->CompositeDefinitions.Add(NewChannel); static_assert(!std::is_same_v, "Please use double-precision composites"); if constexpr (std::is_same_v) { Definition->DoubleCompositeMask |= 1 << Definition->CompositeSize; } ++Definition->CompositeSize; return TCompositePropertyDefinitionBuilder(Definition, Registry); } TCompositePropertyDefinitionBuilder AddComposite(TComponentTypeID InComponent, double StorageType::*DataPtr) { checkf(Definition == &Registry->GetProperties().Last(), TEXT("Cannot re-define a property type after another has been added.")); const PTRINT CompositeOffset = (PTRINT)&(((StorageType*)0)->*DataPtr); FPropertyCompositeDefinition NewChannel = { InComponent, static_cast(CompositeOffset) }; Registry->CompositeDefinitions.Add(NewChannel); Definition->DoubleCompositeMask |= 1 << Definition->CompositeSize; ++Definition->CompositeSize; return TCompositePropertyDefinitionBuilder(Definition, Registry); } template TCompositePropertyDefinitionBuilder& SetCustomAccessors(TCustomPropertyRegistration* InCustomAccessors) { Definition->CustomPropertyRegistration = InCustomAccessors; return *this; } template TCompositePropertyDefinitionBuilder& SetBlenderSystem() { Definition->BlenderSystemClass = BlenderSystemType::StaticClass(); return *this; } TCompositePropertyDefinitionBuilder& SetBlenderSystem(UClass* BlenderSystemClass) { Definition->BlenderSystemClass = BlenderSystemClass; return *this; } void Commit() { Definition->Handler = TPropertyComponentHandler(); Definition->SetupInitialValueProcessor(); } template void Commit(HandlerType&& InHandler) { Definition->Handler = Forward(InHandler); Definition->SetupInitialValueProcessor(); } private: FPropertyDefinition* Definition; FPropertyRegistry* Registry; }; struct FPropertyRecomposerPropertyInfo { static constexpr uint16 INVALID_BLEND_CHANNEL = uint16(-1); uint16 BlendChannel = INVALID_BLEND_CHANNEL; UMovieSceneBlenderSystem* BlenderSystem = nullptr; FMovieSceneEntityID PropertyEntityID; static FPropertyRecomposerPropertyInfo Invalid() { return FPropertyRecomposerPropertyInfo { INVALID_BLEND_CHANNEL, nullptr, FMovieSceneEntityID::Invalid() }; } }; DECLARE_DELEGATE_RetVal_TwoParams(FPropertyRecomposerPropertyInfo, FOnGetPropertyRecomposerPropertyInfo, FMovieSceneEntityID, UObject*); struct FPropertyRecomposerImpl { template TRecompositionResult RecomposeBlendOperational(const TPropertyComponents& InComponents, const FDecompositionQuery& InQuery, const typename PropertyTraits::StorageType& InCurrentValue); FOnGetPropertyRecomposerPropertyInfo OnGetPropertyInfo; }; template TRecompositionResult FPropertyRecomposerImpl::RecomposeBlendOperational(const TPropertyComponents& Components, const FDecompositionQuery& InQuery, const typename PropertyTraits::StorageType& InCurrentValue) { using namespace UE::MovieScene; const FBuiltInComponentTypes* BuiltInComponents = FBuiltInComponentTypes::Get(); const FPropertyDefinition& PropertyDefinition = BuiltInComponents->PropertyRegistry.GetDefinition(Components.CompositeID); TRecompositionResult Result(InCurrentValue, InQuery.Entities.Num()); if (InQuery.Entities.Num() == 0) { return Result; } const FPropertyRecomposerPropertyInfo Property = OnGetPropertyInfo.Execute(InQuery.Entities[0], InQuery.Object); if (Property.BlendChannel == FPropertyRecomposerPropertyInfo::INVALID_BLEND_CHANNEL) { return Result; } UMovieSceneBlenderSystem* Blender = Property.BlenderSystem; if (!Blender) { return Result; } FValueDecompositionParams Params; Params.Query = InQuery; Params.PropertyEntityID = Property.PropertyEntityID; Params.DecomposeBlendChannel = Property.BlendChannel; Params.PropertyTag = PropertyDefinition.PropertyType; TArrayView Composites = BuiltInComponents->PropertyRegistry.GetComposites(PropertyDefinition); PropertyDefinition.Handler->RecomposeBlendOperational(PropertyDefinition, Composites, Params, Blender, InCurrentValue, Result.Values); return Result; } } // namespace MovieScene } // namespace UE