Files
2025-05-18 13:04:45 +08:00

439 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimNextVariablesTest.h"
#include "CoreMinimal.h"
#include "AnimNextTest.h"
#include "ScopedTransaction.h"
#include "UncookedOnlyUtils.h"
#include "Misc/AutomationTest.h"
#include "Param/ParamType.h"
#include "Animation/AnimSequence.h"
#include "Animation/AnimMontage.h"
#include "Async/ParallelFor.h"
#include "Component/AnimNextComponent.h"
#include "Entries/AnimNextEventGraphEntry.h"
#include "Module/AnimNextModule.h"
#include "Module/AnimNextModule_EditorData.h"
#include "Entries/AnimNextVariableEntry.h"
#include "Factories/Factory.h"
#include "Module/AnimNextModuleFactory.h"
#include "Module/RigUnit_AnimNextModuleEvents.h"
#include "Variables/AnimNextUniversalObjectLocatorBindingData.h"
#include "Module/AnimNextModuleInstance.h"
// AnimNext Parameters Tests
#if WITH_DEV_AUTOMATION_TESTS
namespace UE::AnimNext::Tests
{
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FVariableTypesTest, "Animation.AnimNext.VariableTypes", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FVariableTypesTest::RunTest(const FString& InParameters)
{
// None is invalid
FAnimNextParamType ParameterTypeValueNone(FAnimNextParamType::EValueType::None);
AddErrorIfFalse(!ParameterTypeValueNone.IsValid(), TEXT("Parameter type None is valid."));
// None is invalid for all containers
for(uint8 ContainerType = (uint8)FAnimNextParamType::EContainerType::None; ContainerType <= (uint8)FAnimNextParamType::EContainerType::Array; ++ContainerType)
{
FAnimNextParamType ParameterTypeValueContainerNone(FAnimNextParamType::EValueType::None, (FAnimNextParamType::EContainerType)ContainerType);
AddErrorIfFalse(!ParameterTypeValueContainerNone.IsValid(), FString::Printf(TEXT("Parameter type None, container type %d is valid."), ContainerType));
}
// Null object types
for(uint8 ObjectValueType = (uint8)FAnimNextParamType::EValueType::Enum; ObjectValueType <= (uint8)FAnimNextParamType::EValueType::SoftClass; ++ObjectValueType)
{
for(uint8 ContainerType = (uint8)FAnimNextParamType::EContainerType::None; ContainerType <= (uint8)FAnimNextParamType::EContainerType::Array; ++ContainerType)
{
FAnimNextParamType ParameterTypeNullObject((FAnimNextParamType::EValueType)ObjectValueType, (FAnimNextParamType::EContainerType)ContainerType, nullptr);
AddErrorIfFalse(!ParameterTypeNullObject.IsValid(), FString::Printf(TEXT("Parameter type %d, container type %d with null object is valid."), ObjectValueType, ContainerType));
}
}
// Non object types
for(uint8 ValueType = (uint8)FAnimNextParamType::EValueType::Bool; ValueType < (uint8)FAnimNextParamType::EValueType::Enum; ++ValueType)
{
for(uint8 ContainerType = (uint8)FAnimNextParamType::EContainerType::None; ContainerType <= (uint8)FAnimNextParamType::EContainerType::Array; ++ContainerType)
{
FAnimNextParamType TestValueContainerParameterType((FAnimNextParamType::EValueType)ValueType, (FAnimNextParamType::EContainerType)ContainerType);
AddErrorIfFalse(TestValueContainerParameterType.IsValid(), FString::Printf(TEXT("Parameter type %d, container type %d is invalid."), ValueType, ContainerType));
}
}
UObject* ExampleValidObjects[(uint8)FAnimNextParamType::EValueType::SoftClass + 1] =
{
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
FindObjectChecked<UEnum>(nullptr, TEXT("/Script/StructUtils.EPropertyBagPropertyType")),
FAnimNextParamType::StaticStruct(),
UObject::StaticClass(),
UObject::StaticClass(),
UObject::StaticClass(),
UObject::StaticClass()
};
// Non-null valid object types
for(uint8 ObjectValueType = (uint8)FAnimNextParamType::EValueType::Enum; ObjectValueType <= (uint8)FAnimNextParamType::EValueType::SoftClass; ++ObjectValueType)
{
for(uint8 ContainerType = (uint8)FAnimNextParamType::EContainerType::None; ContainerType <= (uint8)FAnimNextParamType::EContainerType::Array; ++ContainerType)
{
FAnimNextParamType TestValueContainerParameterType((FAnimNextParamType::EValueType)ObjectValueType, (FAnimNextParamType::EContainerType)ContainerType, ExampleValidObjects[ObjectValueType]);
AddErrorIfFalse(TestValueContainerParameterType.IsValid(), FString::Printf(TEXT("Object parameter type %d, container type %d is invalid."), ObjectValueType, ContainerType));
}
}
UObject* ExampleInvalidObjects[(uint8)FAnimNextParamType::EValueType::SoftClass + 1] =
{
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
FAnimNextParamType::StaticStruct(),
FindObjectChecked<UEnum>(nullptr, TEXT("/Script/StructUtils.EPropertyBagPropertyType")),
FAnimNextParamType::StaticStruct(),
FAnimNextParamType::StaticStruct(),
FAnimNextParamType::StaticStruct(),
FAnimNextParamType::StaticStruct()
};
// Non-null invalid object types
for(uint8 ObjectValueType = (uint8)FAnimNextParamType::EValueType::Enum; ObjectValueType <= (uint8)FAnimNextParamType::EValueType::SoftClass; ++ObjectValueType)
{
for(uint8 ContainerType = (uint8)FAnimNextParamType::EContainerType::None; ContainerType <= (uint8)FAnimNextParamType::EContainerType::Array; ++ContainerType)
{
FAnimNextParamType TestValueContainerParameterType((FAnimNextParamType::EValueType)ObjectValueType, (FAnimNextParamType::EContainerType)ContainerType, ExampleInvalidObjects[ObjectValueType]);
AddErrorIfFalse(!TestValueContainerParameterType.IsValid(), FString::Printf(TEXT("Object parameter type %d, container type %d is valid."), ObjectValueType, ContainerType));
}
}
// Check type inference
AddErrorIfFalse(FAnimNextParamType::GetType<bool>().IsValid(), TEXT("bool parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<uint8>().IsValid(), TEXT("uint8 parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<int32>().IsValid(), TEXT("int32 parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<int64>().IsValid(), TEXT("int64 parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<float>().IsValid(), TEXT("float parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<double>().IsValid(), TEXT("double parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<FName>().IsValid(), TEXT("FName parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<FString>().IsValid(), TEXT("FString parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<FText>().IsValid(), TEXT("FText parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<EPropertyBagContainerType>().IsValid(), TEXT("Enum parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<FAnimNextParamType>().IsValid(), TEXT("Struct parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<FVector>().IsValid(), TEXT("Struct parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<FTransform>().IsValid(), TEXT("Struct parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<FQuat>().IsValid(), TEXT("Struct parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<UObject*>().IsValid(), TEXT("UObject parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TObjectPtr<UObject>>().IsValid(), TEXT("TObjectPtr<UObject> parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<UClass*>().IsValid(), TEXT("UClass parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TSubclassOf<UObject>>().IsValid(), TEXT("TSubclassOf<UObject> parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TSoftObjectPtr<UObject>>().IsValid(), TEXT("TSoftObjectPtr<UObject> parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TSoftClassPtr<UObject>>().IsValid(), TEXT("TSoftClassPtr<UObject> parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<bool>>().IsValid(), TEXT("bool array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<uint8>>().IsValid(), TEXT("uint8 array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<int32>>().IsValid(), TEXT("int32 array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<int64>>().IsValid(), TEXT("int64 array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<float>>().IsValid(), TEXT("float array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<double>>().IsValid(), TEXT("double array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<FName>>().IsValid(), TEXT("FName array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<FString>>().IsValid(), TEXT("FString array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<FText>>().IsValid(), TEXT("FText array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<EPropertyBagContainerType>>().IsValid(), TEXT("Enum array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<FAnimNextParamType>>().IsValid(), TEXT("Struct array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<FVector>>().IsValid(), TEXT("Struct array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<FTransform>>().IsValid(), TEXT("Struct array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<FQuat>>().IsValid(), TEXT("Struct array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<UObject*>>().IsValid(), TEXT("UObject array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<TObjectPtr<UObject>>>().IsValid(), TEXT("TObjectPtr<UObject> array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<UClass*>>().IsValid(), TEXT("UClass array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<TSubclassOf<UObject>>>().IsValid(), TEXT("TSubclassOf<UObject> array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<TSoftObjectPtr<UObject>>>().IsValid(), TEXT("TSoftObjectPtr<UObject> array parameter is invalid."));
AddErrorIfFalse(FAnimNextParamType::GetType<TArray<TSoftClassPtr<UObject>>>().IsValid(), TEXT("TSoftClassPtr<UObject> array parameter is invalid."));
// Check type from property
#define TEST_ANIMNEXT_PROPERTY(Type, Property) AddErrorIfFalse(FAnimNextParamType::FromProperty(FAnimNextParamTypeTestStruct::StaticStruct()->FindPropertyByName(#Property)) == FAnimNextParamType::GetType<Type>(), #Type " param type is invalid")
#define TEST_ANIMNEXT_PROPERTY_ARRAY(Type, Property) AddErrorIfFalse(FAnimNextParamType::FromProperty(FAnimNextParamTypeTestStruct::StaticStruct()->FindPropertyByName(#Property"Array")) == FAnimNextParamType::GetType<TArray<Type>>(), #Type " array param type is invalid")
TEST_ANIMNEXT_PROPERTY(bool, bBool);
TEST_ANIMNEXT_PROPERTY(uint8, Uint8);
TEST_ANIMNEXT_PROPERTY(int32, Int32);
TEST_ANIMNEXT_PROPERTY(int64, Int64);
TEST_ANIMNEXT_PROPERTY(float, Float);
TEST_ANIMNEXT_PROPERTY(double, Double);
TEST_ANIMNEXT_PROPERTY(FName, Name);
TEST_ANIMNEXT_PROPERTY(FString, String);
TEST_ANIMNEXT_PROPERTY(FText, Text);
TEST_ANIMNEXT_PROPERTY(EPropertyBagContainerType, Enum);
TEST_ANIMNEXT_PROPERTY(FAnimNextParamType, Struct);
TEST_ANIMNEXT_PROPERTY(FVector, Vector);
TEST_ANIMNEXT_PROPERTY(FTransform, Transform);
TEST_ANIMNEXT_PROPERTY(TObjectPtr<UObject>, Object);
TEST_ANIMNEXT_PROPERTY(TObjectPtr<UClass>, Class);
TEST_ANIMNEXT_PROPERTY(TSubclassOf<UObject>, SubclassOf);
TEST_ANIMNEXT_PROPERTY(TSoftObjectPtr<UObject>, SoftObjectPtr);
TEST_ANIMNEXT_PROPERTY(TSoftClassPtr<UObject>, SoftClassPtr);
TEST_ANIMNEXT_PROPERTY_ARRAY(bool, Bool);
TEST_ANIMNEXT_PROPERTY_ARRAY(uint8, Uint8);
TEST_ANIMNEXT_PROPERTY_ARRAY(int32, Int32);
TEST_ANIMNEXT_PROPERTY_ARRAY(int64, Int64);
TEST_ANIMNEXT_PROPERTY_ARRAY(float, Float);
TEST_ANIMNEXT_PROPERTY_ARRAY(double, Double);
TEST_ANIMNEXT_PROPERTY_ARRAY(FName, Name);
TEST_ANIMNEXT_PROPERTY_ARRAY(FString, String);
TEST_ANIMNEXT_PROPERTY_ARRAY(FText, Text);
TEST_ANIMNEXT_PROPERTY_ARRAY(EPropertyBagContainerType, Enum);
TEST_ANIMNEXT_PROPERTY_ARRAY(FAnimNextParamType, Struct);
TEST_ANIMNEXT_PROPERTY_ARRAY(FVector, Vector);
TEST_ANIMNEXT_PROPERTY_ARRAY(FTransform, Transform);
TEST_ANIMNEXT_PROPERTY_ARRAY(TObjectPtr<UObject>, Object);
TEST_ANIMNEXT_PROPERTY_ARRAY(TObjectPtr<UClass>, Class);
TEST_ANIMNEXT_PROPERTY_ARRAY(TSubclassOf<UObject>, SubclassOf);
TEST_ANIMNEXT_PROPERTY_ARRAY(TSoftObjectPtr<UObject>, SoftObjectPtr);
TEST_ANIMNEXT_PROPERTY_ARRAY(TSoftClassPtr<UObject>, SoftClassPtr);
#undef TEST_ANIMNEXT_PROPERTY
#undef TEST_ANIMNEXT_PROPERTY_ARRAY
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FVariables, "Animation.AnimNext.Variables", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FVariables::RunTest(const FString& InParameters)
{
ON_SCOPE_EXIT{ FUtils::CleanupAfterTests(); };
UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), UAnimNextModuleFactory::StaticClass());
UAnimNextRigVMAsset* Asset = CastChecked<UAnimNextRigVMAsset>(Factory->FactoryCreateNew(UAnimNextModule::StaticClass(), GetTransientPackage(), TEXT("TestAsset"), RF_Transient, nullptr, nullptr, NAME_None));
UE_RETURN_ON_ERROR(Asset != nullptr, "FVariables -> Failed to create asset");
UAnimNextRigVMAssetEditorData* EditorData = UncookedOnly::FUtils::GetEditorData<UAnimNextRigVMAssetEditorData>(Asset);
UE_RETURN_ON_ERROR(EditorData != nullptr, "FVariables -> Asset has no editor data.");
// Add variables
UAnimNextVariableEntry* OperandAEntry = EditorData->AddVariable(TEXT("A"), FAnimNextParamType::GetType<int32>(), TEXT("1"));
UE_RETURN_ON_ERROR(OperandAEntry != nullptr, TEXT("Could not create new variable in graph."));
UAnimNextVariableEntry* OperandBEntry = EditorData->AddVariable(TEXT("B"), FAnimNextParamType::GetType<int32>(), TEXT("2"));
UE_RETURN_ON_ERROR(OperandBEntry != nullptr, TEXT("Could not create new variable in graph."));
UAnimNextVariableEntry* ResultEntry = EditorData->AddVariable(TEXT("Result"), FAnimNextParamType::GetType<int32>(), TEXT("12"));
UE_RETURN_ON_ERROR(ResultEntry != nullptr, TEXT("Could not create new variable in graph."));
// Get event graph
UAnimNextEventGraphEntry* EventGraph = Cast<UAnimNextEventGraphEntry>(EditorData->FindEntry(TEXT("PrePhysics")));
if(EventGraph == nullptr)
{
EventGraph = EditorData->AddEventGraph(TEXT("PrePhysics"), FRigUnit_AnimNextPrePhysicsEvent::StaticStruct());
}
UE_RETURN_ON_ERROR(EventGraph != nullptr, TEXT("Could not create new event graph in asset."));
URigVMGraph* RigVMGraph = EventGraph->GetRigVMGraph();
UE_RETURN_ON_ERROR(RigVMGraph->GetNodes().Num() == 1, TEXT("Unexpected number of nodes in new event graph."));
URigVMNode* EventNode = RigVMGraph->GetNodes()[0];
check(EventNode);
URigVMPin* ExecutePin = EventNode->FindPin("ExecuteContext");
UE_RETURN_ON_ERROR(ExecutePin != nullptr, TEXT("Could find initial execute pin."));
URigVMController* Controller = EditorData->GetController(EventGraph->GetRigVMGraph());
URigVMVariableNode* VariableANode = Controller->AddVariableNode("A", RigVMTypeUtils::Int32Type, nullptr, true, TEXT(""));
UE_RETURN_ON_ERROR(VariableANode != nullptr, TEXT("Could not add get variable node."));
URigVMVariableNode* VariableBNode = Controller->AddVariableNode("B", RigVMTypeUtils::Int32Type, nullptr, true, TEXT(""));
UE_RETURN_ON_ERROR(VariableBNode != nullptr, TEXT("Could not add get variable node."));
URigVMVariableNode* SetResultNode = Controller->AddVariableNode("Result", RigVMTypeUtils::Int32Type, nullptr, false, TEXT(""));
UE_RETURN_ON_ERROR(SetResultNode != nullptr, TEXT("Could not add set variable node."));
URigVMUnitNode* TestOpUnitNode = Controller->AddUnitNode(FAnimNextTests_TestOperation::StaticStruct());
bool bLinkAAdded = Controller->AddLink(VariableANode->FindPin("Value"), TestOpUnitNode->FindPin("A"));
UE_RETURN_ON_ERROR(bLinkAAdded, TEXT("Could not link variable node."));
bool bLinkBAdded = Controller->AddLink(VariableBNode->FindPin("Value"), TestOpUnitNode->FindPin("B"));
UE_RETURN_ON_ERROR(bLinkBAdded, TEXT("Could not link variable node."));
bool bLinkResultAdded = Controller->AddLink(TestOpUnitNode->FindPin("Result"), SetResultNode->FindPin("Value"));
UE_RETURN_ON_ERROR(bLinkResultAdded, TEXT("Could not link variable node."));
bool bLinkExec1Added = Controller->AddLink(EventNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()), TestOpUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()));
UE_RETURN_ON_ERROR(bLinkExec1Added, TEXT("Could not link variable node exec."));
bool bLinkExec2Added = Controller->AddLink(TestOpUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()), SetResultNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()));
UE_RETURN_ON_ERROR(bLinkExec2Added, TEXT("Could not link variable node exec."));
URigVMUnitNode* PrintResultUnitNode = Controller->AddUnitNode(FAnimNextTests_PrintResult::StaticStruct());
bool bLinkExec3Added = Controller->AddLink(SetResultNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()), PrintResultUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()));
UE_RETURN_ON_ERROR(bLinkExec3Added, TEXT("Could not link variable node exec."));
URigVMVariableNode* GetResultNode = Controller->AddVariableNode("Result", RigVMTypeUtils::Int32Type, nullptr, true, TEXT(""));
UE_RETURN_ON_ERROR(GetResultNode != nullptr, TEXT("Could not add get variable node."));
bool bLinkResult2Added = Controller->AddLink(GetResultNode->FindPin("Value"), PrintResultUnitNode->FindPin("Result"));
UE_RETURN_ON_ERROR(bLinkResult2Added, TEXT("Could not link variable node."));
TArray<FString> Messages;
FRigVMRuntimeSettings RuntimeSettings;
RuntimeSettings.SetLogFunction([&Messages](const FRigVMLogSettings& InLogSettings, const FRigVMExecuteContext* InContext, const FString& Message)
{
Messages.Add(Message);
});
Asset->GetRigVMExtendedExecuteContext().SetRuntimeSettings(RuntimeSettings);
Asset->GetVM()->ExecuteVM(Asset->GetRigVMExtendedExecuteContext(), FRigUnit_AnimNextPrePhysicsEvent::DefaultEventName);
UE_RETURN_ON_ERROR(Messages.Num() == 1, TEXT("unexpected number of messages"));
UE_RETURN_ON_ERROR(Messages[0] == TEXT("Result = 3"), TEXT("unexpected result message"));
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FVariables_UOLBindings, "Animation.AnimNext.Variables.UOLBindings", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FVariables_UOLBindings::RunTest(const FString& InParameters)
{
ON_SCOPE_EXIT{ FUtils::CleanupAfterTests(); };
UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), UAnimNextModuleFactory::StaticClass());
UAnimNextModule* Asset = CastChecked<UAnimNextModule>(Factory->FactoryCreateNew(UAnimNextModule::StaticClass(), GetTransientPackage(), TEXT("TestAsset"), RF_Transient, nullptr, nullptr, NAME_None));
UE_RETURN_ON_ERROR(Asset != nullptr, "FVariables_UOLBindings -> Failed to create asset");
UAnimNextRigVMAssetEditorData* EditorData = UncookedOnly::FUtils::GetEditorData<UAnimNextRigVMAssetEditorData>(Asset);
UE_RETURN_ON_ERROR(EditorData != nullptr, "FVariables_UOLBindings -> Asset has no editor data.");
UniversalObjectLocator::FParseStringParams ParseStringParams;
ParseStringParams.Flags |= UniversalObjectLocator::EParseStringFlags::ErrorMessaging;
// Add variables
UAnimNextVariableEntry* PropertyEntry = EditorData->AddVariable(TEXT("Property"), FAnimNextParamType::GetType<int32>(), TEXT("1"));
UE_RETURN_ON_ERROR(PropertyEntry != nullptr, TEXT("Could not create new variable in graph."));
TInstancedStruct<FAnimNextUniversalObjectLocatorBindingData> BindingA = TInstancedStruct<FAnimNextUniversalObjectLocatorBindingData>::Make();
FAnimNextUniversalObjectLocatorBindingData& BindingDataA = BindingA.GetMutable<FAnimNextUniversalObjectLocatorBindingData>();
BindingDataA.Type = FAnimNextUniversalObjectLocatorBindingType::Property;
BindingDataA.Property = UAnimNextTestFuncLib::StaticClass()->FindPropertyByName(GET_MEMBER_NAME_CHECKED(UAnimNextTestFuncLib, ValueA));
UE_RETURN_ON_ERROR(BindingDataA.Property != nullptr, TEXT("Could not find property."));
UE::UniversalObjectLocator::FParseStringResult ResultA = BindingDataA.Locator.TryParseString(TEXT("uobj://animobjfunc?&payload0=/Script/AnimNextTestSuite.AnimNextTestFuncLib:GetObj"), ParseStringParams);
UE_RETURN_ON_ERROR(ResultA.bSuccess, TEXT("Could not parse UOL."));
PropertyEntry->SetBinding(MoveTemp(BindingA));
UAnimNextVariableEntry* ObjectAccessorEntry = EditorData->AddVariable(TEXT("ObjectAccessor"), FAnimNextParamType::GetType<int32>(), TEXT("2"));
UE_RETURN_ON_ERROR(ObjectAccessorEntry != nullptr, TEXT("Could not create new variable in graph."));
TInstancedStruct<FAnimNextUniversalObjectLocatorBindingData> BindingB = TInstancedStruct<FAnimNextUniversalObjectLocatorBindingData>::Make();
FAnimNextUniversalObjectLocatorBindingData& BindingDataB = BindingB.GetMutable<FAnimNextUniversalObjectLocatorBindingData>();
BindingDataB.Type = FAnimNextUniversalObjectLocatorBindingType::Function;
BindingDataB.Function = UAnimNextTestFuncLib::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UAnimNextTestFuncLib, GetValueB));
UE_RETURN_ON_ERROR(BindingDataB.Function != nullptr, TEXT("Could not find function."));
UE::UniversalObjectLocator::FParseStringResult ResultB = BindingDataB.Locator.TryParseString(TEXT("uobj://animobjfunc?&payload0=/Script/AnimNextTestSuite.AnimNextTestFuncLib:GetObj"), ParseStringParams);
UE_RETURN_ON_ERROR(ResultB.bSuccess, TEXT("Could not parse UOL."));
ObjectAccessorEntry->SetBinding(MoveTemp(BindingB));
UAnimNextVariableEntry* HoistedAccessorEntry = EditorData->AddVariable(TEXT("HoistedAccessor"), FAnimNextParamType::GetType<int32>(), TEXT("12"));
UE_RETURN_ON_ERROR(HoistedAccessorEntry != nullptr, TEXT("Could not create new variable in graph."));
TInstancedStruct<FAnimNextUniversalObjectLocatorBindingData> BindingC = TInstancedStruct<FAnimNextUniversalObjectLocatorBindingData>::Make();
FAnimNextUniversalObjectLocatorBindingData& BindingDataC = BindingC.GetMutable<FAnimNextUniversalObjectLocatorBindingData>();
BindingDataC.Type = FAnimNextUniversalObjectLocatorBindingType::HoistedFunction;
BindingDataC.Function = UAnimNextTestFuncLib::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UAnimNextTestFuncLib, GetValueC));
UE_RETURN_ON_ERROR(BindingDataC.Function != nullptr, TEXT("Could not find function."));
UE::UniversalObjectLocator::FParseStringResult ResultC = BindingDataC.Locator.TryParseString(TEXT("uobj://animobjfunc?&payload0=/Script/AnimNextTestSuite.AnimNextTestFuncLib:GetObj"), ParseStringParams);
UE_RETURN_ON_ERROR(ResultC.bSuccess, TEXT("Could not parse UOL."));
HoistedAccessorEntry->SetBinding(MoveTemp(BindingC));
// Get event graph
UAnimNextEventGraphEntry* EventGraph = Cast<UAnimNextEventGraphEntry>(EditorData->FindEntry(TEXT("PrePhysics")));
if(EventGraph == nullptr)
{
EventGraph = EditorData->AddEventGraph(TEXT("PrePhysics"), FRigUnit_AnimNextPrePhysicsEvent::StaticStruct());
}
UE_RETURN_ON_ERROR(EventGraph != nullptr, TEXT("Could not create new event graph in asset."));
URigVMGraph* RigVMGraph = EventGraph->GetRigVMGraph();
UE_RETURN_ON_ERROR(RigVMGraph->GetNodes().Num() == 1, TEXT("Unexpected number of nodes in new event graph."));
URigVMNode* EventNode = RigVMGraph->GetNodes()[0];
check(EventNode);
URigVMPin* ExecutePin = EventNode->FindPin("ExecuteContext");
UE_RETURN_ON_ERROR(ExecutePin != nullptr, TEXT("Could find initial execute pin."));
URigVMController* Controller = EditorData->GetController(EventGraph->GetRigVMGraph());
URigVMVariableNode* PropertyEntryNode = Controller->AddVariableNode("Property", RigVMTypeUtils::Int32Type, nullptr, true, TEXT(""));
UE_RETURN_ON_ERROR(PropertyEntryNode != nullptr, TEXT("Could not add get variable node."));
URigVMVariableNode* ObjectAccessorEntryNode = Controller->AddVariableNode("ObjectAccessor", RigVMTypeUtils::Int32Type, nullptr, true, TEXT(""));
UE_RETURN_ON_ERROR(ObjectAccessorEntryNode != nullptr, TEXT("Could not add get variable node."));
URigVMVariableNode* HoistedAccessorEntryNode = Controller->AddVariableNode("HoistedAccessor", RigVMTypeUtils::Int32Type, nullptr, true, TEXT(""));
UE_RETURN_ON_ERROR(HoistedAccessorEntryNode != nullptr, TEXT("Could not add get variable node."));
URigVMUnitNode* PrintPropertyUnitNode = Controller->AddUnitNode(FAnimNextTests_PrintResult::StaticStruct());
bool bLinkAAdded = Controller->AddLink(PropertyEntryNode->FindPin("Value"), PrintPropertyUnitNode->FindPin("Result"));
UE_RETURN_ON_ERROR(bLinkAAdded, TEXT("Could not link variable node."));
bool bLinkExec1Added = Controller->AddLink(EventNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()), PrintPropertyUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()));
UE_RETURN_ON_ERROR(bLinkExec1Added, TEXT("Could not link variable node exec."));
URigVMUnitNode* PrintObjectAccessorUnitNode = Controller->AddUnitNode(FAnimNextTests_PrintResult::StaticStruct());
bool bLinkBAdded = Controller->AddLink(ObjectAccessorEntryNode->FindPin("Value"), PrintObjectAccessorUnitNode->FindPin("Result"));
UE_RETURN_ON_ERROR(bLinkBAdded, TEXT("Could not link variable node."));
bool bLinkExec2Added = Controller->AddLink(PrintPropertyUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()), PrintObjectAccessorUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()));
UE_RETURN_ON_ERROR(bLinkExec2Added, TEXT("Could not link variable node exec."));
URigVMUnitNode* PrintHoistedAccessorUnitNode = Controller->AddUnitNode(FAnimNextTests_PrintResult::StaticStruct());
bool bLinkCAdded = Controller->AddLink(HoistedAccessorEntryNode->FindPin("Value"), PrintHoistedAccessorUnitNode->FindPin("Result"));
UE_RETURN_ON_ERROR(bLinkCAdded, TEXT("Could not link variable node."));
bool bLinkExec3Added = Controller->AddLink(PrintObjectAccessorUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()), PrintHoistedAccessorUnitNode->FindPin(FRigVMStruct::ExecuteContextName.ToString()));
UE_RETURN_ON_ERROR(bLinkExec3Added, TEXT("Could not link variable node exec."));
TArray<FString> Messages;
FRigVMRuntimeSettings RuntimeSettings;
RuntimeSettings.SetLogFunction([&Messages](const FRigVMLogSettings& InLogSettings, const FRigVMExecuteContext* InContext, const FString& Message)
{
Messages.Add(Message);
});
Asset->GetRigVMExtendedExecuteContext().SetRuntimeSettings(RuntimeSettings);
FAnimNextExecuteContext& AnimNextContext = Asset->GetRigVMExtendedExecuteContext().GetPublicDataSafe<FAnimNextExecuteContext>();
FAnimNextModuleInstance Instance(Asset, NewObject<UAnimNextComponent>(), nullptr, nullptr, EAnimNextModuleInitMethod::None);
FAnimNextModuleContextData ContextData(&Instance);
FScopedExecuteContextData ContextDataScope(AnimNextContext, ContextData);
// Execute bindings to fetch data
Asset->GetVM()->ExecuteVM(Asset->GetRigVMExtendedExecuteContext(), FRigUnit_AnimNextExecuteBindings_GT::EventName);
// Run PrePhysics event to print messages
Asset->GetVM()->ExecuteVM(Asset->GetRigVMExtendedExecuteContext(), FRigUnit_AnimNextPrePhysicsEvent::DefaultEventName);
UE_RETURN_ON_ERROR(Messages.Num() == 3, TEXT("unexpected number of messages"));
UE_RETURN_ON_ERROR(Messages[0] == TEXT("Result = 23"), TEXT("unexpected result message"));
UE_RETURN_ON_ERROR(Messages[1] == TEXT("Result = 42"), TEXT("unexpected result message"));
UE_RETURN_ON_ERROR(Messages[2] == TEXT("Result = 12345"), TEXT("unexpected result message"));
return true;
}
}
#endif // WITH_DEV_AUTOMATION_TESTS
FAnimNextTests_TestOperation_Execute()
{
Result = A + B;
}
FAnimNextTests_PrintResult_Execute()
{
ExecuteContext.Logf(EMessageSeverity::Info, TEXT("Result = %d"), Result);
}