// Copyright Epic Games, Inc. All Rights Reserved. #include "Conditions/MovieSceneDirectorBlueprintCondition.h" #include "CoreMinimal.h" #include "Engine/World.h" #include "EntitySystem/MovieSceneSharedPlaybackState.h" #include "Evaluation/MovieSceneEvaluationTemplateInstance.h" #include "Evaluation/SequenceDirectorPlaybackCapability.h" #include "MovieSceneSequence.h" #include "UObject/UnrealType.h" #include "MovieScene.h" #if WITH_EDITOR #include "EdGraphSchema_K2.h" #endif #include UE_INLINE_GENERATED_CPP_BY_NAME(MovieSceneDirectorBlueprintCondition) bool FMovieSceneDirectorBlueprintConditionInvoker::EvaluateDirectorBlueprintCondition(FGuid BindingGuid, FMovieSceneSequenceID SequenceID, TSharedRef SharedPlaybackState, const FMovieSceneDirectorBlueprintConditionData& DirectorBlueprintConditionData) { using namespace UE::MovieScene; UFunction* ConditionFunc = DirectorBlueprintConditionData.Function.Get(); if (!ConditionFunc) { // No condition specified, default to succeeding the condition return true; } // 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 evaluate director blueprint condition '%s' because no director instance was available."), *SharedPlaybackState->GetRootSequence()->GetName(), *ConditionFunc->GetName()); #endif // Fallback to default behavior. return true; } UE_LOG(LogMovieScene, VeryVerbose, TEXT("%s: Evaluating director blueprint condition '%s' with function '%s'."), *SharedPlaybackState->GetRootSequence()->GetName(), *LexToString(BindingGuid), *ConditionFunc->GetName()); TArray> BoundObjects; Algo::Transform(SharedPlaybackState->FindBoundObjects(BindingGuid, SequenceID), BoundObjects, [](const TWeakObjectPtr<> BoundObject) { return BoundObject.Get(); }); FMovieSceneConditionContext ConditionContext{ SharedPlaybackState->GetPlaybackContext(), FMovieSceneBindingProxy(BindingGuid, SharedPlaybackState->GetSequence(SequenceID)), BoundObjects }; return InvokeDirectorBlueprintCondition(DirectorInstance, DirectorBlueprintConditionData, ConditionContext); } bool FMovieSceneDirectorBlueprintConditionInvoker::InvokeDirectorBlueprintCondition(UObject* DirectorInstance, const FMovieSceneDirectorBlueprintConditionData& DirectorBlueprintConditionData, const FMovieSceneConditionContext& ConditionContext) { bool Result = false; // Do some basic checks. UFunction* ConditionFunc = DirectorBlueprintConditionData.Function.Get(); if (!ensure(ConditionFunc)) { return Result; } #if WITH_EDITOR // Not sure why this isn't being checked further down, but check manually here if (!ConditionFunc->HasMetaData(FBlueprintMetadata::MD_CallInEditor) || ConditionFunc->GetMetaData(FBlueprintMetadata::MD_CallInEditor) != TEXT("true")) { if (ConditionContext.WorldContext && ConditionContext.WorldContext->GetWorld()->IsEditorWorld()) { return Result; } } #endif // Parse all function parameters. uint8* Parameters = (uint8*)FMemory_Alloca(ConditionFunc->ParmsSize + ConditionFunc->MinAlignment); Parameters = Align(Parameters, ConditionFunc->MinAlignment); // Initialize parameters. FMemory::Memzero(Parameters, ConditionFunc->ParmsSize); FBoolProperty* ReturnProp = nullptr; for (TFieldIterator It(ConditionFunc); 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 blueprint condition resolver function!")); ReturnProp = CastFieldChecked(LocalProp); } } // Set the condition context parameter struct if we need to pass it to the function. if (FProperty* ConditionContextProp = DirectorBlueprintConditionData.ConditionContextProperty.Get()) { ConditionContextProp->SetValue_InContainer(Parameters, &ConditionContext); } #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) #else if (ensureMsgf(ReturnProp != nullptr, TEXT("The director blueprint condition evaluation function has no return value of type bool"))) #endif { // Invoke the function. DirectorInstance->ProcessEvent(ConditionFunc, Parameters); // Grab the result value. ReturnProp->GetValue_InContainer(Parameters, static_cast(&Result)); } // Destroy parameters. for (TFieldIterator It(ConditionFunc); It; ++It) { FProperty* LocalProp = *It; checkSlow(LocalProp); if (LocalProp->HasAllPropertyFlags(CPF_Parm)) { It->DestroyValue_InContainer(Parameters); } } return Result; } bool UMovieSceneDirectorBlueprintCondition::EvaluateConditionInternal(FGuid BindingGuid, FMovieSceneSequenceID SequenceID, TSharedRef SharedPlaybackState) const { return FMovieSceneDirectorBlueprintConditionInvoker::EvaluateDirectorBlueprintCondition(BindingGuid, SequenceID, SharedPlaybackState, DirectorBlueprintConditionData); }