// Copyright Epic Games, Inc. All Rights Reserved. #include "MovieSceneDynamicBindingInvoker.h" #include "CoreMinimal.h" #include "Engine/World.h" #include "EntitySystem/MovieSceneSharedPlaybackState.h" #include "Evaluation/MovieSceneEvaluationTemplateInstance.h" #include "Evaluation/SequenceDirectorPlaybackCapability.h" #include "MovieSceneDynamicBinding.h" #include "MovieScenePossessable.h" #include "MovieSceneSequence.h" #include "MovieSceneSpawnable.h" #include "UObject/UnrealType.h" #include "MovieScene.h" FMovieSceneDynamicBindingResolveResult FMovieSceneDynamicBindingInvoker::ResolveDynamicBinding(TSharedRef SharedPlaybackState, UMovieSceneSequence* Sequence, const FMovieSceneSequenceID& SequenceID, const FGuid& InGuid, const FMovieSceneDynamicBinding& DynamicBinding) { using namespace UE::MovieScene; if (!ensure(Sequence)) { // Sequence is somehow null... fallback to default behavior. return FMovieSceneDynamicBindingResolveResult(); } UFunction* DynamicBindingFunc = DynamicBinding.Function.Get(); if (!DynamicBindingFunc) { // No dynamic binding, fallback to default behavior. return FMovieSceneDynamicBindingResolveResult(); } // Auto-add the director playback capability, which is just really a cache for director instances after // they've been created by the sequences in the hierarchy. FSequenceDirectorPlaybackCapability* DirectorCapability = SharedPlaybackState->FindCapability(); if (!DirectorCapability) { TSharedRef MutableState = ConstCastSharedRef(SharedPlaybackState); DirectorCapability = &MutableState->AddCapability(); } UObject* DirectorInstance = DirectorCapability->GetOrCreateDirectorInstance(SharedPlaybackState, SequenceID); if (!DirectorInstance) { #if !NO_LOGGING UE_LOG(LogMovieScene, Warning, TEXT("%s: Failed to resolve dynamic binding '%s' because no director instance was available."), *Sequence->GetName(), *DynamicBindingFunc->GetName()); #endif // Fallback to default behavior. return FMovieSceneDynamicBindingResolveResult(); } if (!DynamicBindingFunc->IsIn(DirectorInstance->GetClass())) { UE_LOG(LogMovieScene, Warning, TEXT("%s: Failed to resolve dynamic binding '%s' because the function is not contained within the director instance."), *Sequence->GetName(), *DynamicBindingFunc->GetName()); return FMovieSceneDynamicBindingResolveResult(); } #if WITH_EDITOR const static FName NAME_CallInEditor(TEXT("CallInEditor")); UWorld* World = DirectorInstance->GetWorld(); const bool bIsGameWorld = World && World->IsGameWorld(); if (!bIsGameWorld && !DynamicBindingFunc->HasMetaData(NAME_CallInEditor)) { UE_LOG(LogMovieScene, Verbose, TEXT("%s: Refusing to resolve dynamic binding '%s' in editor world because function '%s' has 'Call in Editor' set to false."), *Sequence->GetName(), *LexToString(InGuid), *DynamicBindingFunc->GetName()); // Fallback to default behavior. return FMovieSceneDynamicBindingResolveResult(); } #endif // WITH_EDITOR UE_LOG(LogMovieScene, VeryVerbose, TEXT("%s: Resolving dynamic binding '%s' with function '%s'."), *Sequence->GetName(), *LexToString(InGuid), *DynamicBindingFunc->GetName()); FMovieSceneDynamicBindingResolveParams ResolveParams; ResolveParams.ObjectBindingID = InGuid; ResolveParams.Sequence = Sequence; ResolveParams.RootSequence = SharedPlaybackState->GetRootSequence(); return InvokeDynamicBinding(DirectorInstance, DynamicBinding, ResolveParams); } FMovieSceneDynamicBindingResolveResult FMovieSceneDynamicBindingInvoker::InvokeDynamicBinding(UObject* DirectorInstance, const FMovieSceneDynamicBinding& DynamicBinding, const FMovieSceneDynamicBindingResolveParams& ResolveParams) { FMovieSceneDynamicBindingResolveResult Result; // Do some basic checks. UFunction* DynamicBindingFunc = DynamicBinding.Function.Get(); if (!ensure(DynamicBindingFunc)) { return Result; } // Parse all function parameters. uint8* Parameters = (uint8*)FMemory_Alloca(DynamicBindingFunc->ParmsSize + DynamicBindingFunc->MinAlignment); Parameters = Align(Parameters, DynamicBindingFunc->MinAlignment); // Initialize parameters. FMemory::Memzero(Parameters, DynamicBindingFunc->ParmsSize); FStructProperty* ReturnProp = nullptr; for (TFieldIterator It(DynamicBindingFunc); It; ++It) { FProperty* LocalProp = *It; checkSlow(LocalProp); if (!LocalProp->HasAnyPropertyFlags(CPF_ZeroConstructor) && LocalProp->HasAllPropertyFlags(CPF_Parm)) { LocalProp->InitializeValue_InContainer(Parameters); } if (LocalProp->HasAnyPropertyFlags(CPF_ReturnParm)) { ensureMsgf(ReturnProp == nullptr, TEXT("Found more than one return parameter in dynamic binding resolver function!")); ReturnProp = CastFieldChecked(LocalProp); } } // Set the resolve parameter struct if we need to pass it to the function. if (FProperty* ResolveParamsProp = DynamicBinding.ResolveParamsProperty.Get()) { ResolveParamsProp->SetValue_InContainer(Parameters, &ResolveParams); } #if WITH_EDITOR // In the editor we need to be more forgiving, because we might have temporarily invalid states, such as // when undo-ing operations. if (ReturnProp != nullptr && ReturnProp->Struct == FMovieSceneDynamicBindingResolveResult::StaticStruct()) #else if (ensureMsgf(ReturnProp != nullptr && ReturnProp->Struct == FMovieSceneDynamicBindingResolveResult::StaticStruct(), TEXT("The dynamic binding resolver function has no return value of type FMovieSceneDynamicBindingResolveResult"))) #endif { // Invoke the function. DirectorInstance->ProcessEvent(DynamicBindingFunc, Parameters); // Grab the result value. ReturnProp->GetValue_InContainer(Parameters, static_cast(&Result)); } // Destroy parameters. for (TFieldIterator It(DynamicBindingFunc); It; ++It) { FProperty* LocalProp = *It; checkSlow(LocalProp); if (LocalProp->HasAllPropertyFlags(CPF_Parm)) { It->DestroyValue_InContainer(Parameters); } } return Result; }