// Copyright Epic Games, Inc. All Rights Reserved. #include "Evaluation/MovieSceneEvaluationField.h" #include "Evaluation/MovieSceneEvaluationTemplateInstance.h" #include "Evaluation/MovieSceneEvaluationTree.h" #include "MovieSceneCommonHelpers.h" #include "MovieSceneTimeHelpers.h" #include "Algo/Sort.h" #include "Algo/BinarySearch.h" #include "Algo/IndexOf.h" #include "EntitySystem/IMovieSceneEntityProvider.h" #include "EntitySystem/MovieSceneEntityIDs.h" #include "EntitySystem/MovieSceneEntityManager.h" #include "MovieSceneSequence.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneEvaluationField) FMovieSceneEntityComponentFieldBuilder::FMovieSceneEntityComponentFieldBuilder(FMovieSceneEntityComponentField* InField) : Field(InField) , SharedMetaDataIndex(Field->SharedMetaData.Emplace()) {} FMovieSceneEntityComponentFieldBuilder::~FMovieSceneEntityComponentFieldBuilder() { const bool bContainsValidEntities = Algo::AnyOf(KeyToFieldIndex, [](FKeyToIndex In) { return In.FieldIndex != INDEX_NONE; }); if (!bContainsValidEntities) { if (ensureMsgf(Field->SharedMetaData.Num() == SharedMetaDataIndex+1, TEXT("Additional shared meta-data has been added since this builder was constructed, recursive builders are not supported"))) { Field->SharedMetaData.RemoveAt(SharedMetaDataIndex, EAllowShrinking::No); } } } FMovieSceneEvaluationFieldSharedEntityMetaData& FMovieSceneEntityComponentFieldBuilder::GetSharedMetaData() { return Field->SharedMetaData[SharedMetaDataIndex]; } int32 FMovieSceneEntityComponentFieldBuilder::GetSharedMetaDataIndex() const { return SharedMetaDataIndex; } int32 FMovieSceneEntityComponentFieldBuilder::FindOrAddEntity(UObject* EntityOwner, uint32 EntityID) { FMovieSceneEvaluationFieldEntityKey Key = { EntityOwner, EntityID }; int32 LocalIndex = Algo::IndexOfBy(KeyToFieldIndex, Key, &FKeyToIndex::Key); if (LocalIndex != INDEX_NONE) { return LocalIndex; } return KeyToFieldIndex.Add(FKeyToIndex{ Key, INDEX_NONE }); } int32 FMovieSceneEntityComponentFieldBuilder::AddMetaData(const FMovieSceneEvaluationFieldEntityMetaData& InMetaData) { if (InMetaData.IsRedundant()) { return INDEX_NONE; } int32 LocalIndex = Algo::IndexOfBy(MetaDataToFieldIndex, InMetaData, &FMetaDataToIndex::MetaData); if (LocalIndex == INDEX_NONE) { LocalIndex = MetaDataToFieldIndex.Add(FMetaDataToIndex{ InMetaData, INDEX_NONE }); } return LocalIndex; } void FMovieSceneEntityComponentFieldBuilder::AddPersistentEntity(const TRange& Range, UObject* EntityOwner, uint32 EntityID, int32 LocalMetaDataIndex) { AddPersistentEntity(Range, FindOrAddEntity(EntityOwner, EntityID), LocalMetaDataIndex); } void FMovieSceneEntityComponentFieldBuilder::AddPersistentEntity(const TRange& Range, int32 LocalIndex, int32 LocalMetaDataIndex) { FMovieSceneEvaluationFieldEntityTree::FEntityAndMetaDataIndex Data = { LocalEntityIndexToFieldIndex(LocalIndex), LocalMetaDataIndexToFieldIndex(LocalMetaDataIndex) }; Field->PersistentEntityTree.SerializedData.AddUnique(Range, Data); } void FMovieSceneEntityComponentFieldBuilder::AddOneShotEntity(const TRange& OneShotRange, UObject* EntityOwner, uint32 EntityID, int32 LocalMetaDataIndex) { AddOneShotEntity(OneShotRange, FindOrAddEntity(EntityOwner, EntityID), LocalMetaDataIndex); } void FMovieSceneEntityComponentFieldBuilder::AddOneShotEntity(const TRange& OneShotRange, int32 LocalIndex, int32 LocalMetaDataIndex) { FMovieSceneEvaluationFieldEntityTree::FEntityAndMetaDataIndex Data = { LocalEntityIndexToFieldIndex(LocalIndex), LocalMetaDataIndexToFieldIndex(LocalMetaDataIndex) }; Field->OneShotEntityTree.SerializedData.AddUnique(OneShotRange, Data); } int32 FMovieSceneEntityComponentFieldBuilder::LocalEntityIndexToFieldIndex(int32 LocalIndex) { checkf(KeyToFieldIndex.IsValidIndex(LocalIndex), TEXT("Invalid local entity index specified")); FKeyToIndex& KeyToIndex = KeyToFieldIndex[LocalIndex]; if (KeyToIndex.FieldIndex == INDEX_NONE) { KeyToIndex.FieldIndex = Field->Entities.Emplace(FMovieSceneEvaluationFieldEntity{ KeyToIndex.Key, SharedMetaDataIndex }); } return KeyToIndex.FieldIndex; } int32 FMovieSceneEntityComponentFieldBuilder::LocalMetaDataIndexToFieldIndex(int32 LocalIndex) { if (LocalIndex == INDEX_NONE) { return INDEX_NONE; } checkf(MetaDataToFieldIndex.IsValidIndex(LocalIndex), TEXT("Invalid local meta-data index specified")); FMetaDataToIndex& MetaDataToIndex = MetaDataToFieldIndex[LocalIndex]; if (MetaDataToIndex.FieldIndex == INDEX_NONE) { MetaDataToIndex.FieldIndex = Field->EntityMetaData.Emplace(MetaDataToIndex.MetaData); } return MetaDataToIndex.FieldIndex; } void FMovieSceneEntityComponentField::QueryPersistentEntities(FFrameNumber QueryTime, TRange& OutRange, FMovieSceneEvaluationFieldEntitySet& OutEntities) const { FMovieSceneEvaluationTreeRangeIterator Iterator = PersistentEntityTree.SerializedData.IterateFromTime(QueryTime); check(Iterator); OutRange = Iterator.Range(); for (FMovieSceneEvaluationFieldEntityTree::FEntityAndMetaDataIndex Pair : PersistentEntityTree.SerializedData.GetAllData(Iterator.Node())) { OutEntities.Add(FMovieSceneEvaluationFieldEntityQuery{ GetEntity(Pair.EntityIndex), Pair.MetaDataIndex }); } } void FMovieSceneEntityComponentField::QueryPersistentEntities(FFrameNumber QueryTime, TFunctionRef QueryCallback, TRange& OutRange) const { FMovieSceneEvaluationTreeRangeIterator Iterator = PersistentEntityTree.SerializedData.IterateFromTime(QueryTime); check(Iterator); OutRange = Iterator.Range(); for (FMovieSceneEvaluationFieldEntityTree::FEntityAndMetaDataIndex Pair : PersistentEntityTree.SerializedData.GetAllData(Iterator.Node())) { FMovieSceneEvaluationFieldEntityQuery Query{ GetEntity(Pair.EntityIndex), Pair.MetaDataIndex }; if (!QueryCallback(Query)) { return; } } } bool FMovieSceneEntityComponentField::HasAnyOneShotEntities() const { return !OneShotEntityTree.SerializedData.IsEmpty(); } void FMovieSceneEntityComponentField::QueryOneShotEntities(const TRange& QueryRange, FMovieSceneEvaluationFieldEntitySet& OutEntities) const { FMovieSceneEvaluationTreeRangeIterator Iterator = OneShotEntityTree.SerializedData.IterateFromLowerBound(QueryRange.GetLowerBound()); check(Iterator); for ( ; Iterator && QueryRange.Overlaps(Iterator.Range()); ++Iterator ) { for (FMovieSceneEvaluationFieldEntityTree::FEntityAndMetaDataIndex Pair : OneShotEntityTree.SerializedData.GetAllData(Iterator.Node())) { OutEntities.Add(FMovieSceneEvaluationFieldEntityQuery{ GetEntity(Pair.EntityIndex), Pair.MetaDataIndex, }); } } } int32 FMovieSceneEvaluationField::GetSegmentFromTime(FFrameNumber Time) const { // Linear search // @todo: accelerated search based on the last evaluated index? for (int32 Index = 0; Index < Ranges.Num(); ++Index) { if (Ranges[Index].Value.Contains(Time)) { return Index; } } return INDEX_NONE; } TRange FMovieSceneEvaluationField::OverlapRange(const TRange& Range) const { if (Ranges.Num() == 0) { return TRange::Empty(); } TArrayView RangesToSearch(Ranges); // Binary search the first lower bound that's greater than the input range's lower bound int32 StartIndex = Algo::UpperBoundBy(RangesToSearch, Range.GetLowerBound(), &FMovieSceneFrameRange::GetLowerBound, MovieSceneHelpers::SortLowerBounds); // StartIndex is always <= RangesToSearch.Num(). If the previous range overlaps the input range, include that if (StartIndex > 0 && RangesToSearch[StartIndex-1].Value.Overlaps(Range)) { StartIndex = StartIndex - 1; } if (StartIndex == RangesToSearch.Num()) { return TRange::Empty(); } // Search the remaining ranges for the last upper bound greater than the input range RangesToSearch = RangesToSearch.Slice(StartIndex, RangesToSearch.Num() - StartIndex); // Binary search the first lower bound that is greater than or equal to the input range's upper bound int32 Length = Range.GetUpperBound().IsOpen() ? RangesToSearch.Num() : Algo::UpperBoundBy(RangesToSearch, Range.GetUpperBound(), &FMovieSceneFrameRange::GetUpperBound, MovieSceneHelpers::SortUpperBounds); // Length is always <= RangesToSearch.Num(). If the previous range overlaps the input range, include that if (Length < RangesToSearch.Num() && RangesToSearch[Length].Value.Overlaps(Range)) { Length = Length + 1; } return Length > 0 ? TRange(StartIndex, StartIndex + Length) : TRange::Empty(); } void FMovieSceneEvaluationField::Invalidate(const TRange& Range) { TRange OverlappingRange = OverlapRange(Range); if (!OverlappingRange.IsEmpty()) { Ranges.RemoveAt(OverlappingRange.GetLowerBoundValue(), OverlappingRange.Size(), EAllowShrinking::No); Groups.RemoveAt(OverlappingRange.GetLowerBoundValue(), OverlappingRange.Size(), EAllowShrinking::No); MetaData.RemoveAt(OverlappingRange.GetLowerBoundValue(), OverlappingRange.Size(), EAllowShrinking::No); #if WITH_EDITORONLY_DATA Signature = FGuid::NewGuid(); #endif } } int32 FMovieSceneEvaluationField::Insert(const TRange& InRange, FMovieSceneEvaluationGroup&& InGroup, FMovieSceneEvaluationMetaData&& InMetaData) { const int32 InsertIndex = Algo::UpperBoundBy(Ranges, InRange.GetLowerBound(), &FMovieSceneFrameRange::GetLowerBound, MovieSceneHelpers::SortLowerBounds); const bool bOverlapping = (Ranges.IsValidIndex(InsertIndex ) && Ranges[InsertIndex ].Value.Overlaps(InRange)) || (Ranges.IsValidIndex(InsertIndex-1) && Ranges[InsertIndex-1].Value.Overlaps(InRange)); if (!ensureAlwaysMsgf(!bOverlapping, TEXT("Attempting to insert an overlapping range into the evaluation field."))) { return INDEX_NONE; } Ranges.Insert(InRange, InsertIndex); MetaData.Insert(MoveTemp(InMetaData), InsertIndex); Groups.Insert(MoveTemp(InGroup), InsertIndex); #if WITH_EDITORONLY_DATA Signature = FGuid::NewGuid(); #endif return InsertIndex; } void FMovieSceneEvaluationField::Add(const TRange& InRange, FMovieSceneEvaluationGroup&& InGroup, FMovieSceneEvaluationMetaData&& InMetaData) { if (ensureAlwaysMsgf(!Ranges.Num() || !Ranges.Last().Value.Overlaps(InRange), TEXT("Attempting to add overlapping ranges to sequence evaluation field."))) { Ranges.Add(InRange); MetaData.Add(MoveTemp(InMetaData)); Groups.Add(MoveTemp(InGroup)); #if WITH_EDITORONLY_DATA Signature = FGuid::NewGuid(); #endif } } void FMovieSceneEvaluationMetaData::DiffSequences(const FMovieSceneEvaluationMetaData& LastFrame, TArray* NewSequences, TArray* ExpiredSequences) const { // This algorithm works on the premise that each array is sorted, and each ID can only appear once auto ThisFrameIDs = ActiveSequences.CreateConstIterator(); auto LastFrameIDs = LastFrame.ActiveSequences.CreateConstIterator(); // Iterate both arrays together while (ThisFrameIDs && LastFrameIDs) { FMovieSceneSequenceID ThisID = *ThisFrameIDs; FMovieSceneSequenceID LastID = *LastFrameIDs; // If they're the same, skip if (ThisID == LastID) { ++ThisFrameIDs; ++LastFrameIDs; continue; } if (LastID < ThisID) { // Last frame iterator is less than this frame's, which means the last ID is no longer evaluated if (ExpiredSequences) { ExpiredSequences->Add(LastID); } ++LastFrameIDs; } else { // Last frame iterator is greater than this frame's, which means this ID is new if (NewSequences) { NewSequences->Add(ThisID); } ++ThisFrameIDs; } } // Add any remaining expired sequences if (ExpiredSequences) { while (LastFrameIDs) { ExpiredSequences->Add(*LastFrameIDs); ++LastFrameIDs; } } // Add any remaining new sequences if (NewSequences) { while (ThisFrameIDs) { NewSequences->Add(*ThisFrameIDs); ++ThisFrameIDs; } } } void FMovieSceneEvaluationMetaData::DiffEntities(const FMovieSceneEvaluationMetaData& LastFrame, TArray* NewKeys, TArray* ExpiredKeys) const { // This algorithm works on the premise that each array is sorted, and each ID can only appear once auto ThisFrameKeys = ActiveEntities.CreateConstIterator(); auto LastFrameKeys = LastFrame.ActiveEntities.CreateConstIterator(); // Iterate both arrays together while (ThisFrameKeys && LastFrameKeys) { FMovieSceneOrderedEvaluationKey ThisKey = *ThisFrameKeys; FMovieSceneOrderedEvaluationKey LastKey = *LastFrameKeys; // If they're the same, skip if (ThisKey.Key == LastKey.Key) { ++ThisFrameKeys; ++LastFrameKeys; continue; } if (LastKey.Key < ThisKey.Key) { // Last frame iterator is less than this frame's, which means the last entity is no longer evaluated if (ExpiredKeys) { ExpiredKeys->Add(LastKey); } ++LastFrameKeys; } else { // Last frame iterator is greater than this frame's, which means this entity is new if (NewKeys) { NewKeys->Add(ThisKey); } ++ThisFrameKeys; } } // Add any remaining expired entities if (ExpiredKeys) { while (LastFrameKeys) { ExpiredKeys->Add(*LastFrameKeys); ++LastFrameKeys; } Algo::SortBy(*ExpiredKeys, &FMovieSceneOrderedEvaluationKey::TearDownIndex); } // Add any remaining new entities if (NewKeys) { while (ThisFrameKeys) { NewKeys->Add(*ThisFrameKeys); ++ThisFrameKeys; } Algo::SortBy(*NewKeys, &FMovieSceneOrderedEvaluationKey::SetupIndex); } }