// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "IMovieScenePlayer.h" #include "Evaluation/EventContextsPlaybackCapability.h" #include "Evaluation/EventTriggerControlPlaybackCapability.h" #include "Evaluation/MovieSceneEvaluationTemplateInstance.h" #include "EntitySystem/MovieSceneSequenceInstance.h" #include "EntitySystem/MovieSceneEntitySystemLinker.h" #include "IMovieScenePlaybackClient.h" #include "UniversalObjectLocatorResolveParams.h" #include "Misc/ScopeRWLock.h" #include "MovieSceneFwd.h" #include "MovieSceneSequence.h" #include "MovieSceneSequenceID.h" #include "UniversalObjectLocatorResolveParameterBuffer.inl" #include "MovieSceneBindingReferences.h" #include "Misc/ScopeRWLock.h" #include "Misc/TransactionallySafeRWLock.h" namespace UE { namespace MovieScene { static FTransactionallySafeRWLock GGlobalPlayerRegistryLock; static TSparseArray GGlobalPlayerRegistry; static TBitArray<> GGlobalPlayerUpdateFlags; UE_DEFINE_MOVIESCENE_PLAYBACK_CAPABILITY(FPlayerIndexPlaybackCapability) IMovieScenePlayer* FPlayerIndexPlaybackCapability::GetPlayer(TSharedRef Owner) { if (FPlayerIndexPlaybackCapability* Cap = Owner->FindCapability()) { return IMovieScenePlayer::Get(Cap->PlayerIndex); } return nullptr; } uint16 FPlayerIndexPlaybackCapability::GetPlayerIndex(TSharedRef Owner) { if (FPlayerIndexPlaybackCapability* Cap = Owner->FindCapability()) { return Cap->PlayerIndex; } return (uint16)-1; } } // namespace MovieScene } // namespace UE UE_DEFINE_MOVIESCENE_PLAYBACK_CAPABILITY(IMovieScenePlaybackClient) PRAGMA_DISABLE_DEPRECATION_WARNINGS IMovieScenePlayer::IMovieScenePlayer() : BindingOverrides(StaticBindingOverrides.BindingOverrides) { UE::TWriteScopeLock ScopeLock(UE::MovieScene::GGlobalPlayerRegistryLock); UE::MovieScene::GGlobalPlayerRegistry.Shrink(); UniqueIndex = UE::MovieScene::GGlobalPlayerRegistry.Add(this); UE::MovieScene::GGlobalPlayerUpdateFlags.PadToNum(UniqueIndex + 1, false); UE::MovieScene::GGlobalPlayerUpdateFlags[UniqueIndex] = 0; } PRAGMA_ENABLE_DEPRECATION_WARNINGS PRAGMA_DISABLE_DEPRECATION_WARNINGS IMovieScenePlayer::~IMovieScenePlayer() { UE::TWriteScopeLock ScopeLock(UE::MovieScene::GGlobalPlayerRegistryLock); UE::MovieScene::GGlobalPlayerUpdateFlags[UniqueIndex] = 0; UE::MovieScene::GGlobalPlayerRegistry.RemoveAt(UniqueIndex, 1); } PRAGMA_ENABLE_DEPRECATION_WARNINGS IMovieScenePlayer* IMovieScenePlayer::Get(uint16 InUniqueIndex) { UE::TReadScopeLock ScopeLock(UE::MovieScene::GGlobalPlayerRegistryLock); check(UE::MovieScene::GGlobalPlayerRegistry.IsValidIndex(InUniqueIndex)); return UE::MovieScene::GGlobalPlayerRegistry[InUniqueIndex]; } void IMovieScenePlayer::Get(TArray& OutPlayers, bool bOnlyUnstoppedPlayers) { UE::TReadScopeLock ScopeLock(UE::MovieScene::GGlobalPlayerRegistryLock); for (auto It = UE::MovieScene::GGlobalPlayerRegistry.CreateIterator(); It; ++It) { if (IMovieScenePlayer* Player = *It) { if (!bOnlyUnstoppedPlayers || Player->GetPlaybackStatus() != EMovieScenePlayerStatus::Stopped) { OutPlayers.Add(*It); } } } } void IMovieScenePlayer::SetIsEvaluatingFlag(uint16 InUniqueIndex, bool bIsUpdating) { check(UE::MovieScene::GGlobalPlayerUpdateFlags.IsValidIndex(InUniqueIndex)); UE::MovieScene::GGlobalPlayerUpdateFlags[InUniqueIndex] = bIsUpdating; } bool IMovieScenePlayer::IsEvaluating() const { return UE::MovieScene::GGlobalPlayerUpdateFlags[UniqueIndex]; } void IMovieScenePlayer::PopulateUpdateFlags(UE::MovieScene::ESequenceInstanceUpdateFlags& OutFlags) { using namespace UE::MovieScene; OutFlags |= ESequenceInstanceUpdateFlags::NeedsPreEvaluation | ESequenceInstanceUpdateFlags::NeedsPostEvaluation; } void IMovieScenePlayer::ResolveBoundObjects(const FGuid& InBindingId, FMovieSceneSequenceID SequenceID, UMovieSceneSequence& Sequence, UObject* ResolutionContext, TArray>& OutObjects) const { // This deprecated version of ResolveBoundObjects no longer gets called directly by the FMovieSceneObjectCache- that uses the ResolveParams overload below. // In order to ensure things continue to work properly for anyone that may have been calling this directly rather than FindBoundObjects, we direct // this towards FindBoundObjects below. TArrayView> BoundObjects = const_cast(this)->FindBoundObjects(InBindingId, SequenceID); for (TWeakObjectPtr<> BoundObject : BoundObjects) { if (UObject* Obj = BoundObject.Get()) { OutObjects.Add(Obj); } } } void IMovieScenePlayer::ResolveBoundObjects(UE::UniversalObjectLocator::FResolveParams& LocatorResolveParams, const FGuid& InBindingId, FMovieSceneSequenceID SequenceID, UMovieSceneSequence& InSequence, TArray>& OutObjects) const { using namespace UE::UniversalObjectLocator; using namespace UE::MovieScene; const IMovieScenePlaybackClient* PlaybackClient = GetPlaybackClient(); bool bAllowDefault = PlaybackClient ? PlaybackClient->RetrieveBindingOverrides(InBindingId, SequenceID, OutObjects) : true; if (bAllowDefault) { if (const FMovieSceneBindingReferences* BindingReferences = InSequence.GetBindingReferences()) { FMovieSceneBindingResolveParams BindingResolveParams{ &InSequence, InBindingId, SequenceID, LocatorResolveParams.Context }; BindingReferences->ResolveBinding(BindingResolveParams, LocatorResolveParams, FindSharedPlaybackState(), OutObjects); } else { InSequence.LocateBoundObjects(InBindingId, LocatorResolveParams, FindSharedPlaybackState(), OutObjects); } } } TArrayView> IMovieScenePlayer::FindBoundObjects(const FGuid& ObjectBindingID, FMovieSceneSequenceIDRef SequenceID) { using namespace UE::MovieScene; if (TSharedPtr SharedPlaybackState = FindSharedPlaybackState()) { FMovieSceneEvaluationState* ActualState = GetEvaluationState(); return ActualState->FindBoundObjects(ObjectBindingID, SequenceID, SharedPlaybackState.ToSharedRef()); } return TArrayView>(); } void IMovieScenePlayer::InvalidateCachedData() { FMovieSceneRootEvaluationTemplateInstance& Template = GetEvaluationTemplate(); UE::MovieScene::FSequenceInstance* RootInstance = Template.FindInstance(MovieSceneSequenceID::Root); if (RootInstance) { RootInstance->InvalidateCachedData(); } } TSharedPtr IMovieScenePlayer::FindSharedPlaybackState() { return GetEvaluationTemplate().GetSharedPlaybackState(); } TSharedPtr IMovieScenePlayer::FindSharedPlaybackState() const { return ConstCastSharedPtr(const_cast(this)->GetEvaluationTemplate().GetSharedPlaybackState()); } TSharedRef IMovieScenePlayer::GetSharedPlaybackState() { // ToSharedRef will assert if evaluation template isn't initialized return GetEvaluationTemplate().GetSharedPlaybackState().ToSharedRef(); } TSharedRef IMovieScenePlayer::GetSharedPlaybackState() const { // ToSharedRef will assert if evaluation template isn't initialized return ConstCastSharedRef(const_cast(this)->GetEvaluationTemplate().GetSharedPlaybackState().ToSharedRef()); } FMovieSceneEvaluationOperand* IMovieScenePlayer::GetBindingOverride(const FMovieSceneEvaluationOperand& InOperand) { if (UE::MovieScene::IStaticBindingOverridesPlaybackCapability* ActualOverrides = GetStaticBindingOverrides()) { return ActualOverrides->GetBindingOverride(InOperand); } return nullptr; } void IMovieScenePlayer::AddBindingOverride(const FMovieSceneEvaluationOperand& InOperand, const FMovieSceneEvaluationOperand& InOverrideOperand) { if (UE::MovieScene::IStaticBindingOverridesPlaybackCapability* ActualOverrides = GetStaticBindingOverrides()) { ActualOverrides->AddBindingOverride(InOperand, InOverrideOperand); } } void IMovieScenePlayer::RemoveBindingOverride(const FMovieSceneEvaluationOperand& InOperand) { if (UE::MovieScene::IStaticBindingOverridesPlaybackCapability* ActualOverrides = GetStaticBindingOverrides()) { ActualOverrides->RemoveBindingOverride(InOperand); } } void IMovieScenePlayer::ResetDirectorInstances() { using namespace UE::MovieScene; TSharedPtr SharedPlaybackState = FindSharedPlaybackState(); if (!SharedPlaybackState) { return; } FSequenceDirectorPlaybackCapability* Cap = SharedPlaybackState->FindCapability(); if (Cap) { Cap->ResetDirectorInstances(); } } UObject* IMovieScenePlayer::GetOrCreateDirectorInstance(TSharedRef SharedPlaybackState, FMovieSceneSequenceIDRef SequenceID) { using namespace UE::MovieScene; FSequenceDirectorPlaybackCapability* Cap = SharedPlaybackState->FindCapability(); if (Cap) { return Cap->GetOrCreateDirectorInstance(SharedPlaybackState, SequenceID); } return nullptr; } TArray IMovieScenePlayer::GetEventContexts() const { using namespace UE::MovieScene; // By default, look for the playback capability, for backwards compatibility. IMovieScenePlayer* This = const_cast(this); if (TSharedPtr SharedPlaybackState = This->FindSharedPlaybackState()) { if (IEventContextsPlaybackCapability* EventContextsCapability = SharedPlaybackState->FindCapability()) { return EventContextsCapability->GetEventContexts(); } } return TArray(); } bool IMovieScenePlayer::IsDisablingEventTriggers(FFrameTime& DisabledUntilTime) const { using namespace UE::MovieScene; // By default, look for the playback capability, for backwards compatibility. IMovieScenePlayer* This = const_cast(this); if (TSharedPtr SharedPlaybackState = This->FindSharedPlaybackState()) { if (FEventTriggerControlPlaybackCapability* TriggerControlCapability = SharedPlaybackState->FindCapability()) { return TriggerControlCapability->IsDisablingEventTriggers(DisabledUntilTime); } } return false; } FGuid IMovieScenePlayer::CreateBinding(UMovieSceneSequence* InSequence, UObject* InObject) { if (InSequence && InObject) { return InSequence->CreatePossessable(InObject); } return FGuid(); } FMovieSceneEvaluationState* IMovieScenePlayer::GetEvaluationState() { using namespace UE::MovieScene; // Return the playback capability, which is generally the same as the State member variable if // InitializeRootInstance has been called, but that member variable will be removed in the future. // In addition to this, if our underlying type is FMovieSceneLegacyPlayer, the playback capability // is NOT the same as our State member variable. if (TSharedPtr SharedPlaybackState = FindSharedPlaybackState()) { return SharedPlaybackState->FindCapability(); } return nullptr; } UE::MovieScene::IStaticBindingOverridesPlaybackCapability* IMovieScenePlayer::GetStaticBindingOverrides() { using namespace UE::MovieScene; // Return the playback capability, which is generally the same as "this" if InitializeRootInstance // has been called, but we want to deprecate the BindingOverrides member field in the future. // In addition to this, if our underlying type is FMovieSceneLegacyPlayer, the playback capability // is NOT the same as "this". if (TSharedPtr SharedPlaybackState = FindSharedPlaybackState()) { return SharedPlaybackState->FindCapability(); } return nullptr; } void IMovieScenePlayer::InitializeRootInstance(TSharedRef NewSharedPlaybackState) { using namespace UE::MovieScene; NewSharedPlaybackState->AddCapability(UniqueIndex); PRAGMA_DISABLE_DEPRECATION_WARNINGS NewSharedPlaybackState->AddCapabilityRaw(&State); PRAGMA_ENABLE_DEPRECATION_WARNINGS // Only add the spawnregister if it is different from the default 'null' register (which does nothing) FMovieSceneSpawnRegister* SpawnRegister = &GetSpawnRegister(); if (SpawnRegister != &IMovieScenePlayer::GetSpawnRegister()) { NewSharedPlaybackState->AddCapabilityRaw(SpawnRegister); } NewSharedPlaybackState->AddCapabilityRaw((IObjectBindingNotifyPlaybackCapability*)this); NewSharedPlaybackState->AddCapabilityRaw(&StaticBindingOverrides); if (IMovieScenePlaybackClient* PlaybackClient = GetPlaybackClient()) { NewSharedPlaybackState->AddCapabilityRaw(PlaybackClient); } UMovieSceneEntitySystemLinker* Linker = NewSharedPlaybackState->GetLinker(); if (ensure(Linker)) { FInstanceRegistry* InstanceRegistry = Linker->GetInstanceRegistry(); if (ensure(InstanceRegistry)) { FSequenceInstance& RootInstance = InstanceRegistry->MutateInstance(NewSharedPlaybackState->GetRootInstanceHandle()); RootInstance.Initialize(); } } }