// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreTypes.h" #include "Evaluation/PreAnimatedState/MovieSceneRestoreStateParams.h" #include "Evaluation/PreAnimatedState/IMovieScenePreAnimatedStorage.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedStateStorage.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedObjectGroupManager.h" #include "Evaluation/PreAnimatedState/MovieScenePreAnimatedCaptureSources.h" #include "EntitySystem/BuiltInComponentTypes.h" namespace UE { namespace MovieScene { /** * Group state class that groups pre-animated storage together by bound object * * Inherit from this class by implementing the following members: * * using KeyType = FObjectKey; * using StorageType = ; * StorageType CachePreAnimatedValue(const FObjectKey& Object); * void RestorePreAnimatedValue(const FObjectKey& Object, StorageType& InOutCachedValue, const FRestoreStateParams& Params); */ struct FBoundObjectPreAnimatedStateTraits : FPreAnimatedStateTraits { enum { NeedsInitialize = true, SupportsGrouping = true, SupportsReplaceObject = true }; MOVIESCENE_API void Initialize(FPreAnimatedStorageID InStorageID, FPreAnimatedStateExtension* InParentExtension); template T ResolveComponent(T&& In) { return Forward(In); } /* Defined as a template rather than a variadic function to prevent error C4840 */ template FPreAnimatedStorageGroupHandle FindGroup(UObject* BoundObject, T&&... Unused) { return FindGroupImpl(BoundObject); } template FPreAnimatedStorageGroupHandle FindGroup(const FObjectComponent& BoundObject, T&&... Unused) { return FindGroupImpl(BoundObject); } /* Defined as a template rather than a variadic function to prevent error C4840 */ template FPreAnimatedStorageGroupHandle MakeGroup(UObject* BoundObject, T&&... Unused) { return MakeGroupImpl(BoundObject); } template FPreAnimatedStorageGroupHandle MakeGroup(const FObjectComponent& BoundObject, T&&... Unused) { return MakeGroupImpl(BoundObject); } MOVIESCENE_API FPreAnimatedStorageGroupHandle FindGroupImpl(UObject* BoundObject); MOVIESCENE_API FPreAnimatedStorageGroupHandle FindGroupImpl(const FObjectComponent& BoundObject); MOVIESCENE_API FPreAnimatedStorageGroupHandle MakeGroupImpl(UObject* BoundObject); MOVIESCENE_API FPreAnimatedStorageGroupHandle MakeGroupImpl(const FObjectComponent& BoundObject); template void ReplaceObject(TTuple& InOutKey, const FObjectKey& NewObject) { InOutKey.template Get<0>() = NewObject; } template void ReplaceObject(KeyType& InOutKey, const FObjectKey& NewObject) { InOutKey.Object = NewObject; } template void ReplaceObject(TObjectKey& InOutKey, const FObjectKey& NewObject) { if (ObjectType* CastResult = Cast(NewObject.ResolveObjectPtr())) { InOutKey = CastResult; } } void ReplaceObject(FObjectKey& InOutKey, const FObjectKey& NewObject) { InOutKey = NewObject; } TSharedPtr ObjectGroupManager; }; template struct TPreAnimatedStateStorage_ObjectTraits : TPreAnimatedStateStorage , IPreAnimatedObjectEntityStorage { using KeyType = typename ObjectTraits::KeyType; using StorageType = typename ObjectTraits::StorageType; static_assert(ObjectTraits::SupportsGrouping, "Pre-animated object state storage should support grouping by object"); TPreAnimatedStateStorage_ObjectTraits() {} public: IPreAnimatedObjectEntityStorage* AsObjectStorage() override { return this; } void BeginTrackingEntities(const FPreAnimatedTrackerParams& Params, TRead EntityIDs, TRead InstanceHandles, TRead BoundObjects) override { const int32 Num = Params.Num; const bool bWantsRestore = Params.bWantsRestoreState; if (!this->ParentExtension->IsCapturingGlobalState() && !bWantsRestore) { return; } FPreAnimatedEntityCaptureSource* EntityMetaData = this->ParentExtension->GetOrCreateEntityMetaData(); for (int32 Index = 0; Index < Num; ++Index) { UObject* BoundObject = BoundObjects[Index]; KeyType Key{ BoundObject }; FPreAnimatedStorageGroupHandle GroupHandle = this->Traits.MakeGroup(BoundObject); FPreAnimatedStorageIndex StorageIndex = this->GetOrCreateStorageIndex(Key); FPreAnimatedStateEntry Entry{ GroupHandle, FPreAnimatedStateCachedValueHandle{ this->StorageID, StorageIndex } }; EntityMetaData->BeginTrackingEntity(Entry, EntityIDs[Index], InstanceHandles[Index], bWantsRestore); } } FPreAnimatedStateEntry MakeEntry(UObject* BoundObject) { KeyType Key{ BoundObject }; FPreAnimatedStorageIndex StorageIndex = this->GetOrCreateStorageIndex(Key); FPreAnimatedStorageGroupHandle GroupHandle = this->Traits.MakeGroup(BoundObject); return FPreAnimatedStateEntry{ GroupHandle, FPreAnimatedStateCachedValueHandle{ this->StorageID, StorageIndex } }; } void BeginTrackingEntity(FMovieSceneEntityID EntityID, bool bWantsRestoreState, FRootInstanceHandle RootInstanceHandle, UObject* BoundObject) override { if (!this->ParentExtension->IsCapturingGlobalState() && !bWantsRestoreState) { return; } FPreAnimatedEntityCaptureSource* EntityMetaData = this->ParentExtension->GetOrCreateEntityMetaData(); FPreAnimatedStateEntry Entry = MakeEntry(BoundObject); EntityMetaData->BeginTrackingEntity(Entry, EntityID, RootInstanceHandle, bWantsRestoreState); } void CachePreAnimatedValues(const FCachePreAnimatedValueParams& Params, TArrayView BoundObjects) override { for (UObject* BoundObject : BoundObjects) { if (BoundObject) { CachePreAnimatedValue(Params, BoundObject); } } } void CachePreAnimatedValue(const FCachePreAnimatedValueParams& Params, UObject* BoundObject, EPreAnimatedCaptureSourceTracking TrackingMode = EPreAnimatedCaptureSourceTracking::CacheIfTracked) { if (this->ShouldTrackCaptureSource(TrackingMode, BoundObject)) { FPreAnimatedStateEntry Entry = MakeEntry(BoundObject); this->TrackCaptureSource(Entry, TrackingMode); CachePreAnimatedValue(Params, Entry, BoundObject); } } void CachePreAnimatedValue(const FCachePreAnimatedValueParams& Params, const FPreAnimatedStateEntry& Entry, UObject* BoundObject) { const FPreAnimatedStorageIndex StorageIndex = Entry.ValueHandle.StorageIndex; EPreAnimatedStorageRequirement StorageRequirement = this->ParentExtension->GetStorageRequirement(Entry); if (!this->IsStorageRequirementSatisfied(StorageIndex, StorageRequirement)) { StorageType NewValue = this->Traits.CachePreAnimatedValue(BoundObject); this->AssignPreAnimatedValue(StorageIndex, StorageRequirement, MoveTemp(NewValue)); } if (Params.bForcePersist) { this->ForciblyPersistStorage(StorageIndex); } } template void CachePreAnimatedValue(const FCachePreAnimatedValueParams& Params, UObject* BoundObject, OnCacheValue&& CacheCallback, EPreAnimatedCaptureSourceTracking TrackingMode = EPreAnimatedCaptureSourceTracking::CacheIfTracked) { if (this->ShouldTrackCaptureSource(TrackingMode, BoundObject)) { FPreAnimatedStateEntry Entry = MakeEntry(BoundObject); this->TrackCaptureSource(Entry, TrackingMode); const FPreAnimatedStorageIndex StorageIndex = Entry.ValueHandle.StorageIndex; EPreAnimatedStorageRequirement StorageRequirement = this->ParentExtension->GetStorageRequirement(Entry); if (!this->IsStorageRequirementSatisfied(StorageIndex, StorageRequirement)) { KeyType Key{ BoundObject }; StorageType NewValue = CacheCallback(Key); this->AssignPreAnimatedValue(StorageIndex, StorageRequirement, MoveTemp(NewValue)); } if (Params.bForcePersist) { this->ForciblyPersistStorage(StorageIndex); } } } }; } // namespace MovieScene } // namespace UE