You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1413 lines
42 KiB
C++
1413 lines
42 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ControlRigBlueprint.h"
|
|
|
|
#include "RigVMBlueprintGeneratedClass.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraphNode_Comment.h"
|
|
#include "Engine/SkeletalMesh.h"
|
|
#include "BlueprintActionDatabaseRegistrar.h"
|
|
#include "ControlRig.h"
|
|
#include "Graph/ControlRigGraph.h"
|
|
#include "Graph/ControlRigGraphSchema.h"
|
|
#include "UObject/ObjectSaveContext.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "ControlRigObjectVersion.h"
|
|
#include "BlueprintCompilationManager.h"
|
|
#include "RigVMCompiler/RigVMCompiler.h"
|
|
#include "RigVMCore/RigVMRegistry.h"
|
|
#include "Units/Execution/RigUnit_BeginExecution.h"
|
|
#include "Units/Hierarchy/RigUnit_SetBoneTransform.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "RigVMPythonUtils.h"
|
|
#include "RigVMTypeUtils.h"
|
|
#include "RigVMModel/Nodes/RigVMAggregateNode.h"
|
|
#include "Rigs/RigControlHierarchy.h"
|
|
#include "Settings/ControlRigSettings.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(ControlRigBlueprint)
|
|
|
|
#if WITH_EDITOR
|
|
#include "IControlRigEditorModule.h"
|
|
#include "Kismet2/WatchedPin.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Editor/UnrealEdEngine.h"
|
|
#include "Editor/Transactor.h"
|
|
#include "CookOnTheSide/CookOnTheFlyServer.h"
|
|
#endif//WITH_EDITOR
|
|
|
|
#define LOCTEXT_NAMESPACE "ControlRigBlueprint"
|
|
|
|
TArray<UControlRigBlueprint*> UControlRigBlueprint::sCurrentlyOpenedRigBlueprints;
|
|
#if WITH_EDITOR
|
|
const FName UControlRigBlueprint::ControlRigPanelNodeFactoryName(TEXT("FControlRigGraphPanelPinFactory"));
|
|
#endif
|
|
|
|
UControlRigBlueprint::UControlRigBlueprint(const FObjectInitializer& ObjectInitializer)
|
|
: URigVMBlueprint(ObjectInitializer)
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
GizmoLibrary_DEPRECATED = nullptr;
|
|
ShapeLibraries.Add(UControlRigSettings::Get()->DefaultShapeLibrary);
|
|
#endif
|
|
|
|
Validator = ObjectInitializer.CreateDefaultSubobject<UControlRigValidator>(this, TEXT("ControlRigValidator"));
|
|
DebugBoneRadius = 1.f;
|
|
|
|
bExposesAnimatableControls = false;
|
|
|
|
Hierarchy = CreateDefaultSubobject<URigHierarchy>(TEXT("Hierarchy"));
|
|
URigHierarchyController* Controller = Hierarchy->GetController(true);
|
|
// give BP a chance to propagate hierarchy changes to available control rig instances
|
|
Controller->OnModified().AddUObject(this, &UControlRigBlueprint::HandleHierarchyModified);
|
|
|
|
if(GetClass() == UControlRigBlueprint::StaticClass())
|
|
{
|
|
CommonInitialization(ObjectInitializer);
|
|
}
|
|
}
|
|
|
|
UControlRigBlueprint::UControlRigBlueprint()
|
|
{
|
|
}
|
|
|
|
UClass* UControlRigBlueprint::RegenerateClass(UClass* ClassToRegenerate, UObject* PreviousCDO)
|
|
{
|
|
UClass* Result = Super::RegenerateClass(ClassToRegenerate, PreviousCDO);
|
|
Hierarchy->CleanupInvalidCaches();
|
|
PropagateHierarchyFromBPToInstances();
|
|
return Result;
|
|
}
|
|
|
|
bool UControlRigBlueprint::RequiresForceLoadMembers(UObject* InObject) const
|
|
{
|
|
// old assets don't support preload filtering
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::RemoveParameters)
|
|
{
|
|
return UBlueprint::RequiresForceLoadMembers(InObject);
|
|
}
|
|
|
|
return Super::RequiresForceLoadMembers(InObject);
|
|
}
|
|
|
|
void UControlRigBlueprint::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent)
|
|
{
|
|
Super::PostEditChangeChainProperty(PropertyChangedEvent);
|
|
|
|
// Propagate shape libraries
|
|
if (PropertyChangedEvent.Property && PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, ShapeLibraries))
|
|
{
|
|
URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass();
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(false /* create if needed */));
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
ArchetypeInstances.Add(CDO);
|
|
|
|
// Propagate libraries to archetypes
|
|
for (UObject* Instance : ArchetypeInstances)
|
|
{
|
|
if (UControlRig* InstanceRig = Cast<UControlRig>(Instance))
|
|
{
|
|
InstanceRig->ShapeLibraries = ShapeLibraries;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UClass* UControlRigBlueprint::GetControlRigClass()
|
|
{
|
|
return GetRigVMHostClass();
|
|
}
|
|
|
|
USkeletalMesh* UControlRigBlueprint::GetPreviewMesh() const
|
|
{
|
|
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
if (!PreviewSkeletalMesh.IsValid())
|
|
{
|
|
(void)PreviewSkeletalMesh.LoadSynchronous();
|
|
}
|
|
|
|
return PreviewSkeletalMesh.Get();
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
void UControlRigBlueprint::SetPreviewMesh(USkeletalMesh* PreviewMesh, bool bMarkAsDirty/*=true*/)
|
|
{
|
|
#if WITH_EDITORONLY_DATA
|
|
if(bMarkAsDirty)
|
|
{
|
|
Modify();
|
|
}
|
|
|
|
PreviewSkeletalMesh = PreviewMesh;
|
|
#endif
|
|
}
|
|
|
|
void UControlRigBlueprint::Serialize(FArchive& Ar)
|
|
{
|
|
RigVMClient.SetOuterClientHost(this, GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, RigVMClient));
|
|
|
|
Super::Serialize(Ar);
|
|
|
|
if(Ar.IsObjectReferenceCollector())
|
|
{
|
|
Ar.UsingCustomVersion(FControlRigObjectVersion::GUID);
|
|
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
if (Ar.IsCooking() && ReferencedObjectPathsStored)
|
|
{
|
|
for (FSoftObjectPath ObjectPath : ReferencedObjectPaths)
|
|
{
|
|
ObjectPath.Serialize(Ar);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
TArray<IRigVMGraphFunctionHost*> ReferencedFunctionHosts = GetReferencedFunctionHosts(false);
|
|
|
|
for(IRigVMGraphFunctionHost* ReferencedFunctionHost : ReferencedFunctionHosts)
|
|
{
|
|
if (URigVMBlueprintGeneratedClass* BPGeneratedClass = Cast<URigVMBlueprintGeneratedClass>(ReferencedFunctionHost))
|
|
{
|
|
Ar << BPGeneratedClass;
|
|
}
|
|
}
|
|
|
|
for(const TSoftObjectPtr<UControlRigShapeLibrary>& ShapeLibraryPtr : ShapeLibraries)
|
|
{
|
|
if(ShapeLibraryPtr.IsValid())
|
|
{
|
|
UControlRigShapeLibrary* ShapeLibrary = ShapeLibraryPtr.Get();
|
|
Ar << ShapeLibrary;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Ar.IsLoading())
|
|
{
|
|
if(Model_DEPRECATED || FunctionLibrary_DEPRECATED)
|
|
{
|
|
TGuardValue<bool> DisableClientNotifs(RigVMClient.bSuspendNotifications, true);
|
|
RigVMClient.SetFromDeprecatedData(Model_DEPRECATED, FunctionLibrary_DEPRECATED);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PreSave(FObjectPreSaveContext ObjectSaveContext)
|
|
{
|
|
Super::PreSave(ObjectSaveContext);
|
|
|
|
bExposesAnimatableControls = false;
|
|
Hierarchy->ForEach<FRigControlElement>([this](FRigControlElement* ControlElement) -> bool
|
|
{
|
|
if (Hierarchy->IsAnimatable(ControlElement))
|
|
{
|
|
bExposesAnimatableControls = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void UControlRigBlueprint::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
|
|
{
|
|
#if WITH_EDITOR
|
|
|
|
// correct the offset transforms
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::ControlOffsetTransform)
|
|
{
|
|
HierarchyContainer_DEPRECATED.ControlHierarchy.PostLoad();
|
|
if (HierarchyContainer_DEPRECATED.ControlHierarchy.Num() > 0)
|
|
{
|
|
MarkDirtyDuringLoad();
|
|
}
|
|
|
|
for (FRigControl& Control : HierarchyContainer_DEPRECATED.ControlHierarchy)
|
|
{
|
|
const FTransform PreviousOffsetTransform = Control.GetTransformFromValue(ERigControlValueType::Initial);
|
|
Control.OffsetTransform = PreviousOffsetTransform;
|
|
Control.InitialValue = Control.Value;
|
|
|
|
if (Control.ControlType == ERigControlType::Transform)
|
|
{
|
|
Control.InitialValue = FRigControlValue::Make<FTransform>(FTransform::Identity);
|
|
}
|
|
else if (Control.ControlType == ERigControlType::TransformNoScale)
|
|
{
|
|
Control.InitialValue = FRigControlValue::Make<FTransformNoScale>(FTransformNoScale::Identity);
|
|
}
|
|
else if (Control.ControlType == ERigControlType::EulerTransform)
|
|
{
|
|
Control.InitialValue = FRigControlValue::Make<FEulerTransform>(FEulerTransform::Identity);
|
|
}
|
|
}
|
|
}
|
|
|
|
// convert the hierarchy from V1 to V2
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::RigHierarchyV2)
|
|
{
|
|
Modify();
|
|
|
|
TGuardValue<bool> SuspendNotifGuard(Hierarchy->GetSuspendNotificationsFlag(), true);
|
|
|
|
Hierarchy->Reset();
|
|
GetHierarchyController()->ImportFromHierarchyContainer(HierarchyContainer_DEPRECATED, false);
|
|
}
|
|
|
|
// perform backwards compat value upgrades
|
|
TArray<URigVMGraph*> GraphsToValidate = GetAllModels();
|
|
for (int32 GraphIndex = 0; GraphIndex < GraphsToValidate.Num(); GraphIndex++)
|
|
{
|
|
URigVMGraph* GraphToValidate = GraphsToValidate[GraphIndex];
|
|
if(GraphToValidate == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for(URigVMNode* Node : GraphToValidate->GetNodes())
|
|
{
|
|
TArray<URigVMPin*> Pins = Node->GetAllPinsRecursively();
|
|
for(URigVMPin* Pin : Pins)
|
|
{
|
|
if(Pin->GetCPPTypeObject() == StaticEnum<ERigElementType>())
|
|
{
|
|
if(Pin->GetDefaultValue() == TEXT("Space"))
|
|
{
|
|
if(URigVMController* Controller = GetController(GraphToValidate))
|
|
{
|
|
FRigVMControllerNotifGuard NotifGuard(Controller, true);
|
|
Controller->SetPinDefaultValue(Pin->GetPinPath(), TEXT("Null"), false, false, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
// upgrade the gizmo libraries to shape libraries
|
|
if(!GizmoLibrary_DEPRECATED.IsNull() || GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::RenameGizmoToShape)
|
|
{
|
|
// if it's an older file and it doesn't have the GizmoLibrary stored,
|
|
// refer to the previous default.
|
|
ShapeLibraries.Reset();
|
|
|
|
if(!GizmoLibrary_DEPRECATED.IsNull())
|
|
{
|
|
ShapeLibrariesToLoadOnPackageLoaded.Add(GizmoLibrary_DEPRECATED.ToString());
|
|
}
|
|
else
|
|
{
|
|
static const FString DefaultGizmoLibraryPath = TEXT("/ControlRig/Controls/DefaultGizmoLibrary.DefaultGizmoLibrary");
|
|
ShapeLibrariesToLoadOnPackageLoaded.Add(DefaultGizmoLibraryPath);
|
|
}
|
|
|
|
URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass();
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(false /* create if needed */));
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
ArchetypeInstances.Insert(CDO, 0);
|
|
|
|
for (UObject* Instance : ArchetypeInstances)
|
|
{
|
|
if (UControlRig* InstanceRig = Cast<UControlRig>(Instance))
|
|
{
|
|
InstanceRig->ShapeLibraries.Reset();
|
|
InstanceRig->GizmoLibrary_DEPRECATED.Reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
void UControlRigBlueprint::HandlePackageDone()
|
|
{
|
|
if (ShapeLibrariesToLoadOnPackageLoaded.Num() > 0)
|
|
{
|
|
for(const FString& ShapeLibraryToLoadOnPackageLoaded : ShapeLibrariesToLoadOnPackageLoaded)
|
|
{
|
|
ShapeLibraries.Add(LoadObject<UControlRigShapeLibrary>(nullptr, *ShapeLibraryToLoadOnPackageLoaded));
|
|
}
|
|
|
|
URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass();
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(false /* create if needed */));
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
ArchetypeInstances.Insert(CDO, 0);
|
|
|
|
for (UObject* Instance : ArchetypeInstances)
|
|
{
|
|
if (UControlRig* InstanceRig = Cast<UControlRig>(Instance))
|
|
{
|
|
InstanceRig->ShapeLibraries = ShapeLibraries;
|
|
}
|
|
}
|
|
|
|
ShapeLibrariesToLoadOnPackageLoaded.Reset();
|
|
}
|
|
|
|
PropagateHierarchyFromBPToInstances();
|
|
|
|
Super::HandlePackageDone();
|
|
}
|
|
|
|
#endif
|
|
|
|
UClass* UControlRigBlueprint::GetRigVMEdGraphNodeClass() const
|
|
{
|
|
return UControlRigGraphNode::StaticClass();
|
|
}
|
|
|
|
UClass* UControlRigBlueprint::GetRigVMEdGraphSchemaClass() const
|
|
{
|
|
return UControlRigGraphSchema::StaticClass();
|
|
}
|
|
|
|
UClass* UControlRigBlueprint::GetRigVMEdGraphClass() const
|
|
{
|
|
return UControlRigGraph::StaticClass();
|
|
}
|
|
|
|
UClass* UControlRigBlueprint::GetRigVMEditorSettingsClass() const
|
|
{
|
|
return UControlRigEditorSettings::StaticClass();
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
const FName& UControlRigBlueprint::GetPanelPinFactoryName() const
|
|
{
|
|
return ControlRigPanelNodeFactoryName;
|
|
}
|
|
|
|
IRigVMEditorModule* UControlRigBlueprint::GetEditorModule() const
|
|
{
|
|
return &IControlRigEditorModule::Get();
|
|
}
|
|
#endif
|
|
|
|
TArray<FString> UControlRigBlueprint::GeneratePythonCommands(const FString InNewBlueprintName)
|
|
{
|
|
TArray<FString> InternalCommands;
|
|
InternalCommands.Add(TEXT("import unreal"));
|
|
InternalCommands.Add(TEXT("unreal.load_module('ControlRigDeveloper')"));
|
|
InternalCommands.Add(TEXT("factory = unreal.ControlRigBlueprintFactory"));
|
|
InternalCommands.Add(FString::Printf(TEXT("blueprint = factory.create_new_control_rig_asset(desired_package_path = '%s')"), *InNewBlueprintName));
|
|
InternalCommands.Add(TEXT("hierarchy = blueprint.hierarchy"));
|
|
InternalCommands.Add(TEXT("hierarchy_controller = hierarchy.get_controller()"));
|
|
|
|
// Hierarchy
|
|
InternalCommands.Append(Hierarchy->GetController(true)->GeneratePythonCommands());
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
const FString PreviewMeshPath = GetPreviewMesh()->GetPathName();
|
|
InternalCommands.Add(FString::Printf(TEXT("blueprint.set_preview_mesh(unreal.load_object(name='%s', outer=None))"),
|
|
*PreviewMeshPath));
|
|
#endif
|
|
|
|
InternalCommands.Append(Super::GeneratePythonCommands(InNewBlueprintName));
|
|
return InternalCommands;
|
|
}
|
|
|
|
void UControlRigBlueprint::GetTypeActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
|
|
{
|
|
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
|
|
|
IControlRigEditorModule::Get().GetTypeActions((UControlRigBlueprint*)this, ActionRegistrar);
|
|
}
|
|
|
|
void UControlRigBlueprint::GetInstanceActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
|
|
{
|
|
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
|
|
|
IControlRigEditorModule::Get().GetInstanceActions((UControlRigBlueprint*)this, ActionRegistrar);
|
|
}
|
|
|
|
void UControlRigBlueprint::PostTransacted(const FTransactionObjectEvent& TransactionEvent)
|
|
{
|
|
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
|
Super::PostTransacted(TransactionEvent);
|
|
|
|
if (TransactionEvent.GetEventType() == ETransactionObjectEventType::UndoRedo)
|
|
{
|
|
TArray<FName> PropertiesChanged = TransactionEvent.GetChangedProperties();
|
|
if (PropertiesChanged.Contains(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, Hierarchy)))
|
|
{
|
|
int32 TransactionIndex = GEditor->Trans->FindTransactionIndex(TransactionEvent.GetTransactionId());
|
|
const FTransaction* Transaction = GEditor->Trans->GetTransaction(TransactionIndex);
|
|
|
|
if (Transaction->GetTitle().BuildSourceString() == TEXT("Transform Gizmo"))
|
|
{
|
|
PropagatePoseFromBPToInstances();
|
|
return;
|
|
}
|
|
|
|
PropagateHierarchyFromBPToInstances();
|
|
|
|
// make sure the bone name list is up 2 date for the editor graph
|
|
for (UEdGraph* Graph : UbergraphPages)
|
|
{
|
|
UControlRigGraph* RigGraph = Cast<UControlRigGraph>(Graph);
|
|
if (RigGraph == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
RigGraph->CacheNameLists(Hierarchy, &DrawContainer, ShapeLibraries);
|
|
}
|
|
|
|
RequestAutoVMRecompilation();
|
|
(void)MarkPackageDirty();
|
|
}
|
|
|
|
if (PropertiesChanged.Contains(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, DrawContainer)))
|
|
{
|
|
PropagateDrawInstructionsFromBPToInstances();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PostDuplicate(bool bDuplicateForPIE)
|
|
{
|
|
Super::PostDuplicate(bDuplicateForPIE);
|
|
|
|
if (URigHierarchyController* Controller = Hierarchy->GetController(true))
|
|
{
|
|
Controller->OnModified().RemoveAll(this);
|
|
Controller->OnModified().AddUObject(this, &UControlRigBlueprint::HandleHierarchyModified);
|
|
}
|
|
}
|
|
|
|
TArray<UControlRigBlueprint*> UControlRigBlueprint::GetCurrentlyOpenRigBlueprints()
|
|
{
|
|
return sCurrentlyOpenedRigBlueprints;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
const FControlRigShapeDefinition* UControlRigBlueprint::GetControlShapeByName(const FName& InName) const
|
|
{
|
|
TMap<FString, FString> LibraryNameMap;
|
|
if(UControlRig* ControlRig = Cast<UControlRig>(GetObjectBeingDebugged()))
|
|
{
|
|
LibraryNameMap = ControlRig->ShapeLibraryNameMap;
|
|
}
|
|
return UControlRigShapeLibrary::GetShapeByName(InName, ShapeLibraries, LibraryNameMap);
|
|
}
|
|
|
|
FName UControlRigBlueprint::AddTransientControl(URigVMPin* InPin)
|
|
{
|
|
TUniquePtr<FControlValueScope> ValueScope;
|
|
if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls
|
|
{
|
|
ValueScope = MakeUnique<FControlValueScope>(this);
|
|
}
|
|
|
|
// for now we only allow one pin control at the same time
|
|
ClearTransientControls();
|
|
|
|
URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass();
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(true /* create if needed */));
|
|
|
|
FRigElementKey SpaceKey;
|
|
FTransform OffsetTransform = FTransform::Identity;
|
|
if (URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(InPin->GetPinForLink()->GetNode()))
|
|
{
|
|
if (TSharedPtr<FStructOnScope> DefaultStructScope = UnitNode->ConstructStructInstance())
|
|
{
|
|
FRigUnit* DefaultStruct = (FRigUnit*)DefaultStructScope->GetStructMemory();
|
|
|
|
FString PinPath = InPin->GetPinForLink()->GetPinPath();
|
|
FString Left, Right;
|
|
|
|
if (URigVMPin::SplitPinPathAtStart(PinPath, Left, Right))
|
|
{
|
|
SpaceKey = DefaultStruct->DetermineSpaceForPin(Right, Hierarchy);
|
|
|
|
URigHierarchy* RigHierarchy = Hierarchy;
|
|
|
|
// use the active rig instead of the CDO rig because we want to access the evaluation result of the rig graph
|
|
// to calculate the offset transform, for example take a look at RigUnit_ModifyTransform
|
|
if (UControlRig* RigBeingDebugged = Cast<UControlRig>(GetObjectBeingDebugged()))
|
|
{
|
|
RigHierarchy = RigBeingDebugged->GetHierarchy();
|
|
}
|
|
|
|
OffsetTransform = DefaultStruct->DetermineOffsetTransformForPin(Right, RigHierarchy);
|
|
}
|
|
}
|
|
}
|
|
|
|
FName ReturnName = NAME_None;
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
UControlRig* InstancedControlRig = Cast<UControlRig>(ArchetypeInstance);
|
|
if (InstancedControlRig)
|
|
{
|
|
FName ControlName = InstancedControlRig->AddTransientControl(InPin, SpaceKey, OffsetTransform);
|
|
if (ReturnName == NAME_None)
|
|
{
|
|
ReturnName = ControlName;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReturnName;
|
|
}
|
|
|
|
FName UControlRigBlueprint::RemoveTransientControl(URigVMPin* InPin)
|
|
{
|
|
TUniquePtr<FControlValueScope> ValueScope;
|
|
if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls
|
|
{
|
|
ValueScope = MakeUnique<FControlValueScope>(this);
|
|
}
|
|
|
|
URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass();
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(true /* create if needed */));
|
|
|
|
FName RemovedName = NAME_None;
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
UControlRig* InstancedControlRig = Cast<UControlRig>(ArchetypeInstance);
|
|
if (InstancedControlRig)
|
|
{
|
|
FName Name = InstancedControlRig->RemoveTransientControl(InPin);
|
|
if (RemovedName == NAME_None)
|
|
{
|
|
RemovedName = Name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return RemovedName;
|
|
}
|
|
|
|
FName UControlRigBlueprint::AddTransientControl(const FRigElementKey& InElement)
|
|
{
|
|
TUniquePtr<FControlValueScope> ValueScope;
|
|
if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls
|
|
{
|
|
ValueScope = MakeUnique<FControlValueScope>(this);
|
|
}
|
|
URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass();
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(true /* create if needed */));
|
|
|
|
FName ReturnName = NAME_None;
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
|
|
// hierarchy transforms will be reset when ClearTransientControls() is called,
|
|
// so to retain any bone transform modifications we have to save them
|
|
TMap<UObject*, FTransform> SavedElementLocalTransforms;
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
UControlRig* InstancedControlRig = Cast<UControlRig>(ArchetypeInstance);
|
|
if (InstancedControlRig)
|
|
{
|
|
if (InstancedControlRig->DynamicHierarchy)
|
|
{
|
|
SavedElementLocalTransforms.FindOrAdd(InstancedControlRig) = InstancedControlRig->DynamicHierarchy->GetLocalTransform(InElement);
|
|
}
|
|
}
|
|
}
|
|
|
|
// for now we only allow one pin control at the same time
|
|
ClearTransientControls();
|
|
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
UControlRig* InstancedControlRig = Cast<UControlRig>(ArchetypeInstance);
|
|
if (InstancedControlRig)
|
|
{
|
|
// restore the element transforms so that transient controls are created at the right place
|
|
if (const FTransform* SavedTransform = SavedElementLocalTransforms.Find(InstancedControlRig))
|
|
{
|
|
if (InstancedControlRig->DynamicHierarchy)
|
|
{
|
|
InstancedControlRig->DynamicHierarchy->SetLocalTransform(InElement, *SavedTransform);
|
|
}
|
|
}
|
|
|
|
FName ControlName = InstancedControlRig->AddTransientControl(InElement);
|
|
if (ReturnName == NAME_None)
|
|
{
|
|
ReturnName = ControlName;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReturnName;
|
|
|
|
}
|
|
|
|
FName UControlRigBlueprint::RemoveTransientControl(const FRigElementKey& InElement)
|
|
{
|
|
TUniquePtr<FControlValueScope> ValueScope;
|
|
if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls
|
|
{
|
|
ValueScope = MakeUnique<FControlValueScope>(this);
|
|
}
|
|
|
|
URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass();
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(true /* create if needed */));
|
|
|
|
FName RemovedName = NAME_None;
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
UControlRig* InstancedControlRig = Cast<UControlRig>(ArchetypeInstance);
|
|
if (InstancedControlRig)
|
|
{
|
|
FName Name = InstancedControlRig->RemoveTransientControl(InElement);
|
|
if (RemovedName == NAME_None)
|
|
{
|
|
RemovedName = Name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return RemovedName;
|
|
}
|
|
|
|
void UControlRigBlueprint::ClearTransientControls()
|
|
{
|
|
TUniquePtr<FControlValueScope> ValueScope;
|
|
if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls
|
|
{
|
|
ValueScope = MakeUnique<FControlValueScope>(this);
|
|
}
|
|
|
|
if (URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass())
|
|
{
|
|
UControlRig* CDO = Cast<UControlRig>(RigClass->GetDefaultObject(true /* create if needed */));
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
CDO->GetArchetypeInstances(ArchetypeInstances);
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
UControlRig* InstancedControlRig = Cast<UControlRig>(ArchetypeInstance);
|
|
if (InstancedControlRig)
|
|
{
|
|
InstancedControlRig->ClearTransientControls();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void UControlRigBlueprint::SetupDefaultObjectDuringCompilation(URigVMHost* InCDO)
|
|
{
|
|
Super::SetupDefaultObjectDuringCompilation(InCDO);
|
|
CastChecked<UControlRig>(InCDO)->GetHierarchy()->CopyHierarchy(Hierarchy);
|
|
}
|
|
|
|
void UControlRigBlueprint::SetupPinRedirectorsForBackwardsCompatibility()
|
|
{
|
|
for(URigVMGraph* Model : RigVMClient)
|
|
{
|
|
for (URigVMNode* Node : Model->GetNodes())
|
|
{
|
|
if (URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(Node))
|
|
{
|
|
UScriptStruct* Struct = UnitNode->GetScriptStruct();
|
|
if (Struct == FRigUnit_SetBoneTransform::StaticStruct())
|
|
{
|
|
URigVMPin* TransformPin = UnitNode->FindPin(TEXT("Transform"));
|
|
URigVMPin* ResultPin = UnitNode->FindPin(TEXT("Result"));
|
|
GetOrCreateController()->AddPinRedirector(false, true, TransformPin->GetPinPath(), ResultPin->GetPinPath());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PathDomainSpecificContentOnLoad()
|
|
{
|
|
PatchRigElementKeyCacheOnLoad();
|
|
PatchPropagateToChildren();
|
|
}
|
|
|
|
void UControlRigBlueprint::PatchRigElementKeyCacheOnLoad()
|
|
{
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::RigElementKeyCache)
|
|
{
|
|
for (URigVMGraph* Graph : GetAllModels())
|
|
{
|
|
URigVMController* Controller = GetOrCreateController(Graph);
|
|
TGuardValue<bool> DisablePinDefaultValueValidation(Controller->bValidatePinDefaults, false);
|
|
FRigVMControllerNotifGuard NotifGuard(Controller, true);
|
|
for (URigVMNode* Node : Graph->GetNodes())
|
|
{
|
|
if (URigVMUnitNode* UnitNode = Cast<URigVMUnitNode>(Node))
|
|
{
|
|
UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct();
|
|
FString FunctionName = FString::Printf(TEXT("%s::%s"), *ScriptStruct->GetStructCPPName(), *UnitNode->GetMethodName().ToString());
|
|
const FRigVMFunction* Function = FRigVMRegistry::Get().FindFunction(*FunctionName);
|
|
check(Function);
|
|
for (TFieldIterator<FProperty> It(Function->Struct); It; ++It)
|
|
{
|
|
if (It->GetCPPType() == TEXT("FCachedRigElement"))
|
|
{
|
|
if (URigVMPin* Pin = Node->FindPin(It->GetName()))
|
|
{
|
|
int32 BoneIndex = FCString::Atoi(*Pin->GetDefaultValue());
|
|
FRigElementKey Key = Hierarchy->GetKey(BoneIndex);
|
|
FCachedRigElement DefaultValueElement(Key, Hierarchy);
|
|
FString Result;
|
|
TBaseStructure<FCachedRigElement>::Get()->ExportText(Result, &DefaultValueElement, nullptr, nullptr, PPF_None, nullptr);
|
|
Controller->SetPinDefaultValue(Pin->GetPinPath(), Result, true, false, false);
|
|
MarkDirtyDuringLoad();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// change the default value form False to True for transform nodes
|
|
void UControlRigBlueprint::PatchPropagateToChildren()
|
|
{
|
|
// no need to update default value past this version
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) >= FControlRigObjectVersion::RenameGizmoToShape)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto IsNullOrControl = [](const URigVMPin* InPin)
|
|
{
|
|
const bool bHasItem = InPin->GetCPPTypeObject() == FRigElementKey::StaticStruct() && InPin->GetName() == "Item";
|
|
if (!bHasItem)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (const URigVMPin* TypePin = InPin->FindSubPin(TEXT("Type")))
|
|
{
|
|
const FString& TypeValue = TypePin->GetDefaultValue();
|
|
return TypeValue == TEXT("Null") || TypeValue == TEXT("Space") || TypeValue == TEXT("Control");
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
auto IsPropagateChildren = [](const URigVMPin* InPin)
|
|
{
|
|
return InPin->GetCPPType() == TEXT("bool") && InPin->GetName() == TEXT("bPropagateToChildren");
|
|
};
|
|
|
|
auto FindPropagatePin = [IsNullOrControl, IsPropagateChildren](const URigVMNode* InNode)-> URigVMPin*
|
|
{
|
|
URigVMPin* PropagatePin = nullptr;
|
|
URigVMPin* ItemPin = nullptr;
|
|
for (URigVMPin* Pin: InNode->GetPins())
|
|
{
|
|
// look for Item pin
|
|
if (!ItemPin && IsNullOrControl(Pin))
|
|
{
|
|
ItemPin = Pin;
|
|
}
|
|
|
|
// look for bPropagateToChildren pin
|
|
if (!PropagatePin && IsPropagateChildren(Pin))
|
|
{
|
|
PropagatePin = Pin;
|
|
}
|
|
|
|
// return propagation pin if both found
|
|
if (ItemPin && PropagatePin)
|
|
{
|
|
return PropagatePin;
|
|
}
|
|
}
|
|
return nullptr;
|
|
};
|
|
|
|
for (URigVMGraph* Graph : GetAllModels())
|
|
{
|
|
TArray< const URigVMPin* > PinsToUpdate;
|
|
for (const URigVMNode* Node : Graph->GetNodes())
|
|
{
|
|
if (const URigVMPin* PropagatePin = FindPropagatePin(Node))
|
|
{
|
|
PinsToUpdate.Add(PropagatePin);
|
|
}
|
|
}
|
|
|
|
if (URigVMController* Controller = GetOrCreateController(Graph))
|
|
{
|
|
FRigVMControllerNotifGuard NotifGuard(Controller, true);
|
|
for (const URigVMPin* Pin: PinsToUpdate)
|
|
{
|
|
Controller->SetPinDefaultValue(Pin->GetPinPath(), TEXT("True"), false, false, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PatchFunctionsOnLoad()
|
|
{
|
|
URigVMBlueprintGeneratedClass* CRGeneratedClass = GetRigVMBlueprintGeneratedClass();
|
|
FRigVMGraphFunctionStore& Store = CRGeneratedClass->GraphFunctionStore;
|
|
const URigVMFunctionLibrary* Library = GetLocalFunctionLibrary();
|
|
|
|
TMap<URigVMLibraryNode*, FRigVMGraphFunctionHeader> OldHeaders;
|
|
|
|
// Backwards compatibility. Store public access in the model
|
|
TArray<FName> BackwardsCompatiblePublicFunctions;
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::StoreFunctionsInGeneratedClass)
|
|
{
|
|
for (const FRigVMOldPublicFunctionData& OldPublicFunction : PublicFunctions_DEPRECATED)
|
|
{
|
|
BackwardsCompatiblePublicFunctions.Add(OldPublicFunction.Name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (GetLinkerCustomVersion(FUE5MainStreamObjectVersion::GUID) < FUE5MainStreamObjectVersion::RigVMSaveFunctionAccessInModel)
|
|
{
|
|
for (const FRigVMGraphFunctionData& FunctionData : Store.PublicFunctions)
|
|
{
|
|
BackwardsCompatiblePublicFunctions.Add(FunctionData.Header.Name);
|
|
URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(FunctionData.Header.LibraryPointer.LibraryNode.ResolveObject());
|
|
OldHeaders.Add(LibraryNode, FunctionData.Header);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Addressing issue where PublicGraphFunctions is populated, but the model PublicFunctionNames is not
|
|
URigVMFunctionLibrary* FunctionLibrary = GetLocalFunctionLibrary();
|
|
if (FunctionLibrary)
|
|
{
|
|
if (PublicGraphFunctions.Num() > FunctionLibrary->PublicFunctionNames.Num())
|
|
{
|
|
for (const FRigVMGraphFunctionHeader& PublicHeader : PublicGraphFunctions)
|
|
{
|
|
BackwardsCompatiblePublicFunctions.Add(PublicHeader.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lets rebuild the FunctionStore from the model
|
|
if (FunctionLibrary)
|
|
{
|
|
Store.PublicFunctions.Reset();
|
|
Store.PrivateFunctions.Reset();
|
|
|
|
for (URigVMLibraryNode* LibraryNode : FunctionLibrary->GetFunctions())
|
|
{
|
|
bool bIsPublic = FunctionLibrary->IsFunctionPublic(LibraryNode->GetFName());
|
|
if (!bIsPublic)
|
|
{
|
|
bIsPublic = BackwardsCompatiblePublicFunctions.Contains(LibraryNode->GetFName());
|
|
if (bIsPublic)
|
|
{
|
|
FunctionLibrary->PublicFunctionNames.Add(LibraryNode->GetFName());
|
|
}
|
|
}
|
|
|
|
FRigVMGraphFunctionHeader Header = LibraryNode->GetFunctionHeader(CRGeneratedClass);
|
|
if (FRigVMGraphFunctionHeader* OldHeader = OldHeaders.Find(LibraryNode))
|
|
{
|
|
Header.ExternalVariables = OldHeader->ExternalVariables;
|
|
Header.Dependencies = OldHeader->Dependencies;
|
|
}
|
|
Store.AddFunction(Header, bIsPublic);
|
|
|
|
}
|
|
}
|
|
|
|
// Update dependencies and external variables if needed
|
|
for (URigVMLibraryNode* LibraryNode : Library->GetFunctions())
|
|
{
|
|
GetRigVMClient()->UpdateExternalVariablesForFunction(LibraryNode);
|
|
GetRigVMClient()->UpdateDependenciesForFunction(LibraryNode);
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::CreateMemberVariablesOnLoad()
|
|
{
|
|
#if WITH_EDITOR
|
|
|
|
const int32 LinkerVersion = GetLinkerCustomVersion(FControlRigObjectVersion::GUID);
|
|
if (LinkerVersion < FControlRigObjectVersion::SwitchedToRigVM)
|
|
{
|
|
// ignore errors during the first potential compile of the VM
|
|
// since that this point variable nodes may still be ill-formed.
|
|
TGuardValue<FRigVMReportDelegate> SuspendReportDelegate(VMCompileSettings.ASTSettings.ReportDelegate,
|
|
FRigVMReportDelegate::CreateLambda([](EMessageSeverity::Type, UObject*, const FString&)
|
|
{
|
|
// do nothing
|
|
})
|
|
);
|
|
InitializeModelIfRequired();
|
|
}
|
|
|
|
AddedMemberVariableMap.Reset();
|
|
|
|
for (int32 VariableIndex = 0; VariableIndex < NewVariables.Num(); VariableIndex++)
|
|
{
|
|
AddedMemberVariableMap.Add(NewVariables[VariableIndex].VarName, VariableIndex);
|
|
}
|
|
|
|
if (RigVMClient.Num() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// setup variables on the blueprint based on the previous "parameters"
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::BlueprintVariableSupport)
|
|
{
|
|
TSharedPtr<FKismetNameValidator> NameValidator = MakeShareable(new FKismetNameValidator(this, NAME_None, nullptr));
|
|
|
|
auto CreateVariable = [this, NameValidator](const URigVMVariableNode* InVariableNode)
|
|
{
|
|
if (!InVariableNode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static const FString VariableString = TEXT("Variable");
|
|
if (URigVMPin* VariablePin = InVariableNode->FindPin(VariableString))
|
|
{
|
|
if (VariablePin->GetDirection() != ERigVMPinDirection::Visible)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FRigVMGraphVariableDescription Description = InVariableNode->GetVariableDescription();
|
|
if (AddedMemberVariableMap.Contains(Description.Name))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FEdGraphPinType PinType = RigVMTypeUtils::PinTypeFromExternalVariable(Description.ToExternalVariable());
|
|
if (!PinType.PinCategory.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FName VarName = FindHostMemberVariableUniqueName(NameValidator, Description.Name.ToString());
|
|
const int32 VariableIndex = AddHostMemberVariable(this, VarName, PinType, false, false, FString());
|
|
if (VariableIndex != INDEX_NONE)
|
|
{
|
|
AddedMemberVariableMap.Add(Description.Name, VariableIndex);
|
|
MarkDirtyDuringLoad();
|
|
}
|
|
};
|
|
|
|
auto CreateParameter = [this, NameValidator](const URigVMParameterNode* InParameterNode)
|
|
{
|
|
if (!InParameterNode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static const FString ParameterString = TEXT("Parameter");
|
|
if (const URigVMPin* ParameterPin = InParameterNode->FindPin(ParameterString))
|
|
{
|
|
if (ParameterPin->GetDirection() != ERigVMPinDirection::Visible)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
const FRigVMGraphParameterDescription Description = InParameterNode->GetParameterDescription();
|
|
if (AddedMemberVariableMap.Contains(Description.Name))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FEdGraphPinType PinType = RigVMTypeUtils::PinTypeFromExternalVariable(Description.ToExternalVariable());
|
|
if (!PinType.PinCategory.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FName VarName = FindHostMemberVariableUniqueName(NameValidator, Description.Name.ToString());
|
|
const int32 VariableIndex = AddHostMemberVariable(this, VarName, PinType, true, !Description.bIsInput, FString());
|
|
|
|
if (VariableIndex != INDEX_NONE)
|
|
{
|
|
AddedMemberVariableMap.Add(Description.Name, VariableIndex);
|
|
MarkDirtyDuringLoad();
|
|
}
|
|
};
|
|
|
|
for (const URigVMGraph* Model : RigVMClient)
|
|
{
|
|
const TArray<URigVMNode*>& Nodes = Model->GetNodes();
|
|
for (const URigVMNode* Node : Nodes)
|
|
{
|
|
if (const URigVMVariableNode* VariableNode = Cast<URigVMVariableNode>(Node))
|
|
{
|
|
CreateVariable(VariableNode);
|
|
}
|
|
|
|
// Leaving this for backwards compatibility, even though we don't support parameters anymore
|
|
// When a parameter node is found, we will create a variable
|
|
else if (const URigVMParameterNode* ParameterNode = Cast<URigVMParameterNode>(Node))
|
|
{
|
|
CreateParameter(ParameterNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void UControlRigBlueprint::PatchVariableNodesOnLoad()
|
|
{
|
|
#if WITH_EDITOR
|
|
|
|
// setup variables on the blueprint based on the previous "parameters"
|
|
if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::BlueprintVariableSupport)
|
|
{
|
|
TGuardValue<bool> GuardNotifsSelf(bSuspendModelNotificationsForSelf, true);
|
|
|
|
check(GetDefaultModel());
|
|
|
|
auto PatchVariableNode = [this](const URigVMVariableNode* InVariableNode)
|
|
{
|
|
if (!InVariableNode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FRigVMGraphVariableDescription Description = InVariableNode->GetVariableDescription();
|
|
if (!AddedMemberVariableMap.Contains(Description.Name))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int32 VariableIndex = AddedMemberVariableMap.FindChecked(Description.Name);
|
|
const FName VarName = NewVariables[VariableIndex].VarName;
|
|
|
|
GetOrCreateController()->RefreshVariableNode(
|
|
InVariableNode->GetFName(), VarName, Description.CPPType, Description.CPPTypeObject, false);
|
|
|
|
MarkDirtyDuringLoad();
|
|
};
|
|
|
|
auto PatchParameterNode = [this](const URigVMParameterNode* InParameterNode)
|
|
{
|
|
if (!InParameterNode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FRigVMGraphParameterDescription Description = InParameterNode->GetParameterDescription();
|
|
if (!AddedMemberVariableMap.Contains(Description.Name))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int32 VariableIndex = AddedMemberVariableMap.FindChecked(Description.Name);
|
|
const FName VarName = NewVariables[VariableIndex].VarName;
|
|
|
|
GetOrCreateController()->ReplaceParameterNodeWithVariable(
|
|
InParameterNode->GetFName(), VarName, Description.CPPType, Description.CPPTypeObject, false);
|
|
|
|
MarkDirtyDuringLoad();
|
|
};
|
|
|
|
for(const URigVMGraph* Model : RigVMClient)
|
|
{
|
|
TArray<URigVMNode*> Nodes = Model->GetNodes();
|
|
for (URigVMNode* Node : Nodes)
|
|
{
|
|
if (const URigVMVariableNode* VariableNode = Cast<URigVMVariableNode>(Node))
|
|
{
|
|
PatchVariableNode(VariableNode);
|
|
}
|
|
else if (const URigVMParameterNode* ParameterNode = Cast<URigVMParameterNode>(Node))
|
|
{
|
|
PatchParameterNode(ParameterNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
Super::PatchVariableNodesOnLoad();
|
|
}
|
|
|
|
void UControlRigBlueprint::PropagatePoseFromInstanceToBP(UControlRig* InControlRig) const
|
|
{
|
|
check(InControlRig);
|
|
// current transforms in BP and CDO are meaningless, no need to copy them
|
|
// we use BP hierarchy to initialize CDO and instances' hierarchy,
|
|
// so it should always be in the initial state.
|
|
Hierarchy->CopyPose(InControlRig->GetHierarchy(), false, true, false, true);
|
|
}
|
|
|
|
void UControlRigBlueprint::PropagatePoseFromBPToInstances() const
|
|
{
|
|
if (UClass* MyControlRigClass = GeneratedClass)
|
|
{
|
|
if (UControlRig* DefaultObject = Cast<UControlRig>(MyControlRigClass->GetDefaultObject(false)))
|
|
{
|
|
DefaultObject->PostInitInstanceIfRequired();
|
|
DefaultObject->GetHierarchy()->CopyPose(Hierarchy, true, true, true);
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
DefaultObject->GetArchetypeInstances(ArchetypeInstances);
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
if (UControlRig* InstanceRig = Cast<UControlRig>(ArchetypeInstance))
|
|
{
|
|
InstanceRig->PostInitInstanceIfRequired();
|
|
InstanceRig->GetHierarchy()->CopyPose(Hierarchy, true, true, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PropagateHierarchyFromBPToInstances() const
|
|
{
|
|
if (UClass* MyControlRigClass = GeneratedClass)
|
|
{
|
|
if (UControlRig* DefaultObject = Cast<UControlRig>(MyControlRigClass->GetDefaultObject(false)))
|
|
{
|
|
DefaultObject->PostInitInstanceIfRequired();
|
|
DefaultObject->GetHierarchy()->CopyHierarchy(Hierarchy);
|
|
DefaultObject->HierarchySettings = HierarchySettings;
|
|
DefaultObject->Initialize(true);
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
DefaultObject->GetArchetypeInstances(ArchetypeInstances);
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
if (UControlRig* InstanceRig = Cast<UControlRig>(ArchetypeInstance))
|
|
{
|
|
InstanceRig->PostInitInstanceIfRequired();
|
|
InstanceRig->GetHierarchy()->CopyHierarchy(Hierarchy);
|
|
InstanceRig->HierarchySettings = HierarchySettings;
|
|
InstanceRig->Initialize(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PropagateDrawInstructionsFromBPToInstances() const
|
|
{
|
|
if (UClass* MyControlRigClass = GeneratedClass)
|
|
{
|
|
if (UControlRig* DefaultObject = Cast<UControlRig>(MyControlRigClass->GetDefaultObject(false)))
|
|
{
|
|
DefaultObject->DrawContainer = DrawContainer;
|
|
|
|
TArray<UObject*> ArchetypeInstances;
|
|
DefaultObject->GetArchetypeInstances(ArchetypeInstances);
|
|
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
if (UControlRig* InstanceRig = Cast<UControlRig>(ArchetypeInstance))
|
|
{
|
|
InstanceRig->DrawContainer = DrawContainer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// make sure the bone name list is up 2 date for the editor graph
|
|
for (UEdGraph* Graph : UbergraphPages)
|
|
{
|
|
UControlRigGraph* RigGraph = Cast<UControlRigGraph>(Graph);
|
|
if (RigGraph == nullptr)
|
|
{
|
|
continue;
|
|
}
|
|
RigGraph->CacheNameLists(Hierarchy, &DrawContainer, ShapeLibraries);
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PropagatePropertyFromBPToInstances(FRigElementKey InRigElement, const FProperty* InProperty) const
|
|
{
|
|
int32 ElementIndex = Hierarchy->GetIndex(InRigElement);
|
|
ensure(ElementIndex != INDEX_NONE);
|
|
check(InProperty);
|
|
|
|
if (UClass* MyControlRigClass = GeneratedClass)
|
|
{
|
|
if (UControlRig* DefaultObject = Cast<UControlRig>(MyControlRigClass->GetDefaultObject(false)))
|
|
{
|
|
TArray<UObject*> ArchetypeInstances;
|
|
DefaultObject->GetArchetypeInstances(ArchetypeInstances);
|
|
|
|
const int32 PropertyOffset = InProperty->GetOffset_ReplaceWith_ContainerPtrToValuePtr();
|
|
const int32 PropertySize = InProperty->GetSize();
|
|
|
|
uint8* Source = ((uint8*)Hierarchy->Get(ElementIndex)) + PropertyOffset;
|
|
for (UObject* ArchetypeInstance : ArchetypeInstances)
|
|
{
|
|
if (UControlRig* InstanceRig = Cast<UControlRig>(ArchetypeInstance))
|
|
{
|
|
InstanceRig->PostInitInstanceIfRequired();
|
|
uint8* Dest = ((uint8*)InstanceRig->GetHierarchy()->Get(ElementIndex)) + PropertyOffset;
|
|
FMemory::Memcpy(Dest, Source, PropertySize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UControlRigBlueprint::PropagatePropertyFromInstanceToBP(FRigElementKey InRigElement, const FProperty* InProperty, UControlRig* InInstance) const
|
|
{
|
|
const int32 ElementIndex = Hierarchy->GetIndex(InRigElement);
|
|
ensure(ElementIndex != INDEX_NONE);
|
|
check(InProperty);
|
|
|
|
const int32 PropertyOffset = InProperty->GetOffset_ReplaceWith_ContainerPtrToValuePtr();
|
|
const int32 PropertySize = InProperty->GetSize();
|
|
uint8* Source = ((uint8*)InInstance->GetHierarchy()->Get(ElementIndex)) + PropertyOffset;
|
|
uint8* Dest = ((uint8*)Hierarchy->Get(ElementIndex)) + PropertyOffset;
|
|
FMemory::Memcpy(Dest, Source, PropertySize);
|
|
}
|
|
|
|
|
|
void UControlRigBlueprint::HandleHierarchyModified(ERigHierarchyNotification InNotification, URigHierarchy* InHierarchy, const FRigBaseElement* InElement)
|
|
{
|
|
#if WITH_EDITOR
|
|
|
|
if(bSuspendAllNotifications)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch(InNotification)
|
|
{
|
|
case ERigHierarchyNotification::ElementRemoved:
|
|
{
|
|
Modify();
|
|
Influences.OnKeyRemoved(InElement->GetKey());
|
|
PropagateHierarchyFromBPToInstances();
|
|
break;
|
|
}
|
|
case ERigHierarchyNotification::ElementRenamed:
|
|
{
|
|
Modify();
|
|
Influences.OnKeyRenamed(FRigElementKey(InHierarchy->GetPreviousName(InElement->GetKey()), InElement->GetType()), InElement->GetKey());
|
|
PropagateHierarchyFromBPToInstances();
|
|
break;
|
|
}
|
|
case ERigHierarchyNotification::ElementAdded:
|
|
case ERigHierarchyNotification::ParentChanged:
|
|
case ERigHierarchyNotification::ElementReordered:
|
|
case ERigHierarchyNotification::HierarchyReset:
|
|
{
|
|
Modify();
|
|
PropagateHierarchyFromBPToInstances();
|
|
break;
|
|
}
|
|
case ERigHierarchyNotification::ElementSelected:
|
|
{
|
|
bool bClearTransientControls = true;
|
|
if (const FRigControlElement* ControlElement = Cast<FRigControlElement>(InElement))
|
|
{
|
|
if (ControlElement->Settings.bIsTransientControl)
|
|
{
|
|
bClearTransientControls = false;
|
|
}
|
|
}
|
|
|
|
if(bClearTransientControls)
|
|
{
|
|
if(UControlRig* RigBeingDebugged = Cast<UControlRig>(GetObjectBeingDebugged()))
|
|
{
|
|
const FName TransientControlName = UControlRig::GetNameForTransientControl(InElement->GetKey());
|
|
const FRigElementKey TransientControlKey(TransientControlName, ERigElementType::Control);
|
|
if (const FRigControlElement* ControlElement = RigBeingDebugged->GetHierarchy()->Find<FRigControlElement>(TransientControlKey))
|
|
{
|
|
if (ControlElement->Settings.bIsTransientControl)
|
|
{
|
|
bClearTransientControls = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bClearTransientControls)
|
|
{
|
|
ClearTransientControls();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
HierarchyModifiedEvent.Broadcast(InNotification, InHierarchy, InElement);
|
|
|
|
#endif
|
|
}
|
|
|
|
UControlRigBlueprint::FControlValueScope::FControlValueScope(UControlRigBlueprint* InBlueprint)
|
|
: Blueprint(InBlueprint)
|
|
{
|
|
#if WITH_EDITOR
|
|
check(Blueprint);
|
|
|
|
if (UControlRig* CR = Cast<UControlRig>(Blueprint->GetObjectBeingDebugged()))
|
|
{
|
|
TArray<FRigControlElement*> Controls = CR->AvailableControls();
|
|
for (FRigControlElement* ControlElement : Controls)
|
|
{
|
|
ControlValues.Add(ControlElement->GetName(), CR->GetControlValue(ControlElement->GetName()));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
UControlRigBlueprint::FControlValueScope::~FControlValueScope()
|
|
{
|
|
#if WITH_EDITOR
|
|
check(Blueprint);
|
|
|
|
if (UControlRig* CR = Cast<UControlRig>(Blueprint->GetObjectBeingDebugged()))
|
|
{
|
|
for (const TPair<FName, FRigControlValue>& Pair : ControlValues)
|
|
{
|
|
if (CR->FindControl(Pair.Key))
|
|
{
|
|
CR->SetControlValue(Pair.Key, Pair.Value);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|
|
|
|
|