Files
UnrealEngine/Engine/Source/Runtime/MovieSceneTracks/Private/Systems/MovieScenePropertyInstantiator.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

1226 lines
46 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Systems/MovieScenePropertyInstantiator.h"
#include "Algo/AllOf.h"
#include "Algo/IndexOf.h"
#include "EntitySystem/Interrogation/MovieSceneInterrogationLinker.h"
#include "EntitySystem/MovieSceneBlenderSystem.h"
#include "EntitySystem/MovieSceneEntityBuilder.h"
#include "EntitySystem/MovieSceneEntityGroupingSystem.h"
#include "EntitySystem/MovieSceneEntityIDs.h"
#include "EntitySystem/MovieSceneEntitySystemLinker.h"
#include "EntitySystem/MovieScenePropertyBinding.h"
#include "EntitySystem/MovieScenePropertyRegistry.h"
#include "EntitySystem/MovieSceneInitialValueSystem.h"
#include "ProfilingDebugging/CountersTrace.h"
#include "Systems/MovieScenePiecewiseDoubleBlenderSystem.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(MovieScenePropertyInstantiator)
DECLARE_CYCLE_STAT(TEXT("DiscoverInvalidatedProperties"), MovieSceneEval_DiscoverInvalidatedProperties, STATGROUP_MovieSceneECS);
DECLARE_CYCLE_STAT(TEXT("ProcessInvalidatedProperties"), MovieSceneEval_ProcessInvalidatedProperties, STATGROUP_MovieSceneECS);
DECLARE_CYCLE_STAT(TEXT("InitializePropertyMetaData"), MovieSceneEval_InitializePropertyMetaData, STATGROUP_MovieSceneECS);
namespace UE::MovieScene
{
struct FPropertyInstantiatorGroupingPolicy
{
using GroupKeyType = TTuple<UObject*, FName>;
bool GetGroupKey(UObject* Object, const FMovieScenePropertyBinding& PropertyBinding, GroupKeyType& OutGroupKey)
{
OutGroupKey = MakeTuple(Object, PropertyBinding.PropertyPath);
return true;
}
#if WITH_EDITOR
bool OnObjectsReplaced(GroupKeyType& InOutKey, const TMap<UObject*, UObject*>& ReplacementMap)
{
if (UObject* const * NewObject = ReplacementMap.Find(InOutKey.Key))
{
InOutKey.Key = *NewObject;
return true;
}
return false;
}
#endif
};
} // namespace UE::MovieScene
UMovieScenePropertyInstantiatorSystem::FContributorKey UMovieScenePropertyInstantiatorSystem::FPropertyParameters::MakeContributorKey() const
{
return FContributorKey(PropertyInfoIndex);
}
UMovieScenePropertyInstantiatorSystem::UMovieScenePropertyInstantiatorSystem(const FObjectInitializer& ObjInit)
: Super(ObjInit)
{
using namespace UE::MovieScene;
BuiltInComponents = FBuiltInComponentTypes::Get();
RecomposerImpl.OnGetPropertyInfo = FOnGetPropertyRecomposerPropertyInfo::CreateUObject(
this, &UMovieScenePropertyInstantiatorSystem::FindPropertyFromSource);
SystemCategories = FSystemInterrogator::GetExcludedFromInterrogationCategory();
RelevantComponent = BuiltInComponents->PropertyBinding;
if (HasAnyFlags(RF_ClassDefaultObject))
{
DefineComponentConsumer(GetClass(), BuiltInComponents->BoundObject);
DefineComponentConsumer(GetClass(), BuiltInComponents->Group);
DefineComponentConsumer(GetClass(), BuiltInComponents->HierarchicalBlendTarget);
DefineComponentConsumer(GetClass(), BuiltInComponents->Tags.Ignored);
DefineComponentProducer(GetClass(), BuiltInComponents->BlendChannelInput);
DefineComponentProducer(GetClass(), BuiltInComponents->SymbolicTags.CreatesEntities);
DefineImplicitPrerequisite(GetClass(), UMovieSceneInitialValueSystem::StaticClass());
}
}
UE::MovieScene::FPropertyStats UMovieScenePropertyInstantiatorSystem::GetStatsForProperty(UE::MovieScene::FCompositePropertyTypeID PropertyID) const
{
const int32 Index = PropertyID.AsIndex();
if (PropertyStats.IsValidIndex(Index))
{
return PropertyStats[Index];
}
return UE::MovieScene::FPropertyStats();
}
void UMovieScenePropertyInstantiatorSystem::OnLink()
{
using namespace UE::MovieScene;
CleanFastPathMask.Reset();
CleanFastPathMask.SetAll({ BuiltInComponents->FastPropertyOffset, BuiltInComponents->SlowProperty, BuiltInComponents->CustomPropertyIndex });
CleanFastPathMask.CombineWithBitwiseOR(Linker->EntityManager.GetComponents()->GetMigrationMask(), EBitwiseOperatorFlags::MaxSize);
UMovieSceneEntityGroupingSystem* GroupingSystem = Linker->LinkSystem<UMovieSceneEntityGroupingSystem>();
PropertyGroupingKey = GroupingSystem->AddGrouping(
FPropertyInstantiatorGroupingPolicy(),
BuiltInComponents->BoundObject, BuiltInComponents->PropertyBinding);
#if WITH_EDITOR
FCoreUObjectDelegates::OnObjectsReplaced.AddUObject(this, &UMovieScenePropertyInstantiatorSystem::OnObjectsReplaced);
#endif
}
void UMovieScenePropertyInstantiatorSystem::OnUnlink()
{
using namespace UE::MovieScene;
const bool bAllPropertiesClean = (
ResolvedProperties.Num() == 0 &&
Contributors.Num() == 0 &&
NewContributors.Num() == 0);
if (!ensure(bAllPropertiesClean))
{
ResolvedProperties.Reset();
Contributors.Reset();
NewContributors.Reset();
PropertyStats.Reset();
}
const bool bAllPropertiesGone = Algo::AllOf(
PropertyStats, [](const FPropertyStats& Item)
{
return Item.NumProperties == 0 && Item.NumPartialProperties == 0;
});
if (!ensure(bAllPropertiesGone))
{
PropertyStats.Reset();
}
const bool bAllTasksDone = (
InitializePropertyMetaDataTasks.Num() == 0 &&
SaveGlobalStateTasks.Num() == 0);
if (!ensure(bAllTasksDone))
{
InitializePropertyMetaDataTasks.Reset();
SaveGlobalStateTasks.Reset();
}
UMovieSceneEntityGroupingSystem* GroupingSystem = Linker->FindSystem<UMovieSceneEntityGroupingSystem>();
if (ensure(GroupingSystem))
{
GroupingSystem->RemoveGrouping(PropertyGroupingKey);
}
PropertyGroupingKey = FEntityGroupingPolicyKey();
#if WITH_EDITOR
FCoreUObjectDelegates::OnObjectsReplaced.RemoveAll(this);
#endif
}
void UMovieScenePropertyInstantiatorSystem::OnCleanTaggedGarbage()
{
using namespace UE::MovieScene;
// Only process expired properties for this GC pass to ensure we don't end up creating any new entities
DiscoverExpiredProperties(PendingInvalidatedProperties);
TArrayView<const FPropertyDefinition> Properties = this->BuiltInComponents->PropertyRegistry.GetProperties();
bool bAnyDestroyed = false;
// Look through our resolved properties to detect any outputs that have been destroyed
for (int32 Index = 0; Index < ResolvedProperties.GetMaxIndex(); ++Index)
{
if (!ResolvedProperties.IsAllocated(Index))
{
continue;
}
FObjectPropertyInfo& PropertyInfo = ResolvedProperties[Index];
FContributorKey ContributorKey(Index);
const bool bFastPathOutputBeingDestroyed = PropertyInfo.PreviousFastPathID && Linker->EntityManager.HasComponent(PropertyInfo.PreviousFastPathID, BuiltInComponents->Tags.NeedsUnlink);
// If the fast path is being destroyed, we need to copy over any initial values from that fast path entity to
// any additional contributors that might still be alive. This can happen if a sequence gets GC'd and removes its entities while another is still alive animating the same thing.
if (bFastPathOutputBeingDestroyed)
{
FMovieSceneEntityID TemporaryFastPathEntity;
for (auto It = Contributors.CreateConstKeyIterator(ContributorKey); It; ++It)
{
Linker->EntityManager.AddComponent(It->Value, BuiltInComponents->Tags.NeedsLink);
TemporaryFastPathEntity = It->Value;
}
if (TemporaryFastPathEntity)
{
const FPropertyDefinition& PropertyDefinition = Properties[PropertyInfo.PropertyDefinitionIndex];
FComponentMask CopyMask;
CopyMask.Set(PropertyDefinition.InitialValueType);
CopyMask.Set(BuiltInComponents->Tags.HasAssignedInitialValue);
for (FComponentTypeID Component : PropertyDefinition.MetaDataTypes)
{
CopyMask.Set(Component);
}
Linker->EntityManager.CopyComponents(PropertyInfo.PreviousFastPathID, TemporaryFastPathEntity, CopyMask);
PropertyInfo.PreviousFastPathID = TemporaryFastPathEntity;
}
else
{
// There is nothing else animating this - destroy the property entirely
DestroyStaleProperty(Index);
bAnyDestroyed = true;
if (PendingInvalidatedProperties.IsValidIndex(Index) && PendingInvalidatedProperties[Index] == true)
{
// This property index is no longer valid at all
PendingInvalidatedProperties[Index] = false;
}
}
}
else if (PropertyInfo.FinalBlendOutputID && Linker->EntityManager.HasComponent(PropertyInfo.FinalBlendOutputID, BuiltInComponents->Tags.NeedsUnlink))
{
// Really we shouldn't have any contributors any more if the output is being destroyed since the target object must be going away
// (which means all the contributors that reference that target object must also be going away)
if (!ensureMsgf(!Contributors.Contains(ContributorKey), TEXT("Blend output is being destroyed while there are still contributors.")))
{
// We still have contributors?? Shouldn't happen, but it's recoverable by just re-resolving all the contributors
for (auto It = Contributors.CreateKeyIterator(ContributorKey); It; ++It)
{
Linker->EntityManager.AddComponent(It->Value, BuiltInComponents->Tags.NeedsLink);
It.RemoveCurrent();
}
}
DestroyStaleProperty(Index);
bAnyDestroyed = true;
if (PendingInvalidatedProperties.IsValidIndex(Index) && PendingInvalidatedProperties[Index] == true)
{
// This property index is no longer valid at all
PendingInvalidatedProperties[Index] = false;
}
}
}
if (bAnyDestroyed)
{
PostDestroyStaleProperties();
}
}
void UMovieScenePropertyInstantiatorSystem::OnObjectsReplaced(const TMap<UObject*, UObject*>& ReplacementMap)
{
#if WITH_EDITOR
for (auto It = ResolvedProperties.CreateIterator(); It; ++It)
{
FObjectPropertyInfo& ResolvedProperty = (*It);
if (UObject* const* NewObject = ReplacementMap.Find(ResolvedProperty.BoundObject))
{
ResolvedProperty.BoundObject = *NewObject;
}
}
#endif
}
void UMovieScenePropertyInstantiatorSystem::OnRun(FSystemTaskPrerequisites& InPrerequisites, FSystemSubsequentTasks& Subsequents)
{
using namespace UE::MovieScene;
// Discover any newly created or expiring property entities
DiscoverInvalidatedProperties(PendingInvalidatedProperties);
if (PendingInvalidatedProperties.Num() != 0)
{
UpgradeFloatToDoubleProperties(PendingInvalidatedProperties);
ProcessInvalidatedProperties(PendingInvalidatedProperties);
PendingInvalidatedProperties.Empty();
}
if (InitializePropertyMetaDataTasks.Find(true) != INDEX_NONE)
{
InitializePropertyMetaData(InPrerequisites, Subsequents);
}
}
void UMovieScenePropertyInstantiatorSystem::DiscoverInvalidatedProperties(TBitArray<>& OutInvalidatedProperties)
{
using namespace UE::MovieScene;
MOVIESCENE_DETAILED_SCOPE_CYCLE_COUNTER(MovieSceneEval_DiscoverInvalidatedProperties);
TArrayView<const FPropertyDefinition> Properties = BuiltInComponents->PropertyRegistry.GetProperties();
PropertyStats.SetNum(Properties.Num());
DiscoverNewProperties(OutInvalidatedProperties);
DiscoverExpiredProperties(OutInvalidatedProperties);
}
void UMovieScenePropertyInstantiatorSystem::DiscoverNewProperties(TBitArray<>&OutInvalidatedProperties)
{
using namespace UE::MovieScene;
TArrayView<const FPropertyDefinition> Properties = this->BuiltInComponents->PropertyRegistry.GetProperties();
auto VisitNewProperties = [this, Properties, &OutInvalidatedProperties](FEntityAllocationIteratorItem AllocationItem, const FMovieSceneEntityID* EntityIDs, UObject* const * ObjectPtrs, const FMovieScenePropertyBinding* PropertyPtrs, const FEntityGroupID* GroupIDs)
{
const FEntityAllocation* Allocation = AllocationItem.GetAllocation();
const FComponentMask& AllocationType = AllocationItem.GetAllocationType();
const int32 PropertyDefinitionIndex = Algo::IndexOfByPredicate(Properties, [=](const FPropertyDefinition& InDefinition){ return Allocation->HasComponent(InDefinition.PropertyType); });
if (PropertyDefinitionIndex == INDEX_NONE)
{
return;
}
const FPropertyDefinition& PropertyDefinition = Properties[PropertyDefinitionIndex];
FCustomAccessorView CustomAccessors = PropertyDefinition.CustomPropertyRegistration ? PropertyDefinition.CustomPropertyRegistration->GetAccessors() : FCustomAccessorView();
const bool bIgnored = AllocationType.Contains(BuiltInComponents->Tags.Ignored);
if (bIgnored)
{
// Ignored properties should no longer contribute
for (int32 Index = 0; Index < Allocation->Num(); ++Index)
{
const int32 PropertyIndex = GroupIDs[Index].GroupIndex;
if (PropertyIndex != INDEX_NONE)
{
OutInvalidatedProperties.PadToNum(PropertyIndex + 1, false);
OutInvalidatedProperties[PropertyIndex] = true;
this->Contributors.Remove(PropertyIndex, EntityIDs[Index]);
}
}
}
else
{
for (int32 Index = 0; Index < Allocation->Num(); ++Index)
{
const bool bResolved = this->ResolveProperty(CustomAccessors, ObjectPtrs[Index], PropertyPtrs[Index], GroupIDs[Index], PropertyDefinitionIndex);
if (bResolved)
{
const int32 PropertyIndex = GroupIDs[Index].GroupIndex;
FContributorKey Key { PropertyIndex };
this->Contributors.Add(Key, EntityIDs[Index]);
this->NewContributors.Add(Key, EntityIDs[Index]);
OutInvalidatedProperties.PadToNum(PropertyIndex + 1, false);
OutInvalidatedProperties[PropertyIndex] = true;
}
}
}
};
FEntityTaskBuilder()
.ReadEntityIDs()
.Read(BuiltInComponents->BoundObject)
.Read(BuiltInComponents->PropertyBinding)
.Read(BuiltInComponents->Group)
.FilterNone({ BuiltInComponents->BlendChannelOutput })
.FilterAll({ BuiltInComponents->Tags.NeedsLink })
.Iterate_PerAllocation(&Linker->EntityManager, VisitNewProperties);
}
void UMovieScenePropertyInstantiatorSystem::DiscoverExpiredProperties(TBitArray<>& OutInvalidatedProperties)
{
using namespace UE::MovieScene;
auto VisitExpiredEntities = [this, &OutInvalidatedProperties](FMovieSceneEntityID EntityID, const FEntityGroupID& GroupID)
{
const int32 PropertyIndex = GroupID.GroupIndex;
if (PropertyIndex != INDEX_NONE)
{
OutInvalidatedProperties.PadToNum(PropertyIndex + 1, false);
OutInvalidatedProperties[PropertyIndex] = true;
this->Contributors.Remove(PropertyIndex, EntityID);
}
};
FEntityTaskBuilder()
.ReadEntityIDs()
.Read(BuiltInComponents->Group)
.FilterNone({ BuiltInComponents->BlendChannelOutput })
.FilterAll({ BuiltInComponents->BoundObject, BuiltInComponents->PropertyBinding, BuiltInComponents->Tags.NeedsUnlink })
.Iterate_PerEntity(&Linker->EntityManager, VisitExpiredEntities);
}
void UMovieScenePropertyInstantiatorSystem::UpgradeFloatToDoubleProperties(const TBitArray<>& InvalidatedProperties)
{
using namespace UE::MovieScene;
MOVIESCENE_DETAILED_SCOPE_CYCLE_COUNTER(MovieSceneEval_ProcessInvalidatedProperties);
const FMovieSceneTracksComponentTypes* TrackComponents = FMovieSceneTracksComponentTypes::Get();
TArrayView<const FPropertyDefinition> Properties = BuiltInComponents->PropertyRegistry.GetProperties();
for (TConstSetBitIterator<> It(InvalidatedProperties); It; ++It)
{
const int32 PropertyIndex = It.GetIndex();
if (!ResolvedProperties.IsValidIndex(PropertyIndex))
{
continue;
}
FObjectPropertyInfo& PropertyInfo = ResolvedProperties[PropertyIndex];
const bool bHadConversionInfo = PropertyInfo.ConvertedFromPropertyDefinitionIndex.IsSet();
// The first time we encounter a specific property, we need to figure out if it needs type conversion or not.
if (!bHadConversionInfo)
{
// Don't do anything if the property isn't of the kind of type we need to care about. Right now, we only
// support dealing with float->double and FVectorXf->FVectorXd.
const FPropertyDefinition& PropertyDefinition = Properties[PropertyInfo.PropertyDefinitionIndex];
if (PropertyDefinition.PropertyType != TrackComponents->Float.PropertyTag &&
PropertyDefinition.PropertyType != TrackComponents->FloatVector.PropertyTag)
{
PropertyInfo.ConvertedFromPropertyDefinitionIndex = INDEX_NONE;
continue;
}
FTrackInstancePropertyBindings Bindings(PropertyInfo.PropertyBinding.PropertyName, PropertyInfo.PropertyBinding.PropertyPath.ToString());
FProperty* BoundProperty = Bindings.GetProperty(*PropertyInfo.BoundObject);
if (!BoundProperty)
{
continue;
}
// Patch the resolved property info to point to the double-precision property definition.
PropertyInfo.ConvertedFromPropertyDefinitionIndex = INDEX_NONE;
if (PropertyDefinition.PropertyType == TrackComponents->Float.PropertyTag)
{
const bool bIsDouble = BoundProperty->IsA<FDoubleProperty>();
if (bIsDouble)
{
const int32 DoublePropertyDefinitionIndex = Properties.IndexOfByPredicate(
[TrackComponents](const FPropertyDefinition& Item) { return Item.PropertyType == TrackComponents->Double.PropertyTag; });
ensure(DoublePropertyDefinitionIndex != INDEX_NONE);
PropertyInfo.ConvertedFromPropertyDefinitionIndex = PropertyInfo.PropertyDefinitionIndex;
PropertyInfo.PropertyDefinitionIndex = DoublePropertyDefinitionIndex;
}
}
else if (PropertyDefinition.PropertyType == TrackComponents->FloatVector.PropertyTag)
{
const UScriptStruct* BoundStructProperty = CastField<FStructProperty>(BoundProperty)->Struct;
const bool bIsDouble = (
(BoundStructProperty == TBaseStructure<FVector2D>::Get()
) ||
(
BoundStructProperty == TBaseStructure<FVector>::Get() ||
BoundStructProperty == TVariantStructure<FVector3d>::Get()
) ||
(
BoundStructProperty == TBaseStructure<FVector4>::Get() ||
BoundStructProperty == TVariantStructure<FVector4d>::Get()
));
if (bIsDouble)
{
const int32 DoublePropertyDefinitionIndex = Properties.IndexOfByPredicate(
[TrackComponents](const FPropertyDefinition& Item) { return Item.PropertyType == TrackComponents->DoubleVector.PropertyTag; });
ensure(DoublePropertyDefinitionIndex != INDEX_NONE);
PropertyInfo.ConvertedFromPropertyDefinitionIndex = PropertyInfo.PropertyDefinitionIndex;
PropertyInfo.PropertyDefinitionIndex = DoublePropertyDefinitionIndex;
}
}
else
{
check(false);
}
}
const int32 OldPropertyDefinitionIndex = PropertyInfo.ConvertedFromPropertyDefinitionIndex.Get(INDEX_NONE);
if (OldPropertyDefinitionIndex == INDEX_NONE)
{
continue;
}
// Now we need to patch the contributors so that they have double-precision components.
// We only need to do it for the *new* contributors discovered this frame.
FComponentTypeID OldPropertyTag, NewPropertyTag;
const FPropertyDefinition& PropertyDefinition = Properties[PropertyInfo.PropertyDefinitionIndex];
if (PropertyDefinition.PropertyType == TrackComponents->Double.PropertyTag)
{
OldPropertyTag = TrackComponents->Float.PropertyTag;
NewPropertyTag = TrackComponents->Double.PropertyTag;
}
else if (PropertyDefinition.PropertyType == TrackComponents->DoubleVector.PropertyTag)
{
OldPropertyTag = TrackComponents->FloatVector.PropertyTag;
NewPropertyTag = TrackComponents->DoubleVector.PropertyTag;
}
if (ensure(OldPropertyTag && NewPropertyTag))
{
// Swap out the property tag
FContributorKey Key(PropertyIndex);
for (auto ContribIt = NewContributors.CreateKeyIterator(Key); ContribIt; ++ContribIt)
{
const FMovieSceneEntityID CurID(ContribIt.Value());
FComponentMask EntityType = Linker->EntityManager.GetEntityType(CurID);
EntityType.Remove(OldPropertyTag);
EntityType.Set(NewPropertyTag);
Linker->EntityManager.ChangeEntityType(CurID, EntityType);
}
// Update contributor info and stats. This is only done the first time this property is encountered.
if (!bHadConversionInfo)
{
--PropertyStats[OldPropertyDefinitionIndex].NumProperties;
++PropertyStats[PropertyInfo.PropertyDefinitionIndex].NumProperties;
}
}
}
}
void UMovieScenePropertyInstantiatorSystem::ProcessInvalidatedProperties(const TBitArray<>& InvalidatedProperties)
{
using namespace UE::MovieScene;
MOVIESCENE_DETAILED_SCOPE_CYCLE_COUNTER(MovieSceneEval_ProcessInvalidatedProperties);
TBitArray<> StaleProperties;
TArrayView<const FPropertyDefinition> Properties = BuiltInComponents->PropertyRegistry.GetProperties();
FPropertyParameters Params;
// This is all random access at this point :(
for (TConstSetBitIterator<> It(InvalidatedProperties); It; ++It)
{
const int32 PropertyIndex = It.GetIndex();
if (!ResolvedProperties.IsValidIndex(PropertyIndex))
{
continue;
}
// Update our view of how this property is animated
Params.PropertyInfo = &ResolvedProperties[PropertyIndex];
Params.PropertyDefinition = &Properties[Params.PropertyInfo->PropertyDefinitionIndex];
Params.PropertyInfoIndex = PropertyIndex;
UpdatePropertyInfo(Params);
// Does it have anything at all contributing to it anymore?
if (!Contributors.Contains(PropertyIndex))
{
StaleProperties.PadToNum(PropertyIndex + 1, false);
StaleProperties[PropertyIndex] = true;
}
// Does it support fast path?
else if (Params.PropertyInfo->bSupportsFastPath)
{
InitializeFastPath(Params);
}
// Else use the (slightly more) expensive blend path
else
{
InitializeBlendPath(Params);
}
}
// Restore and destroy stale properties
if (StaleProperties.Find(true) != INDEX_NONE)
{
for (TConstSetBitIterator<> It(StaleProperties); It; ++It)
{
DestroyStaleProperty(It.GetIndex());
}
PostDestroyStaleProperties();
}
NewContributors.Empty();
}
void UMovieScenePropertyInstantiatorSystem::DestroyStaleProperty(int32 PropertyIndex)
{
FObjectPropertyInfo* PropertyInfo = &ResolvedProperties[PropertyIndex];
if (PropertyInfo->BlendChannel != INVALID_BLEND_CHANNEL)
{
if (UMovieSceneBlenderSystem* Blender = PropertyInfo->Blender.Get())
{
const FMovieSceneBlendChannelID BlendChannelID(Blender->GetBlenderSystemID(), PropertyInfo->BlendChannel);
Blender->ReleaseBlendChannel(BlendChannelID);
}
Linker->EntityManager.AddComponents(PropertyInfo->FinalBlendOutputID, BuiltInComponents->FinishedMask);
}
if (PropertyInfo->bIsPartiallyAnimated)
{
--PropertyStats[PropertyInfo->PropertyDefinitionIndex].NumPartialProperties;
}
--PropertyStats[PropertyInfo->PropertyDefinitionIndex].NumProperties;
ResolvedProperties.RemoveAt(PropertyIndex);
// PropertyInfo is now garbage
}
void UMovieScenePropertyInstantiatorSystem::PostDestroyStaleProperties()
{
ResolvedProperties.Shrink();
}
void UMovieScenePropertyInstantiatorSystem::UpdatePropertyInfo(const FPropertyParameters& Params)
{
using namespace UE::MovieScene;
TArrayView<const FPropertyCompositeDefinition> Composites = BuiltInComponents->PropertyRegistry.GetComposites(*Params.PropertyDefinition);
// This function updates the meta-data associated with a property for each hbias that it is animated from
// There are 3 possible 'modes' for hbias to be considered:
// - Blended means that greater biases are allowed to override lower biases, but still blend with them when the weight is < 1
// - Non-blended hbias simply disables contribution from any lower biases
// - Entities tagged with IgnoreHierarchicalBias will always be relevant and contribute with the highest bias
// Channel masks for all entities, entities within the active hbias, and within the 'ignored hbias' buckets
// Set bits denote channels that are not animated by entities in these contexts
FChannelMask ActiveBiasEmptyChannels(true, Params.PropertyDefinition->CompositeSize);
// Key that visits any contributor to this property regardless of hbias
FContributorKey AnyContributor(Params.PropertyInfoIndex);
int32 NumContributors = 0;
int16 HBias = 0;
bool bSupportsFastPath = true;
bool bWantsRestoreState = false;
bool bNeedsInitialValue = false;
bool bBlendHierarchicalBias = false;
// Iterate all contributors for this property to re-generate the meta-data
for (auto ContributorIt = Contributors.CreateConstKeyIterator(AnyContributor); ContributorIt; ++ContributorIt)
{
FMovieSceneEntityID Contributor = ContributorIt.Value();
const FComponentMask& Type = Linker->EntityManager.GetEntityType(Contributor);
if (Type.Contains(BuiltInComponents->HierarchicalBias))
{
HBias = Linker->EntityManager.ReadComponentChecked(Contributor, BuiltInComponents->HierarchicalBias);
}
// Update the various empty channel masks
for (int32 CompositeIndex = 0; CompositeIndex < Params.PropertyDefinition->CompositeSize; ++CompositeIndex)
{
const bool bCheckChannel = ActiveBiasEmptyChannels[CompositeIndex] == true;
if (bCheckChannel)
{
FComponentTypeID ThisChannel = Composites[CompositeIndex].ComponentTypeID;
if (ThisChannel && Type.Contains(ThisChannel))
{
ActiveBiasEmptyChannels[CompositeIndex] = false;
}
}
}
++NumContributors;
// Update whether this meta-data entry wants restore state
if (!bWantsRestoreState && Type.Contains(BuiltInComponents->Tags.RestoreState))
{
bWantsRestoreState = true;
}
// Update whether this meta-data entry needs an initial value or not
if (!bNeedsInitialValue && Type.Contains(BuiltInComponents->Tags.AlwaysCacheInitialValue))
{
bNeedsInitialValue = true;
}
if (!bBlendHierarchicalBias && Type.Contains(BuiltInComponents->Tags.BlendHierarchicalBias))
{
bBlendHierarchicalBias = true;
bSupportsFastPath = false;
}
// Update whether this property supports fast path
if (bSupportsFastPath)
{
if (NumContributors > 1)
{
bSupportsFastPath = false;
}
else
{
if (Type.Contains(BuiltInComponents->Tags.RelativeBlend) ||
Type.Contains(BuiltInComponents->Tags.AdditiveBlend) ||
Type.Contains(BuiltInComponents->Tags.OverrideBlend) ||
Type.Contains(BuiltInComponents->Tags.AdditiveFromBaseBlend) ||
Type.Contains(BuiltInComponents->WeightAndEasingResult))
{
bSupportsFastPath = false;
}
}
}
}
// Reset the restore state status of the property if we still have contributors
// We do not do this if there are no contributors to ensure that stale properties are restored correctly
Params.PropertyInfo->EmptyChannels = ActiveBiasEmptyChannels;
const bool bWasPartial = Params.PropertyInfo->bIsPartiallyAnimated;
const bool bIsPartial = Params.PropertyInfo->EmptyChannels.Find(true) != INDEX_NONE;
if (bWasPartial != bIsPartial)
{
const int32 StatIndex = Params.PropertyInfo->PropertyDefinitionIndex;
PropertyStats[StatIndex].NumPartialProperties += bIsPartial ? 1 : -1;
}
Params.PropertyInfo->bIsPartiallyAnimated = bIsPartial;
Params.PropertyInfo->bMaxHBiasHasChanged = Params.PropertyInfo->HBias != HBias;
Params.PropertyInfo->HBias = HBias;
Params.PropertyInfo->bSupportsFastPath = bSupportsFastPath;
Params.PropertyInfo->bWantsRestoreState = bWantsRestoreState;
Params.PropertyInfo->bNeedsInitialValue = bNeedsInitialValue;
}
void UMovieScenePropertyInstantiatorSystem::InitializeFastPath(const FPropertyParameters& Params)
{
using namespace UE::MovieScene;
// Find the sole contributor with the specific property info and hbias
const int16 ActiveHBias = Params.PropertyInfo->HBias;
FContributorKey AnyContributor(Params.PropertyInfoIndex);
for (auto ContributorIt = Contributors.CreateConstKeyIterator(AnyContributor); ContributorIt; ++ContributorIt)
{
FMovieSceneEntityID Contributor = ContributorIt.Value();
FTypelessMutation SoleContributorMutation;
SoleContributorMutation.RemoveMask.SetAll({ BuiltInComponents->BlendChannelInput, BuiltInComponents->HierarchicalBlendTarget });
if (Params.PropertyInfo->bNeedsInitialValue)
{
SoleContributorMutation.AddMask.Set(Params.PropertyDefinition->InitialValueType);
}
if (Params.PropertyDefinition->MetaDataTypes.Num() > 0)
{
InitializePropertyMetaDataTasks.PadToNum(Params.PropertyInfo->PropertyDefinitionIndex+1, false);
InitializePropertyMetaDataTasks[Params.PropertyInfo->PropertyDefinitionIndex] = true;
for (FComponentTypeID Component : Params.PropertyDefinition->MetaDataTypes)
{
SoleContributorMutation.AddMask.Set(Component);
}
}
// Ensure the sole contributor is set up to apply the property as a final output
switch (Params.PropertyInfo->Property.GetIndex())
{
case 0:
FEntityBuilder()
.Add(BuiltInComponents->FastPropertyOffset, Params.PropertyInfo->Property.template Get<uint16>())
.MutateExisting(&Linker->EntityManager, Contributor, SoleContributorMutation);
break;
case 1:
FEntityBuilder()
.Add(BuiltInComponents->CustomPropertyIndex, Params.PropertyInfo->Property.template Get<FCustomPropertyIndex>())
.MutateExisting(&Linker->EntityManager, Contributor, SoleContributorMutation);
break;
case 2:
FEntityBuilder()
.Add(BuiltInComponents->SlowProperty, Params.PropertyInfo->Property.template Get<FSlowPropertyPtr>())
.MutateExisting(&Linker->EntityManager, Contributor, SoleContributorMutation);
break;
}
// Copy initial values and meta-data back off our old blend output
FMovieSceneEntityID OldOutput = Params.PropertyInfo->PreviousFastPathID ? Params.PropertyInfo->PreviousFastPathID : Params.PropertyInfo->FinalBlendOutputID;
if (OldOutput)
{
FComponentMask CopyMask;
CopyMask.Set(Params.PropertyDefinition->InitialValueType);
CopyMask.Set(BuiltInComponents->Tags.HasAssignedInitialValue);
for (FComponentTypeID Component : Params.PropertyDefinition->MetaDataTypes)
{
CopyMask.Set(Component);
}
Linker->EntityManager.CopyComponents(OldOutput, Contributor, CopyMask);
}
Params.PropertyInfo->PreviousFastPathID = Contributor;
}
// If this was previously blended, destroy the blend output
if (Params.PropertyInfo->FinalBlendOutputID)
{
check(!Linker->EntityManager.HasComponent(Params.PropertyInfo->FinalBlendOutputID, BuiltInComponents->BlendChannelInput));
UMovieSceneBlenderSystem* Blender = Params.PropertyInfo->Blender.Get();
if (Blender && Params.PropertyInfo->BlendChannel != INVALID_BLEND_CHANNEL)
{
const FMovieSceneBlendChannelID BlendChannelID(Blender->GetBlenderSystemID(), Params.PropertyInfo->BlendChannel);
Blender->ReleaseBlendChannel(BlendChannelID);
Params.PropertyInfo->BlendChannel = INVALID_BLEND_CHANNEL;
Params.PropertyInfo->Blender = nullptr;
}
Linker->EntityManager.AddComponent(Params.PropertyInfo->FinalBlendOutputID, BuiltInComponents->Tags.NeedsUnlink);
Params.PropertyInfo->FinalBlendOutputID = FMovieSceneEntityID();
}
}
UMovieScenePropertyInstantiatorSystem::FSetupBlenderSystemResult UMovieScenePropertyInstantiatorSystem::SetupBlenderSystem(const FPropertyParameters& Params)
{
using namespace UE::MovieScene;
UClass* NewBlenderClass = nullptr;
int32 BlenderClassPriority = TNumericLimits<int32>::Lowest();
// Iterate all the contributors to locate the correct blender system by-priority
FContributorKey ContributorKey = Params.MakeContributorKey();
for (auto ContributorIt = Contributors.CreateConstKeyIterator(ContributorKey); ContributorIt; ++ContributorIt)
{
FMovieSceneEntityID Contributor = ContributorIt.Value();
TOptionalComponentReader<TSubclassOf<UMovieSceneBlenderSystem>> BlenderTypeComponent = Linker->EntityManager.ReadComponent(Contributor, BuiltInComponents->BlenderType);
if (!BlenderTypeComponent)
{
continue;
}
UClass* ProspectiveBlenderClass = BlenderTypeComponent->Get();
if (NewBlenderClass == ProspectiveBlenderClass)
{
// If it's already the same, don't waste time getting the CDO or anything like that
continue;
}
const UMovieSceneBlenderSystem* CDO = GetDefault<UMovieSceneBlenderSystem>(ProspectiveBlenderClass);
if (!NewBlenderClass || CDO->GetSelectionPriority() > BlenderClassPriority)
{
NewBlenderClass = ProspectiveBlenderClass;
BlenderClassPriority = CDO->GetSelectionPriority();
}
else
{
#if DO_CHECK
ensureMsgf(CDO->GetSelectionPriority() != BlenderClassPriority,
TEXT("Encountered 2 different blender classes being used with the same priority - this is undefined behavior. Please check the system classes to ensure they have different priorities (%s and %s)."),
*ProspectiveBlenderClass->GetName(), *NewBlenderClass->GetName());
#endif
}
}
if (!NewBlenderClass)
{
NewBlenderClass = Params.PropertyDefinition->BlenderSystemClass;
}
if (!NewBlenderClass)
{
return FSetupBlenderSystemResult();
}
FComponentTypeID BlenderTypeTag = GetDefault<UMovieSceneBlenderSystem>(NewBlenderClass)->GetBlenderTypeTag();
ensureMsgf(BlenderTypeTag, TEXT("Encountered a blender system (%s) with an invalid type tag."), *NewBlenderClass->GetName());
FBlenderSystemInfo NewBlenderInfo{ NewBlenderClass, BlenderTypeTag };
FBlenderSystemInfo OldBlenderInfo;
UMovieSceneBlenderSystem* ExistingBlender = Params.PropertyInfo->Blender.Get();
if (ExistingBlender)
{
UClass* OldBlenderClass = ExistingBlender->GetClass();
if (OldBlenderClass != NewBlenderClass)
{
OldBlenderInfo = FBlenderSystemInfo{ OldBlenderClass, ExistingBlender->GetBlenderTypeTag() };
}
else
{
// It's the same - keep the same info
OldBlenderInfo = NewBlenderInfo;
}
}
return FSetupBlenderSystemResult{NewBlenderInfo, OldBlenderInfo};
}
void UMovieScenePropertyInstantiatorSystem::InitializeBlendPath(const FPropertyParameters& Params)
{
using namespace UE::MovieScene;
TArrayView<const FPropertyCompositeDefinition> Composites = BuiltInComponents->PropertyRegistry.GetComposites(*Params.PropertyDefinition);
FSetupBlenderSystemResult SetupResult = SetupBlenderSystem(Params);
// -----------------------------------------------------------------------------------------------------
// Situation 0: there is no blender system for this type of property. Normally, this means we can't
// end up trying to blend it, but it may happen if that property is inside a sub-sequence with hierarchical
// easing.
if (!SetupResult.CurrentInfo.BlenderSystemClass)
{
InitializeFastPath(Params);
return;
}
// -----------------------------------------------------------------------------------------------------
// Situation 1: New or modified contributors (inputs) but we're already set up for blending using the same system
if (Params.PropertyInfo->FinalBlendOutputID && SetupResult.CurrentInfo.BlenderSystemClass == SetupResult.PreviousInfo.BlenderSystemClass)
{
UMovieSceneBlenderSystem* Blender = Params.PropertyInfo->Blender.Get();
check(Blender);
// Ensure the output entity still matches the correct set of channels of all inputs
FComponentMask NewEntityType;
Params.MakeOutputComponentType(Linker->EntityManager, Composites, NewEntityType);
Linker->EntityManager.ChangeEntityType(Params.PropertyInfo->FinalBlendOutputID, NewEntityType);
const FMovieSceneBlendChannelID BlendChannel(Blender->GetBlenderSystemID(), Params.PropertyInfo->BlendChannel);
// Change new contributors to include the blend input components
FContributorKey ContributorKey = Params.MakeContributorKey();
TMultiMap<FContributorKey, FMovieSceneEntityID>::TConstKeyIterator ContributorIt = Params.PropertyInfo->bMaxHBiasHasChanged
? Contributors.CreateConstKeyIterator(ContributorKey)
: NewContributors.CreateConstKeyIterator(ContributorKey);
for (; ContributorIt; ++ContributorIt)
{
FEntityBuilder()
.Add(BuiltInComponents->BlendChannelInput, BlendChannel)
.AddTag(SetupResult.CurrentInfo.BlenderTypeTag)
.MutateExisting(&Linker->EntityManager, ContributorIt.Value());
}
check(!Linker->EntityManager.HasComponent(Params.PropertyInfo->FinalBlendOutputID, BuiltInComponents->BlendChannelInput));
// Nothing more to do
return;
}
// -----------------------------------------------------------------------------------------------------
// Situation 2: Never used blending before, or the blender type has changed
UMovieSceneBlenderSystem* OldBlender = Params.PropertyInfo->Blender.Get();
UMovieSceneBlenderSystem* NewBlender = CastChecked<UMovieSceneBlenderSystem>(Linker->LinkSystem(SetupResult.CurrentInfo.BlenderSystemClass));
const FMovieSceneBlendChannelID NewBlendChannel = NewBlender->AllocateBlendChannel();
FTypelessMutation InputMutation;
// Clean any previously-fast-path entities
InputMutation.RemoveMask = CleanFastPathMask;
InputMutation.RemoveMask.Set(Params.PropertyDefinition->InitialValueType);
InputMutation.RemoveMask.Set(BuiltInComponents->Tags.HasAssignedInitialValue);
for (FComponentTypeID Component : Params.PropertyDefinition->MetaDataTypes)
{
InputMutation.RemoveMask.Set(Component);
}
// -----------------------------------------------------------------------------------------------------
// Situation 2.1: We're already set up for blending, but with a different blender system
if (OldBlender)
{
const FMovieSceneBlendChannelID OldBlendChannel(OldBlender->GetBlenderSystemID(), Params.PropertyInfo->BlendChannel);
OldBlender->ReleaseBlendChannel(OldBlendChannel);
Params.PropertyInfo->Blender = NewBlender;
Params.PropertyInfo->BlendChannel = NewBlendChannel.ChannelID;
// Change the output entity by adding the new blend channel and tag, while simultaneously
// updating the channels and restore state flags etc added by MakeOutputComponentType
{
FTypelessMutation OutputMutation;
OutputMutation.RemoveAll();
Params.MakeOutputComponentType(Linker->EntityManager, Composites, OutputMutation.AddMask);
// Remove the old blender type tag before add the new one
OutputMutation.AddMask.Remove({ SetupResult.PreviousInfo.BlenderTypeTag });
FEntityBuilder()
.Add(BuiltInComponents->BlendChannelOutput, NewBlendChannel)
.AddTag(SetupResult.CurrentInfo.BlenderTypeTag)
.MutateExisting(&Linker->EntityManager, Params.PropertyInfo->FinalBlendOutputID, OutputMutation);
}
// Ensure that the old blend tag is removed from inputs
InputMutation.Remove({ SetupResult.PreviousInfo.BlenderTypeTag });
}
// -----------------------------------------------------------------------------------------------------
// Situation 2.2: Never encountered blending before - need to create a new output entity to receive the blend result
else
{
Params.PropertyInfo->Blender = NewBlender;
Params.PropertyInfo->BlendChannel = NewBlendChannel.ChannelID;
FComponentMask NewMask;
NewMask.Set(Params.PropertyDefinition->InitialValueType);
if (Params.PropertyDefinition->MetaDataTypes.Num() > 0)
{
InitializePropertyMetaDataTasks.PadToNum(Params.PropertyInfo->PropertyDefinitionIndex+1, false);
InitializePropertyMetaDataTasks[Params.PropertyInfo->PropertyDefinitionIndex] = true;
for (FComponentTypeID Component : Params.PropertyDefinition->MetaDataTypes)
{
NewMask.Set(Component);
}
}
for (int32 Index = 0; Index < Composites.Num(); ++Index)
{
if (Params.PropertyInfo->EmptyChannels[Index] == false)
{
NewMask.Set(Composites[Index].ComponentTypeID);
}
}
NewMask.Set(Params.PropertyDefinition->PropertyType);
FMovieSceneEntityID NewOutputEntityID;
auto NewOutputEntity = FEntityBuilder()
.Add(BuiltInComponents->BlendChannelOutput, NewBlendChannel)
.Add(BuiltInComponents->PropertyBinding, Params.PropertyInfo->PropertyBinding)
.Add(BuiltInComponents->BoundObject, Params.PropertyInfo->BoundObject)
.AddTagConditional(BuiltInComponents->Tags.RestoreState, Params.PropertyInfo->bWantsRestoreState)
.AddTag(SetupResult.CurrentInfo.BlenderTypeTag)
.AddTag(BuiltInComponents->Tags.NeedsLink)
.AddMutualComponents();
switch (Params.PropertyInfo->Property.GetIndex())
{
// Never seen this property before
case 0:
NewOutputEntityID = NewOutputEntity
.Add(BuiltInComponents->FastPropertyOffset, Params.PropertyInfo->Property.template Get<uint16>())
.CreateEntity(&Linker->EntityManager, NewMask);
break;
case 1:
NewOutputEntityID = NewOutputEntity
.Add(BuiltInComponents->CustomPropertyIndex, Params.PropertyInfo->Property.template Get<FCustomPropertyIndex>())
.CreateEntity(&Linker->EntityManager, NewMask);
break;
case 2:
NewOutputEntityID = NewOutputEntity
.Add(BuiltInComponents->SlowProperty, Params.PropertyInfo->Property.template Get<FSlowPropertyPtr>())
.CreateEntity(&Linker->EntityManager, NewMask);
break;
}
if (Params.PropertyInfo->PreviousFastPathID)
{
// If this contributor has the initial values on it, we copy its initial values and meta-data components
FComponentMask CopyMask = Linker->EntityManager.GetComponents()->GetCopyAndMigrationMask();
CopyMask.Set(Params.PropertyDefinition->InitialValueType);
CopyMask.Set(BuiltInComponents->Tags.HasAssignedInitialValue);
for (FComponentTypeID Component : Params.PropertyDefinition->MetaDataTypes)
{
CopyMask.Set(Component);
}
Linker->EntityManager.CopyComponents(Params.PropertyInfo->PreviousFastPathID, NewOutputEntityID, CopyMask);
}
// The property entity ID is now the blend output entity
Params.PropertyInfo->FinalBlendOutputID = NewOutputEntityID;
Params.PropertyInfo->PreviousFastPathID = FMovieSceneEntityID();
check(!Linker->EntityManager.HasComponent(Params.PropertyInfo->FinalBlendOutputID, BuiltInComponents->BlendChannelInput));
}
// Change *all* contributors (not just new ones because the old ones will have the old blender's channel and tag on them)
// to include the new blend channel input, and remove the old blender type. No need to remove the clean fast path mask because
// that will have already happened as part of the 'completely new blending' branch below
FContributorKey ContributorKey(Params.PropertyInfoIndex);
for (auto ContributorIt = Contributors.CreateConstKeyIterator(ContributorKey); ContributorIt; ++ContributorIt)
{
FMovieSceneEntityID Contributor = ContributorIt.Value();
FEntityBuilder()
.Add(BuiltInComponents->BlendChannelInput, NewBlendChannel)
.AddTag(SetupResult.CurrentInfo.BlenderTypeTag)
.MutateExisting(&Linker->EntityManager, Contributor, InputMutation);
}
check(!Linker->EntityManager.HasComponent(Params.PropertyInfo->FinalBlendOutputID, BuiltInComponents->BlendChannelInput));
}
bool UMovieScenePropertyInstantiatorSystem::ResolveProperty(UE::MovieScene::FCustomAccessorView CustomAccessors, UObject* Object, const FMovieScenePropertyBinding& PropertyBinding, const UE::MovieScene::FEntityGroupID& GroupID, int32 PropertyDefinitionIndex)
{
using namespace UE::MovieScene;
if (ResolvedProperties.IsValidIndex(GroupID.GroupIndex))
{
#if !UE_BUILD_SHIPPING
const FObjectPropertyInfo& ResolvedProperty = ResolvedProperties[GroupID.GroupIndex];
ensure(ResolvedProperty.BoundObject == Object);
ensure(ResolvedProperty.PropertyBinding.PropertyPath == PropertyBinding.PropertyPath);
#endif
return true;
}
TOptional<FResolvedProperty> ResolvedProperty = FPropertyRegistry::ResolveProperty(Object, PropertyBinding, CustomAccessors);
if (!ResolvedProperty.IsSet())
{
UE_LOG(LogMovieScene, Warning, TEXT("Unable to resolve property '%s' from '%s' instance '%s'"), *PropertyBinding.PropertyPath.ToString(), *Object->GetClass()->GetName(), *Object->GetName());
return false;
}
ResolvedProperties.EmplaceAt(GroupID.GroupIndex, MoveTemp(ResolvedProperty.GetValue()));
FObjectPropertyInfo& NewInfo = ResolvedProperties[GroupID.GroupIndex];
NewInfo.BoundObject = Object;
NewInfo.PropertyBinding = PropertyBinding;
NewInfo.PropertyDefinitionIndex = PropertyDefinitionIndex;
++PropertyStats[PropertyDefinitionIndex].NumProperties;
return true;
}
UE::MovieScene::FPropertyRecomposerPropertyInfo UMovieScenePropertyInstantiatorSystem::FindPropertyFromSource(FMovieSceneEntityID EntityID, UObject* Object) const
{
using namespace UE::MovieScene;
TOptionalComponentReader<FMovieScenePropertyBinding> PropertyBinding = Linker->EntityManager.ReadComponent(EntityID, BuiltInComponents->PropertyBinding);
if (!PropertyBinding)
{
return FPropertyRecomposerPropertyInfo::Invalid();
}
TOptionalComponentReader<FEntityGroupID> GroupID = Linker->EntityManager.ReadComponent(EntityID, BuiltInComponents->Group);
if (!GroupID)
{
// If this Entity didn't have a group, see if any of its children are bound to the same object and use that
Linker->EntityManager.IterateImmediateChildren(EntityID, [this, Object, &GroupID](FMovieSceneEntityID ChildEntityID){
TOptionalComponentReader<UObject*> BoundObject = this->Linker->EntityManager.ReadComponent(ChildEntityID, BuiltInComponents->BoundObject);
TOptionalComponentReader<FEntityGroupID> ChildGroup = this->Linker->EntityManager.ReadComponent(ChildEntityID, BuiltInComponents->Group);
if (BoundObject && *BoundObject == Object && ChildGroup)
{
GroupID = MoveTemp(ChildGroup);
}
});
}
if (!GroupID)
{
return FPropertyRecomposerPropertyInfo::Invalid();
}
const int32 PropertyIndex = GroupID->GroupIndex;
if (PropertyIndex != INDEX_NONE && ensure(ResolvedProperties.IsValidIndex(PropertyIndex)))
{
const uint16 BlendChannel = ResolvedProperties[PropertyIndex].BlendChannel;
const FObjectPropertyInfo& PropertyInfo = ResolvedProperties[PropertyIndex];
return FPropertyRecomposerPropertyInfo { BlendChannel, PropertyInfo.Blender.Get(), PropertyInfo.FinalBlendOutputID };
}
return FPropertyRecomposerPropertyInfo::Invalid();
}
void UMovieScenePropertyInstantiatorSystem::InitializePropertyMetaData(FSystemTaskPrerequisites& InPrerequisites, FSystemSubsequentTasks& Subsequents)
{
using namespace UE::MovieScene;
MOVIESCENE_DETAILED_SCOPE_CYCLE_COUNTER(MovieSceneEval_InitializePropertyMetaData);
for (TConstSetBitIterator<> TypesToCache(InitializePropertyMetaDataTasks); TypesToCache; ++TypesToCache)
{
FCompositePropertyTypeID PropertyID = FCompositePropertyTypeID::FromIndex(TypesToCache.GetIndex());
const FPropertyDefinition& Definition = BuiltInComponents->PropertyRegistry.GetDefinition(PropertyID);
Definition.Handler->DispatchInitializePropertyMetaDataTasks(Definition, InPrerequisites, Subsequents, Linker);
}
InitializePropertyMetaDataTasks.Empty();
}
void UMovieScenePropertyInstantiatorSystem::FPropertyParameters::MakeOutputComponentType(
const UE::MovieScene::FEntityManager& EntityManager,
TArrayView<const UE::MovieScene::FPropertyCompositeDefinition> Composites,
UE::MovieScene::FComponentMask& OutComponentType) const
{
using namespace UE::MovieScene;
// Get the existing type
if (PropertyInfo->FinalBlendOutputID)
{
OutComponentType = EntityManager.GetEntityType(PropertyInfo->FinalBlendOutputID);
}
// Ensure the property has only the exact combination of channels that constitute its animation
for (int32 Index = 0; Index < Composites.Num(); ++Index)
{
FComponentTypeID Composite = Composites[Index].ComponentTypeID;
if (PropertyInfo->EmptyChannels[Index] != true)
{
OutComponentType.Set(Composite);
}
else
{
OutComponentType.Remove(Composite);
}
}
OutComponentType.Set(PropertyDefinition->PropertyType);
// Set the restore state tag appropriately
if (PropertyInfo->bWantsRestoreState)
{
OutComponentType.Set(FBuiltInComponentTypes::Get()->Tags.RestoreState);
}
else
{
OutComponentType.Remove(FBuiltInComponentTypes::Get()->Tags.RestoreState);
}
}