Files
UnrealEngine/Engine/Source/Runtime/MovieScene/Public/EntitySystem/MovieScenePropertyComponentHandler.h
2025-05-18 13:04:45 +08:00

874 lines
33 KiB
C++

// 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<typename PropertyTraits, typename MetaDatatype, typename MetaDataIndices, typename CompositeIndices, typename ...CompositeTypes>
struct TPropertyComponentHandlerImpl;
template<typename PropertyTraits, typename ...CompositeTypes>
struct TPropertyComponentHandler
: TPropertyComponentHandlerImpl<PropertyTraits, typename PropertyTraits::MetaDataType, TMakeIntegerSequence<int, PropertyTraits::MetaDataType::Num>, TMakeIntegerSequence<int, sizeof...(CompositeTypes)>, CompositeTypes...>
{
};
template<typename, typename, typename>
struct TInitialValueProcessorImpl;
template<typename PropertyTraits, typename ...MetaDataTypes, int ...MetaDataIndices>
struct TInitialValueProcessorImpl<PropertyTraits, TPropertyMetaData<MetaDataTypes...>, TIntegerSequence<int, MetaDataIndices...>> : IInitialValueProcessor
{
using StorageType = typename PropertyTraits::StorageType;
TSortedMap<FInterrogationChannel, StorageType> ValuesByChannel;
FBuiltInComponentTypes* BuiltInComponents;
ICustomPropertyRegistration* CustomPropertyRegistration;
TComponentTypeID<StorageType> InitialValueType;
TTuple<TComponentTypeID<MetaDataTypes>...> MetaDataComponents;
FCustomAccessorView CustomAccessors;
// Transient properties reset on each usage.
IInterrogationExtension* Interrogation;
TPropertyValueStorage<PropertyTraits>* CacheStorage;
FEntityAllocationWriteContext WriteContext;
TInitialValueProcessorImpl(
TComponentTypeID<StorageType> InInitialValueType,
TArrayView<const FComponentTypeID> InMetaDataComponents,
ICustomPropertyRegistration* InCustomPropertyRegistration
)
: BuiltInComponents(FBuiltInComponentTypes::Get())
, CustomPropertyRegistration(InCustomPropertyRegistration)
, InitialValueType(InInitialValueType)
, MetaDataComponents(InMetaDataComponents[MetaDataIndices].ReinterpretCast<MetaDataTypes>()...)
, WriteContext(FEntityAllocationWriteContext::NewAllocation())
{
Interrogation = nullptr;
CacheStorage = nullptr;
}
virtual void Initialize(UMovieSceneEntitySystemLinker* Linker, FInitialValueCache* InitialValueCache) override
{
Interrogation = Linker->FindExtension<IInterrogationExtension>();
WriteContext = FEntityAllocationWriteContext(Linker->EntityManager);
if (CustomPropertyRegistration)
{
CustomAccessors = CustomPropertyRegistration->GetAccessors();
}
if (InitialValueCache)
{
CacheStorage = InitialValueCache->GetStorage<PropertyTraits>(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<StorageType> InitialValues = Allocation->WriteComponents(InitialValueType, WriteContext);
TComponentReader<UObject*> BoundObjects = Allocation->ReadComponents(BuiltInComponents->BoundObject);
TTuple< TComponentReader<MetaDataTypes>... > MetaData(
Allocation->ReadComponents(MetaDataComponents.template Get<MetaDataIndices>())...
);
if (TOptionalComponentReader<FCustomPropertyIndex> CustomIndices = Allocation->TryReadComponents(BuiltInComponents->CustomPropertyIndex))
{
const FCustomPropertyIndex* RawIndices = CustomIndices.AsPtr();
for (int32 Index = 0; Index < Num; ++Index)
{
PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get<MetaDataIndices>()[Index]..., CustomAccessors[RawIndices[Index].Value], InitialValues[Index]);
}
}
else if (TOptionalComponentReader<uint16> FastOffsets = Allocation->TryReadComponents(BuiltInComponents->FastPropertyOffset))
{
const uint16* RawOffsets = FastOffsets.AsPtr();
for (int32 Index = 0; Index < Num; ++Index)
{
PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get<MetaDataIndices>()[Index]..., RawOffsets[Index], InitialValues[Index]);
}
}
else if (TOptionalComponentReader<TSharedPtr<FTrackInstancePropertyBindings>> SlowProperties = Allocation->TryReadComponents(BuiltInComponents->SlowProperty))
{
const TSharedPtr<FTrackInstancePropertyBindings>* RawProperties = SlowProperties.AsPtr();
for (int32 Index = 0; Index < Num; ++Index)
{
PropertyTraits::GetObjectPropertyValue(BoundObjects[Index], MetaData.template Get<MetaDataIndices>()[Index]..., RawProperties[Index].Get(), InitialValues[Index]);
}
}
}
void VisitAllocationCached(const FEntityAllocation* Allocation)
{
const int32 Num = Allocation->Num();
TComponentWriter<FInitialValueIndex> InitialValueIndices = Allocation->WriteComponents(BuiltInComponents->InitialValueIndex, WriteContext);
TComponentWriter<StorageType> InitialValues = Allocation->WriteComponents(InitialValueType, WriteContext);
TComponentReader<UObject*> BoundObjects = Allocation->ReadComponents(BuiltInComponents->BoundObject);
TTuple< TComponentReader<MetaDataTypes>... > MetaData(
Allocation->ReadComponents(MetaDataComponents.template Get<MetaDataIndices>())...
);
if (TOptionalComponentReader<FCustomPropertyIndex> CustomIndices = Allocation->TryReadComponents(BuiltInComponents->CustomPropertyIndex))
{
const FCustomPropertyIndex* RawIndices = CustomIndices.AsPtr();
for (int32 Index = 0; Index < Num; ++Index)
{
TOptional<FInitialValueIndex> 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<MetaDataIndices>()[Index]..., CustomAccessors[RawIndices[Index].Value], Value);
InitialValues[Index] = Value;
InitialValueIndices[Index] = CacheStorage->AddInitialValue(BoundObjects[Index], Value, RawIndices[Index]);
}
}
}
else if (TOptionalComponentReader<uint16> FastOffsets = Allocation->TryReadComponents(BuiltInComponents->FastPropertyOffset))
{
const uint16* RawOffsets = FastOffsets.AsPtr();
for (int32 Index = 0; Index < Num; ++Index)
{
TOptional<FInitialValueIndex> 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<MetaDataIndices>()[Index]..., RawOffsets[Index], Value);
InitialValues[Index] = Value;
InitialValueIndices[Index] = CacheStorage->AddInitialValue(BoundObjects[Index], Value, RawOffsets[Index]);
}
}
}
else if (TOptionalComponentReader<TSharedPtr<FTrackInstancePropertyBindings>> SlowProperties = Allocation->TryReadComponents(BuiltInComponents->SlowProperty))
{
const TSharedPtr<FTrackInstancePropertyBindings>* RawProperties = SlowProperties.AsPtr();
for (int32 Index = 0; Index < Num; ++Index)
{
TOptional<FInitialValueIndex> 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<MetaDataIndices>()[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<StorageType> InitialValues = Allocation->WriteComponents(InitialValueType, WriteContext);
TComponentReader<FInterrogationKey> OutputKeys = Allocation->ReadComponents(BuiltInComponents->Interrogation.OutputKey);
TTuple< TComponentReader<MetaDataTypes>... > MetaData(
Allocation->ReadComponents(MetaDataComponents.template Get<MetaDataIndices>())...
);
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<FCustomPropertyIndex>())
{
CachedValue = CacheStorage->FindCachedValue(Object, *CustomIndex);
}
else
{
CachedValue = CacheStorage->FindCachedValue(Object, Property->Get<uint16>());
}
if (CachedValue)
{
InitialValues[Index] = *CachedValue;
ValuesByChannel.Add(Channel, *CachedValue);
continue;
}
}
// No cached value available, must retrieve it now
TOptional<StorageType> CurrentValue;
if (!Property.IsSet())
{
PropertyTraits::GetObjectPropertyValue(Object, MetaData.template Get<MetaDataIndices>()[Index]..., ChannelInfo->PropertyBinding.PropertyPath, CurrentValue.Emplace());
}
else if (const FCustomPropertyIndex* Custom = Property->TryGet<FCustomPropertyIndex>())
{
PropertyTraits::GetObjectPropertyValue(Object, MetaData.template Get<MetaDataIndices>()[Index]..., CustomAccessors[Custom->Value], CurrentValue.Emplace());
}
else
{
const uint16 FastPtrOffset = Property->Get<uint16>();
PropertyTraits::GetObjectPropertyValue(Object, MetaData.template Get<MetaDataIndices>()[Index]..., FastPtrOffset, CurrentValue.Emplace());
}
InitialValues[Index] = CurrentValue.GetValue();
ValuesByChannel.Add(Channel, CurrentValue.GetValue());
};
}
};
template<typename PropertyTraits>
struct TInitialValueProcessor : TInitialValueProcessorImpl<PropertyTraits, typename PropertyTraits::MetaDataType, TMakeIntegerSequence<int, PropertyTraits::MetaDataType::Num>>
{
using Super = TInitialValueProcessorImpl<PropertyTraits, typename PropertyTraits::MetaDataType, TMakeIntegerSequence<int, PropertyTraits::MetaDataType::Num>>;
TInitialValueProcessor() = delete;
TInitialValueProcessor(
TComponentTypeID<typename PropertyTraits::StorageType> InInitialValueType,
TArrayView<const FComponentTypeID> InMetaDataComponents,
ICustomPropertyRegistration* InCustomPropertyRegistration
) : Super(InInitialValueType, InMetaDataComponents, InCustomPropertyRegistration)
{}
};
template<typename T, typename U = decltype(T::bIsComposite)>
constexpr bool IsCompositePropertyTraits(T*)
{
return T::bIsComposite;
}
constexpr bool IsCompositePropertyTraits(...)
{
return true;
}
template<typename PropertyTraits, typename ...MetaDataTypes, int ...MetaDataIndices, typename ...CompositeTypes, int ...CompositeIndices>
struct TPropertyComponentHandlerImpl<PropertyTraits, TPropertyMetaData<MetaDataTypes...>, TIntegerSequence<int, MetaDataIndices...>, TIntegerSequence<int, CompositeIndices...>, CompositeTypes...> : IPropertyComponentHandler
{
static constexpr bool bIsComposite = IsCompositePropertyTraits((PropertyTraits*)nullptr);
using StorageType = typename PropertyTraits::StorageType;
using CompleteSetterTask = std::conditional_t<bIsComposite, TSetCompositePropertyValues<PropertyTraits, CompositeTypes...>, TSetPropertyValues<PropertyTraits>>;
using PreAnimatedStorageType = TPreAnimatedPropertyStorage<PropertyTraits>;
TAutoRegisterPreAnimatedStorageID<PreAnimatedStorageType> StorageID;
TPropertyComponentHandlerImpl()
{
}
virtual TSharedPtr<IPreAnimatedStorage> GetPreAnimatedStateStorage(const FPropertyDefinition& Definition, FPreAnimatedStateExtension* Container) override
{
TSharedPtr<PreAnimatedStorageType> Existing = Container->FindStorage(StorageID);
if (!Existing)
{
Existing = MakeShared<PreAnimatedStorageType>(Definition);
Existing->Initialize(StorageID, Container);
Container->AddStorage(StorageID, Existing);
}
return Existing;
}
virtual void ScheduleSetterTasks(const FPropertyDefinition& Definition, TArrayView<const FPropertyCompositeDefinition> Composites, const FPropertyStats& Stats, IEntitySystemScheduler* TaskScheduler, UMovieSceneEntitySystemLinker* Linker)
{
ScheduleSetterTasksImpl(Definition, Composites, Stats, TaskScheduler, Linker, FEntityComponentFilter());
}
void ScheduleSetterTasksImpl(const FPropertyDefinition& Definition, TArrayView<const FPropertyCompositeDefinition> 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<MetaDataTypes>(MetaDataIndices)...)
.ReadAllOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast<CompositeTypes>()...)
.FilterAll({ Definition.PropertyType })
.FilterNone({ BuiltInComponents->Tags.Ignored })
.CombineFilter(AdditionalFilter)
.SetStat(Definition.StatID)
.SetDesiredThread(Linker->EntityManager.GetGatherThread())
.template Fork_PerAllocation<CompleteSetterTask>(&Linker->EntityManager, TaskScheduler, Definition.CustomPropertyRegistration);
if constexpr (bIsComposite)
{
if (Stats.NumPartialProperties > 0)
{
using PartialSetterTask = TSetPartialPropertyValues<PropertyTraits, CompositeTypes...>;
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<MetaDataTypes>(MetaDataIndices)...)
.ReadAnyOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast<CompositeTypes>()...)
.FilterAny({ CompletePropertyMask })
.FilterAll({ Definition.PropertyType })
.FilterNone({ BuiltInComponents->Tags.Ignored })
.CombineFilter(AdditionalFilter)
.FilterOut(CompletePropertyMask)
.SetStat(Definition.StatID)
.SetDesiredThread(Linker->EntityManager.GetGatherThread())
.template Fork_PerAllocation<PartialSetterTask>(&Linker->EntityManager, TaskScheduler, Definition.CustomPropertyRegistration, Composites);
}
}
}
virtual void DispatchSetterTasks(const FPropertyDefinition& Definition, TArrayView<const FPropertyCompositeDefinition> 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<MetaDataTypes>(MetaDataIndices)...)
.ReadAllOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast<CompositeTypes>()...)
.FilterAll({ Definition.PropertyType })
.FilterNone({ BuiltInComponents->Tags.Ignored })
.SetStat(Definition.StatID)
.SetDesiredThread(Linker->EntityManager.GetGatherThread())
.template Dispatch_PerAllocation<CompleteSetterTask>(&Linker->EntityManager, InPrerequisites, &Subsequents, Definition.CustomPropertyRegistration);
if constexpr (bIsComposite)
{
if (Stats.NumPartialProperties > 0)
{
using PartialSetterTask = TSetPartialPropertyValues<PropertyTraits, CompositeTypes...>;
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<MetaDataTypes>(MetaDataIndices)...)
.ReadAnyOf(Composites[CompositeIndices].ComponentTypeID.ReinterpretCast<CompositeTypes>()...)
.FilterAny({ CompletePropertyMask })
.FilterAll({ Definition.PropertyType })
.FilterOut(CompletePropertyMask)
.FilterNone({ BuiltInComponents->Tags.Ignored })
.SetStat(Definition.StatID)
.SetDesiredThread(Linker->EntityManager.GetGatherThread())
.template Dispatch_PerAllocation<PartialSetterTask>(&Linker->EntityManager, InPrerequisites, &Subsequents, Definition.CustomPropertyRegistration, Composites);
}
}
}
virtual TSharedPtr<IInitialValueProcessor> MakeInitialValueProcessor(const FPropertyDefinition& Definition) override
{
return MakeShared<TInitialValueProcessor<PropertyTraits>>(
Definition.InitialValueType.ReinterpretCast<StorageType>(),
Definition.MetaDataTypes,
Definition.CustomPropertyRegistration
);
}
virtual void RecomposeBlendOperational(const FPropertyDefinition& PropertyDefinition, TArrayView<const FPropertyCompositeDefinition> Composites, const FValueDecompositionParams& InParams, UMovieSceneBlenderSystem* Blender, FConstPropertyComponentView InCurrentValue, FPropertyComponentArrayView OutResult) override
{
RecomposeBlendImpl(PropertyDefinition, Composites, InParams, Blender, InCurrentValue.ReinterpretCast<StorageType>(), OutResult.ReinterpretCast<StorageType>());
}
void RecomposeBlendImpl(const FPropertyDefinition& PropertyDefinition, TArrayView<const FPropertyCompositeDefinition> Composites, const FValueDecompositionParams& InParams, UMovieSceneBlenderSystem* Blender, const StorageType& InCurrentValue, TArrayView<StorageType> OutResults)
{
check(OutResults.Num() == InParams.Query.Entities.Num());
IMovieSceneValueDecomposer* ValueDecomposer = Cast<IMovieSceneValueDecomposer>(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<StorageType> InitialValueComponent;
if (InParams.PropertyEntityID)
{
TComponentTypeID<StorageType> InitialValueType = PropertyDefinition.InitialValueType.ReinterpretCast<StorageType>();
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<uint8*>(&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<const double*>(reinterpret_cast<const uint8*>(InitialValuePtr) + Composites[CompositeIndex].CompositeOffset);
}
const double NewComposite = *reinterpret_cast<const double*>(reinterpret_cast<const uint8*>(&InCurrentValue) + Composites[CompositeIndex].CompositeOffset);
double* RecomposedComposite = reinterpret_cast<double*>(Result + Composites[CompositeIndex].CompositeOffset);
*RecomposedComposite = AlignedOutput.Value.Recompose(EntityID, NewComposite, InitialValueComposite);
}
}
}
EntityManager.ReleaseLockDown();
}
virtual void RecomposeBlendChannel(const FPropertyDefinition& PropertyDefinition, TArrayView<const FPropertyCompositeDefinition> Composites, int32 CompositeIndex, const FValueDecompositionParams& InParams, UMovieSceneBlenderSystem* Blender, double InCurrentValue, TArrayView<double> 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<IMovieSceneValueDecomposer>(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<StorageType> InitialValueComponent;
if (InParams.PropertyEntityID)
{
TComponentTypeID<StorageType> InitialValueType = PropertyDefinition.InitialValueType.ReinterpretCast<StorageType>();
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<const double*>(reinterpret_cast<const uint8*>(InitialValuePtr) + Composite.CompositeOffset);
}
const double RecomposedComposite = AlignedOutput.Value.Recompose(EntityID, InCurrentValue, InitialValueComposite);
OutResults[Index] = RecomposedComposite;
}
}
EntityManager.ReleaseLockDown();
}
virtual void RebuildOperational(const FPropertyDefinition& PropertyDefinition, TArrayView<const FPropertyCompositeDefinition> Composites, const TArrayView<FMovieSceneEntityID>& EntityIDs, UMovieSceneEntitySystemLinker* Linker, FPropertyComponentArrayView OutResult) override
{
TArrayView<StorageType> TypedResults = OutResult.ReinterpretCast<StorageType>();
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<CompositeTypes>()).ComponentAtIndex(Location.ComponentOffset)...
);
}
}
};
template<typename PropertyTraits>
struct TPropertyDefinitionBuilder
{
TPropertyDefinitionBuilder<PropertyTraits>& AddSoleChannel(TComponentTypeID<typename PropertyTraits::StorageType> 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<typename PropertyTraits::StorageType, float>, "Please use double-precision composites");
if constexpr (std::is_same_v<typename PropertyTraits::StorageType, double>)
{
Definition->DoubleCompositeMask = 1;
}
return *this;
}
template<int InlineSize>
TPropertyDefinitionBuilder<PropertyTraits>& SetCustomAccessors(TCustomPropertyRegistration<PropertyTraits, InlineSize>* InCustomAccessors)
{
Definition->CustomPropertyRegistration = InCustomAccessors;
return *this;
}
TPropertyDefinitionBuilder<PropertyTraits>& SetStat(TStatId InStatID)
{
Definition->StatID = InStatID;
return *this;
}
template<typename BlenderSystemType>
TPropertyDefinitionBuilder<PropertyTraits>& SetBlenderSystem()
{
Definition->BlenderSystemClass = BlenderSystemType::StaticClass();
return *this;
}
TPropertyDefinitionBuilder<PropertyTraits>& SetBlenderSystem(UClass* BlenderSystemClass)
{
Definition->BlenderSystemClass = BlenderSystemClass;
return *this;
}
void Commit()
{
Definition->Handler = TPropertyComponentHandler<PropertyTraits, typename PropertyTraits::StorageType>();
Definition->SetupInitialValueProcessor();
}
template<typename HandlerType>
void Commit(HandlerType&& InHandler)
{
Definition->Handler = Forward<HandlerType>(InHandler);
Definition->SetupInitialValueProcessor();
}
protected:
friend FPropertyRegistry;
TPropertyDefinitionBuilder(FPropertyDefinition* InDefinition, FPropertyRegistry* InRegistry)
: Definition(InDefinition), Registry(InRegistry)
{}
FPropertyDefinition* Definition;
FPropertyRegistry* Registry;
};
template<typename PropertyTraits, typename... Composites>
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<typename T>
TCompositePropertyDefinitionBuilder<PropertyTraits, Composites..., T> AddComposite(TComponentTypeID<T> 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<uint16>(CompositeOffset) };
Registry->CompositeDefinitions.Add(NewChannel);
static_assert(!std::is_same_v<T, float>, "Please use double-precision composites");
if constexpr (std::is_same_v<T, double>)
{
Definition->DoubleCompositeMask |= 1 << Definition->CompositeSize;
}
++Definition->CompositeSize;
return TCompositePropertyDefinitionBuilder<PropertyTraits, Composites..., T>(Definition, Registry);
}
TCompositePropertyDefinitionBuilder<PropertyTraits, Composites..., double> AddComposite(TComponentTypeID<double> 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<uint16>(CompositeOffset) };
Registry->CompositeDefinitions.Add(NewChannel);
Definition->DoubleCompositeMask |= 1 << Definition->CompositeSize;
++Definition->CompositeSize;
return TCompositePropertyDefinitionBuilder<PropertyTraits, Composites..., double>(Definition, Registry);
}
template<int InlineSize>
TCompositePropertyDefinitionBuilder<PropertyTraits, Composites...>& SetCustomAccessors(TCustomPropertyRegistration<PropertyTraits, InlineSize>* InCustomAccessors)
{
Definition->CustomPropertyRegistration = InCustomAccessors;
return *this;
}
template<typename BlenderSystemType>
TCompositePropertyDefinitionBuilder<PropertyTraits, Composites...>& SetBlenderSystem()
{
Definition->BlenderSystemClass = BlenderSystemType::StaticClass();
return *this;
}
TCompositePropertyDefinitionBuilder<PropertyTraits, Composites...>& SetBlenderSystem(UClass* BlenderSystemClass)
{
Definition->BlenderSystemClass = BlenderSystemClass;
return *this;
}
void Commit()
{
Definition->Handler = TPropertyComponentHandler<PropertyTraits, Composites...>();
Definition->SetupInitialValueProcessor();
}
template<typename HandlerType>
void Commit(HandlerType&& InHandler)
{
Definition->Handler = Forward<HandlerType>(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<typename PropertyTraits>
TRecompositionResult<typename PropertyTraits::StorageType> RecomposeBlendOperational(const TPropertyComponents<PropertyTraits>& InComponents, const FDecompositionQuery& InQuery, const typename PropertyTraits::StorageType& InCurrentValue);
FOnGetPropertyRecomposerPropertyInfo OnGetPropertyInfo;
};
template<typename PropertyTraits>
TRecompositionResult<typename PropertyTraits::StorageType> FPropertyRecomposerImpl::RecomposeBlendOperational(const TPropertyComponents<PropertyTraits>& 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<typename PropertyTraits::StorageType> 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<const FPropertyCompositeDefinition> Composites = BuiltInComponents->PropertyRegistry.GetComposites(PropertyDefinition);
PropertyDefinition.Handler->RecomposeBlendOperational(PropertyDefinition, Composites, Params, Blender, InCurrentValue, Result.Values);
return Result;
}
} // namespace MovieScene
} // namespace UE