// 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 "ModularRig.h" #include "ModularRigController.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 "Units/ControlRigNodeWorkflow.h" #include "Units/Execution/RigUnit_PrepareForExecution.h" #include "Units/Execution/RigUnit_DynamicHierarchy.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" #include "ScopedTransaction.h" #include "Algo/Count.h" #endif//WITH_EDITOR #define LOCTEXT_NAMESPACE "ControlRigBlueprint" TArray UControlRigBlueprint::sCurrentlyOpenedRigBlueprints; UControlRigBlueprint::UControlRigBlueprint(const FObjectInitializer& ObjectInitializer) : URigVMBlueprint(ObjectInitializer) { #if WITH_EDITORONLY_DATA GizmoLibrary_DEPRECATED = nullptr; ShapeLibraries.Add(UControlRigSettings::Get()->DefaultShapeLibrary); #endif Validator = ObjectInitializer.CreateDefaultSubobject(this, TEXT("ControlRigValidator")); DebugBoneRadius = 1.f; bExposesAnimatableControls = false; Hierarchy = CreateDefaultSubobject(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); } ModularRigModel.SetOuterClientHost(this); UModularRigController* ModularController = ModularRigModel.GetController(); ModularController->OnModified().AddUObject(this, &UControlRigBlueprint::HandleRigModulesModified); } UControlRigBlueprint::UControlRigBlueprint() { ModulesRecompilationBracket = 0; } 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::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); // if this is any of our external variables we need to request construction so that the rig rebuilds itself if(NewVariables.ContainsByPredicate([&PropertyChangedEvent](const FBPVariableDescription& Variable) { return Variable.VarName == PropertyChangedEvent.GetMemberPropertyName(); })) { if(UControlRig* DebuggedControlRig = Cast(GetObjectBeingDebugged())) { if(const FProperty* PropertyOnRig = DebuggedControlRig->GetClass()->FindPropertyByName(PropertyChangedEvent.MemberProperty->GetFName())) { if(PropertyOnRig->SameType(PropertyChangedEvent.MemberProperty)) { UControlRig* CDO = DebuggedControlRig->GetClass()->GetDefaultObject(); const uint8* SourceMemory = PropertyOnRig->ContainerPtrToValuePtr(CDO); uint8* TargetMemory = PropertyOnRig->ContainerPtrToValuePtr(DebuggedControlRig); PropertyOnRig->CopyCompleteValue(TargetMemory, SourceMemory); } } DebuggedControlRig->RequestConstruction(); } } } 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(RigClass->GetDefaultObject(false /* create if needed */)); TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); ArchetypeInstances.Add(CDO); // Propagate libraries to archetypes for (UObject* Instance : ArchetypeInstances) { if (UControlRig* InstanceRig = Cast(Instance)) { InstanceRig->ShapeLibraries = ShapeLibraries; } } } } UClass* UControlRigBlueprint::GetControlRigClass() const { return GetRigVMHostClass(); } bool UControlRigBlueprint::IsModularRig() const { if(const UClass* Class = GetControlRigClass()) { return Class->IsChildOf(UModularRig::StaticClass()); } return false; } 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 } bool UControlRigBlueprint::IsControlRigModule() const { return RigModuleSettings.Identifier.IsValid(); } #if WITH_EDITORONLY_DATA bool UControlRigBlueprint::CanTurnIntoControlRigModule(bool InAutoConvertHierarchy, FString* OutErrorMessage) const { if(IsControlRigModule()) { if(OutErrorMessage) { static const FString Message = TEXT("This asset is already a Control Rig Module."); *OutErrorMessage = Message; } return false; } if (GetRigVMHostClass()->IsChildOf()) { if(OutErrorMessage) { static const FString Message = TEXT("This asset is a Modular Rig."); *OutErrorMessage = Message; } return false; } if(Hierarchy == nullptr) { if(OutErrorMessage) { static const FString Message = TEXT("This asset contains no hierarchy."); *OutErrorMessage = Message; } return false; } const TArray Keys = Hierarchy->GetAllKeys(true); for(const FRigElementKey& Key : Keys) { if(!InAutoConvertHierarchy) { if(Key.Type != ERigElementType::Bone && Key.Type != ERigElementType::Curve && Key.Type != ERigElementType::Connector) { if(OutErrorMessage) { static constexpr TCHAR Format[] = TEXT("The hierarchy contains elements other than bones (for example '%s'). Modules only allow imported bones and user authored connectors."); *OutErrorMessage = FString::Printf(Format, *Key.ToString()); } return false; } if(Key.Type == ERigElementType::Bone) { if(Hierarchy->FindChecked(Key)->BoneType != ERigBoneType::Imported) { if(OutErrorMessage) { static constexpr TCHAR Format[] = TEXT("The hierarchy contains a user defined bone ('%s') - only imported bones are allowed."); *OutErrorMessage = FString::Printf(Format, *Key.ToString()); } return false; } } } } return true; } bool UControlRigBlueprint::TurnIntoControlRigModule(bool InAutoConvertHierarchy, FString* OutErrorMessage) { if(!CanTurnIntoControlRigModule(InAutoConvertHierarchy, OutErrorMessage)) { return false; } FScopedTransaction Transaction(LOCTEXT("TurnIntoControlRigModule", "Turn Rig into Module")); Modify(); RigModuleSettings.Identifier = FRigModuleIdentifier(); RigModuleSettings.Identifier.Name = GetName(); if(Hierarchy) { Hierarchy->Modify(); URigHierarchyController* Controller = Hierarchy->GetController(true); // create a copy of this hierarchy URigHierarchy* CopyOfHierarchy = NewObject(GetTransientPackage()); CopyOfHierarchy->CopyHierarchy(Hierarchy); // also create a hierarchy based on the preview mesh URigHierarchy* PreviewMeshHierarchy = NewObject(GetTransientPackage()); if(PreviewSkeletalMesh) { PreviewMeshHierarchy->GetController(true)->ImportBones(PreviewSkeletalMesh->GetSkeleton()); PreviewMeshHierarchy->GetController(true)->ImportSocketsFromSkeletalMesh(PreviewSkeletalMesh.Get(), NAME_None, false, false, false, false, false);; } // disable compilation { FRigVMBlueprintCompileScope CompileScope(this); // remove everything from the hierarchy Hierarchy->Reset(); const TArray AllKeys = CopyOfHierarchy->GetAllKeys(true); TArray KeysToSpawn; for(const FRigElementKey& Key : AllKeys) { if(Key.Type == ERigElementType::Curve) { continue; } if(Key.Type == ERigElementType::Bone) { if(PreviewMeshHierarchy->Contains(Key)) { continue; } } if(Key.Type == ERigElementType::Null) { // if this is a mesh socket based null if(PreviewMeshHierarchy->Contains(Key)) { continue; } } KeysToSpawn.Add(Key); } (void)ConvertHierarchyElementsToSpawnerNodes(CopyOfHierarchy, KeysToSpawn, false); if(Hierarchy->Num(ERigElementType::Connector) == 0) { static const FName RootName = TEXT("Root"); static const FString RootDescription = TEXT("This is the default temporary socket used for the root connection."); const FRigElementKey ConnectorKey = Controller->AddConnector(RootName); const FRigElementKey SocketKey = Controller->AddSocket(RootName, FRigElementKey(), FTransform::Identity, false, FRigSocketElement::SocketDefaultColor, RootDescription, false); (void)ResolveConnector(ConnectorKey, SocketKey); } } } OnRigTypeChangedDelegate.Broadcast(this); return true; } bool UControlRigBlueprint::CanTurnIntoStandaloneRig(FString* OutErrorMessage) const { return IsControlRigModule(); } bool UControlRigBlueprint::TurnIntoStandaloneRig(FString* OutErrorMessage) { if(!CanTurnIntoStandaloneRig(OutErrorMessage)) { return false; } FScopedTransaction Transaction(LOCTEXT("TurnIntoStandaloneRig", "Turn Module into Rig")); Modify(); RigModuleSettings = FRigModuleSettings(); if(Hierarchy) { Hierarchy->Modify(); Hierarchy->Reset(); if(PreviewSkeletalMesh) { Hierarchy->GetController(true)->ImportBones(PreviewSkeletalMesh->GetSkeleton()); Hierarchy->GetController(true)->ImportSocketsFromSkeletalMesh(PreviewSkeletalMesh.Get(), NAME_None, false, false, false, false, false); } } OnRigTypeChangedDelegate.Broadcast(this); return true; } TArray UControlRigBlueprint::ConvertHierarchyElementsToSpawnerNodes(URigHierarchy* InHierarchy, TArray InKeys, bool bRemoveElements) { TArray SpawnerNodes; // find the construction event const URigVMNode* EventNode = nullptr; for(const URigVMGraph* Graph : GetRigVMClient()->GetAllModels(false, false)) { for(const URigVMNode* Node : Graph->GetNodes()) { if(Node->IsEvent() && Node->GetEventName() == FRigUnit_PrepareForExecution::EventName) { EventNode = Node; break; } } if(EventNode) { break; } } FVector2D NodePosition = FVector2D::ZeroVector; const FVector2D NodePositionIncrement = FVector2D(400, 0); // if we didn't find the construction event yet, create it if(EventNode == nullptr) { const URigVMGraph* ConstructionGraph = GetRigVMClient()->AddModel(TEXT("ConstructionGraph"), true); URigVMController* GraphController = GetRigVMClient()->GetOrCreateController(ConstructionGraph); EventNode = GraphController->AddUnitNode(FRigUnit_PrepareForExecution::StaticStruct(), FRigUnit::GetMethodName(), NodePosition); NodePosition += NodePositionIncrement; } const URigVMPin* LastPin = EventNode->FindExecutePin(); if(LastPin) { // follow the node's execution links to find the last one bool bCarryOn = true; while(bCarryOn) { static const TArray ExecutePinPaths = { FRigVMStruct::ControlFlowCompletedName.ToString(), FRigVMStruct::ExecuteContextName.ToString() }; for(const FString& ExecutePinPath : ExecutePinPaths) { if(const URigVMPin* ExecutePin = LastPin->GetNode()->FindPin(ExecutePinPath)) { const TArray TargetPins = ExecutePin->GetLinkedTargetPins(); if(TargetPins.IsEmpty()) { bCarryOn = false; break; } LastPin = TargetPins[0]; NodePosition = LastPin->GetNode()->GetPosition() + NodePositionIncrement; } } } } const URigVMGraph* ConstructionGraph = EventNode->GetGraph(); URigVMController* GraphController = GetRigVMClient()->GetOrCreateController(ConstructionGraph); auto GetParentAndTransformDefaults = [InHierarchy](const FRigElementKey& InKey, FString& OutParentDefault, FString& OutTransformDefault) { const FRigElementKey Parent = InHierarchy->GetFirstParent(InKey); OutParentDefault.Reset(); FRigElementKey::StaticStruct()->ExportText(OutParentDefault, &Parent, nullptr, nullptr, PPF_None, nullptr); const FTransform Transform = InHierarchy->GetInitialLocalTransform(InKey); OutTransformDefault.Reset(); TBaseStructure::Get()->ExportText(OutTransformDefault, &Transform, nullptr, nullptr, PPF_None, nullptr); }; TMap ParentItemPinMap; auto AddParentItemLink = [GraphController, InHierarchy, &SpawnerNodes, &ParentItemPinMap] (const FRigElementKey& Key, URigVMNode* Node) { SpawnerNodes.Add(Node); ParentItemPinMap.Add(Key, Node->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Item))); if(const URigVMPin** SourcePin = ParentItemPinMap.Find(InHierarchy->GetFirstParent(Key))) { if(const URigVMPin* TargetPin = Node->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Parent))) { GraphController->AddLink((*SourcePin)->GetPinPath(), TargetPin->GetPinPath(), true); } } }; for(const FRigElementKey& Key : InKeys) { if(Key.Type == ERigElementType::Bone) { FString ParentDefault, TransformDefault; GetParentAndTransformDefaults(Key, ParentDefault, TransformDefault); URigVMNode* AddBoneNode = GraphController->AddUnitNode(FRigUnit_HierarchyAddBone::StaticStruct(), FRigUnit::GetMethodName(), NodePosition); NodePosition += NodePositionIncrement; AddParentItemLink(Key, AddBoneNode); if(LastPin) { if(const URigVMPin* NextPin = AddBoneNode->FindExecutePin()) { GraphController->AddLink(LastPin->GetPinPath(), NextPin->GetPinPath(), true); LastPin = NextPin; } } GraphController->SetPinDefaultValue(AddBoneNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Name))->GetPinPath(), Key.Name.ToString(), true, true); GraphController->SetPinDefaultValue(AddBoneNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Parent))->GetPinPath(), ParentDefault, true, true); GraphController->SetPinDefaultValue(AddBoneNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddBone, Space))->GetPinPath(), TEXT("LocalSpace"), true, true); GraphController->SetPinDefaultValue(AddBoneNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddBone, Transform))->GetPinPath(), TransformDefault, true, true); } else if(Key.Type == ERigElementType::Null) { FString ParentDefault, TransformDefault; GetParentAndTransformDefaults(Key, ParentDefault, TransformDefault); URigVMNode* AddNullNode = GraphController->AddUnitNode(FRigUnit_HierarchyAddNull::StaticStruct(), FRigUnit::GetMethodName(), NodePosition); NodePosition += NodePositionIncrement; AddParentItemLink(Key, AddNullNode); SpawnerNodes.Add(AddNullNode); if(LastPin) { if(const URigVMPin* NextPin = AddNullNode->FindExecutePin()) { GraphController->AddLink(LastPin->GetPinPath(), NextPin->GetPinPath(), true); LastPin = NextPin; } } GraphController->SetPinDefaultValue(AddNullNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Name))->GetPinPath(), Key.Name.ToString(), true, true); GraphController->SetPinDefaultValue(AddNullNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Parent))->GetPinPath(), ParentDefault, true, true); GraphController->SetPinDefaultValue(AddNullNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddNull, Space))->GetPinPath(), TEXT("LocalSpace"), true, true); GraphController->SetPinDefaultValue(AddNullNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddNull, Transform))->GetPinPath(), TransformDefault, true, true); } else if(Key.Type == ERigElementType::Control) { FRigControlElement* ControlElement = InHierarchy->FindChecked(Key); FString ParentDefault, TransformDefault; GetParentAndTransformDefaults(Key, ParentDefault, TransformDefault); const FTransform OffsetTransform = InHierarchy->GetControlOffsetTransform(ControlElement, ERigTransformType::InitialLocal); FString OffsetDefault; TBaseStructure::Get()->ExportText(OffsetDefault, &OffsetTransform, nullptr, nullptr, PPF_None, nullptr); if(ControlElement->Settings.AnimationType == ERigControlAnimationType::AnimationChannel) { UScriptStruct* UnitNodeStruct = nullptr; TRigVMTypeIndex TypeIndex = INDEX_NONE; FString InitialValue, MinimumValue, MaximumValue, SettingsValue; switch(ControlElement->Settings.ControlType) { case ERigControlType::Bool: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelBool::StaticStruct(); TypeIndex = RigVMTypeUtils::TypeIndex::Bool; InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); break; } case ERigControlType::Float: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelFloat::StaticStruct(); TypeIndex = RigVMTypeUtils::TypeIndex::Float; InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); if(ControlElement->Settings.LimitEnabled.Num() == 1) { FRigUnit_HierarchyAddAnimationChannelSingleLimitSettings Settings; Settings.Enabled = ControlElement->Settings.LimitEnabled[0]; FRigUnit_HierarchyAddAnimationChannelSingleLimitSettings::StaticStruct()->ExportText(SettingsValue, &Settings, &Settings, nullptr, PPF_None, nullptr); } break; } case ERigControlType::ScaleFloat: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelScaleFloat::StaticStruct(); TypeIndex = RigVMTypeUtils::TypeIndex::Float; InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); if(ControlElement->Settings.LimitEnabled.Num() == 1) { FRigUnit_HierarchyAddAnimationChannelSingleLimitSettings Settings; Settings.Enabled = ControlElement->Settings.LimitEnabled[0]; FRigUnit_HierarchyAddAnimationChannelSingleLimitSettings::StaticStruct()->ExportText(SettingsValue, &Settings, &Settings, nullptr, PPF_None, nullptr); } break; } case ERigControlType::Integer: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelInteger::StaticStruct(); TypeIndex = RigVMTypeUtils::TypeIndex::Int32; InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); if(ControlElement->Settings.LimitEnabled.Num() == 1) { FRigUnit_HierarchyAddAnimationChannelSingleLimitSettings Settings; Settings.Enabled = ControlElement->Settings.LimitEnabled[0]; FRigUnit_HierarchyAddAnimationChannelSingleLimitSettings::StaticStruct()->ExportText(SettingsValue, &Settings, &Settings, nullptr, PPF_None, nullptr); } break; } case ERigControlType::Vector2D: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelVector2D::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); if(ControlElement->Settings.LimitEnabled.Num() == 2) { FRigUnit_HierarchyAddAnimationChannel2DLimitSettings Settings; Settings.X = ControlElement->Settings.LimitEnabled[0]; Settings.Y = ControlElement->Settings.LimitEnabled[1]; FRigUnit_HierarchyAddAnimationChannel2DLimitSettings::StaticStruct()->ExportText(SettingsValue, &Settings, &Settings, nullptr, PPF_None, nullptr); } break; } case ERigControlType::Position: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelVector::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); if(ControlElement->Settings.LimitEnabled.Num() == 3) { FRigUnit_HierarchyAddAnimationChannelVectorLimitSettings Settings; Settings.X = ControlElement->Settings.LimitEnabled[0]; Settings.Y = ControlElement->Settings.LimitEnabled[1]; Settings.Z = ControlElement->Settings.LimitEnabled[2]; FRigUnit_HierarchyAddAnimationChannelVectorLimitSettings::StaticStruct()->ExportText(SettingsValue, &Settings, &Settings, nullptr, PPF_None, nullptr); } break; } case ERigControlType::Scale: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelScaleVector::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); if(ControlElement->Settings.LimitEnabled.Num() == 3) { FRigUnit_HierarchyAddAnimationChannelVectorLimitSettings Settings; Settings.X = ControlElement->Settings.LimitEnabled[0]; Settings.Y = ControlElement->Settings.LimitEnabled[1]; Settings.Z = ControlElement->Settings.LimitEnabled[2]; FRigUnit_HierarchyAddAnimationChannelVectorLimitSettings::StaticStruct()->ExportText(SettingsValue, &Settings, &Settings, nullptr, PPF_None, nullptr); } break; } case ERigControlType::Rotator: { UnitNodeStruct = FRigUnit_HierarchyAddAnimationChannelRotator::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); MinimumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Minimum).ToString(); MaximumValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Maximum).ToString(); if(ControlElement->Settings.LimitEnabled.Num() == 3) { FRigUnit_HierarchyAddAnimationChannelRotatorLimitSettings Settings; Settings.Pitch = ControlElement->Settings.LimitEnabled[0]; Settings.Yaw = ControlElement->Settings.LimitEnabled[1]; Settings.Roll = ControlElement->Settings.LimitEnabled[2]; FRigUnit_HierarchyAddAnimationChannelRotatorLimitSettings::StaticStruct()->ExportText(SettingsValue, &Settings, &Settings, nullptr, PPF_None, nullptr); } break; } default: { break; } } if(UnitNodeStruct == nullptr) { continue; } URigVMNode* AddControlNode = GraphController->AddUnitNode(UnitNodeStruct, FRigUnit::GetMethodName(), NodePosition); NodePosition += NodePositionIncrement; AddParentItemLink(Key, AddControlNode); if(LastPin) { if(const URigVMPin* NextPin = AddControlNode->FindExecutePin()) { GraphController->AddLink(LastPin->GetPinPath(), NextPin->GetPinPath(), true); LastPin = NextPin; } } GraphController->ResolveWildCardPin(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddAnimationChannelFloat, InitialValue))->GetPinPath(), TypeIndex, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddAnimationChannelFloat, Name))->GetPinPath(), Key.Name.ToString(), true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddAnimationChannelFloat, Parent))->GetPinPath(), ParentDefault, true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddAnimationChannelFloat, InitialValue))->GetPinPath(), InitialValue, true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddAnimationChannelFloat, MinimumValue))->GetPinPath(), MinimumValue, true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddAnimationChannelFloat, MaximumValue))->GetPinPath(), MaximumValue, true, true); if(!SettingsValue.IsEmpty()) { GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddAnimationChannelFloat, LimitsEnabled))->GetPinPath(), SettingsValue, true, true); } } else { UScriptStruct* UnitNodeStruct = nullptr; TRigVMTypeIndex TypeIndex = INDEX_NONE; FString InitialValue; switch(ControlElement->Settings.ControlType) { case ERigControlType::Float: case ERigControlType::ScaleFloat: { UnitNodeStruct = FRigUnit_HierarchyAddControlFloat::StaticStruct(); TypeIndex = RigVMTypeUtils::TypeIndex::Float; InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); break; } case ERigControlType::Integer: { UnitNodeStruct = FRigUnit_HierarchyAddControlInteger::StaticStruct(); TypeIndex = RigVMTypeUtils::TypeIndex::Int32; InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); break; } case ERigControlType::Vector2D: { UnitNodeStruct = FRigUnit_HierarchyAddControlVector2D::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); break; } case ERigControlType::Position: case ERigControlType::Scale: { UnitNodeStruct = FRigUnit_HierarchyAddControlVector::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); break; } case ERigControlType::Rotator: { UnitNodeStruct = FRigUnit_HierarchyAddControlRotator::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); InitialValue = InHierarchy->GetControlValue(Key, ERigControlValueType::Initial).ToString(); break; } case ERigControlType::Transform: case ERigControlType::TransformNoScale: case ERigControlType::EulerTransform: { UnitNodeStruct = FRigUnit_HierarchyAddControlTransform::StaticStruct(); TypeIndex = FRigVMRegistry::Get().GetTypeIndex(); const FTransform InitialTransform = InHierarchy->GetInitialLocalTransform(Key); TBaseStructure::Get()->ExportText(InitialValue, &InitialTransform, nullptr, nullptr, PPF_None, nullptr); break; } default: { break; } } if(UnitNodeStruct == nullptr) { continue; } URigVMNode* AddControlNode = GraphController->AddUnitNode(UnitNodeStruct, FRigUnit::GetMethodName(), NodePosition); NodePosition += NodePositionIncrement; AddParentItemLink(Key, AddControlNode); if(LastPin) { if(const URigVMPin* NextPin = AddControlNode->FindExecutePin()) { GraphController->AddLink(LastPin->GetPinPath(), NextPin->GetPinPath(), true); LastPin = NextPin; } } GraphController->ResolveWildCardPin(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddControlInteger, InitialValue))->GetPinPath(), TypeIndex, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Name))->GetPinPath(), Key.Name.ToString(), true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Parent))->GetPinPath(), ParentDefault, true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddControlElement, OffsetSpace))->GetPinPath(), TEXT("LocalSpace"), true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddControlElement, OffsetTransform))->GetPinPath(), OffsetDefault, true, true); GraphController->SetPinDefaultValue(AddControlNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddControlInteger, InitialValue))->GetPinPath(), InitialValue, true, true); if(const FStructProperty* SettingsProperty = CastField(UnitNodeStruct->FindPropertyByName(TEXT("Settings")))) { UScriptStruct* SettingsStruct = CastChecked(SettingsProperty->Struct); FStructOnScope SettingsScope(SettingsStruct); FRigUnit_HierarchyAddControl_Settings* Settings = (FRigUnit_HierarchyAddControl_Settings*)SettingsScope.GetStructMemory(); Settings->ConfigureFrom(ControlElement, ControlElement->Settings); FString SettingsDefault; SettingsStruct->ExportText(SettingsDefault, Settings, nullptr, nullptr, PPF_None, nullptr); GraphController->SetPinDefaultValue(AddControlNode->FindPin(SettingsProperty->GetName())->GetPinPath(), SettingsDefault, true, true); } } } else if(Key.Type == ERigElementType::Socket) { FString ParentDefault, TransformDefault; GetParentAndTransformDefaults(Key, ParentDefault, TransformDefault); URigVMNode* AddSocketNode = GraphController->AddUnitNode(FRigUnit_HierarchyAddSocket::StaticStruct(), FRigUnit::GetMethodName(), NodePosition); NodePosition += NodePositionIncrement; AddParentItemLink(Key, AddSocketNode); SpawnerNodes.Add(AddSocketNode); if(LastPin) { if(const URigVMPin* NextPin = AddSocketNode->FindExecutePin()) { GraphController->AddLink(LastPin->GetPinPath(), NextPin->GetPinPath(), true); LastPin = NextPin; } } GraphController->SetPinDefaultValue(AddSocketNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Name))->GetPinPath(), Key.Name.ToString(), true, true); GraphController->SetPinDefaultValue(AddSocketNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddElement, Parent))->GetPinPath(), ParentDefault, true, true); GraphController->SetPinDefaultValue(AddSocketNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddNull, Space))->GetPinPath(), TEXT("LocalSpace"), true, true); GraphController->SetPinDefaultValue(AddSocketNode->FindPin(GET_MEMBER_NAME_STRING_CHECKED(FRigUnit_HierarchyAddNull, Transform))->GetPinPath(), TransformDefault, true, true); } } if(bRemoveElements && InHierarchy) { InHierarchy->Modify(); for(const FRigElementKey& Key : InKeys) { InHierarchy->GetController(true)->RemoveElement(Key, true); } } return SpawnerNodes; } #endif // WITH_EDITORONLY_DATA UTexture2D* UControlRigBlueprint::GetRigModuleIcon() const { if(IsControlRigModule()) { if(UTexture2D* Icon = Cast(RigModuleSettings.Icon.TryLoad())) { return Icon; } } return nullptr; } void UControlRigBlueprint::SetPreviewMesh(USkeletalMesh* PreviewMesh, bool bMarkAsDirty/*=true*/) { #if WITH_EDITORONLY_DATA if(bMarkAsDirty) { Modify(); } PreviewSkeletalMesh = PreviewMesh; if(IsControlRigModule()) { SourceHierarchyImport.Reset(); SourceCurveImport.Reset(); } #endif } void UControlRigBlueprint::Serialize(FArchive& Ar) { UE_RIGVM_ARCHIVETRACE_SCOPE(Ar, FString::Printf(TEXT("UControlRigBlueprint(%s)"), *GetName())); if(IsValidChecked(this)) { RigVMClient.SetOuterClientHost(this, GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, RigVMClient)); ModularRigModel.SetOuterClientHost(this); } Super::Serialize(Ar); UE_RIGVM_ARCHIVETRACE_ENTRY(Ar, TEXT("Super::Serialize")); 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 ReferencedFunctionHosts = GetReferencedFunctionHosts(false); for(IRigVMGraphFunctionHost* ReferencedFunctionHost : ReferencedFunctionHosts) { if (URigVMBlueprintGeneratedClass* BPGeneratedClass = Cast(ReferencedFunctionHost)) { Ar << BPGeneratedClass; } } for(const TSoftObjectPtr& ShapeLibraryPtr : ShapeLibraries) { if(ShapeLibraryPtr.IsValid()) { UControlRigShapeLibrary* ShapeLibrary = ShapeLibraryPtr.Get(); Ar << ShapeLibrary; } } } } if(Ar.IsLoading()) { if(Model_DEPRECATED || FunctionLibrary_DEPRECATED) { TGuardValue DisableClientNotifs(RigVMClient.bSuspendNotifications, true); RigVMClient.SetFromDeprecatedData(Model_DEPRECATED, FunctionLibrary_DEPRECATED); } ModularRigModel.UpdateCachedChildren(); ModularRigModel.Connections.UpdateFromConnectionList(); } } void UControlRigBlueprint::PreSave(FObjectPreSaveContext ObjectSaveContext) { Super::PreSave(ObjectSaveContext); // make sure to save the VM with high performance settings // so that during cooking we reach small footprints. // these settings may have changed during the user session. VMCompileSettings.ASTSettings.bFoldAssignments = true; VMCompileSettings.ASTSettings.bFoldLiterals = true; bExposesAnimatableControls = false; Hierarchy->ForEach([this](FRigControlElement* ControlElement) -> bool { if (Hierarchy->IsAnimatable(ControlElement)) { bExposesAnimatableControls = true; return false; } return true; }); if(IsControlRigModule()) { URigHierarchy* DebuggedHierarchy = Hierarchy; if(UControlRig* DebuggedRig = Cast(GetObjectBeingDebugged())) { DebuggedHierarchy = DebuggedRig->GetHierarchy(); } TGuardValue SuspendNotifGuard(Hierarchy->GetSuspendNotificationsFlag(), true); TGuardValue SuspendNotifGuardOnDebuggedHierarchy(DebuggedHierarchy->GetSuspendNotificationsFlag(), true); UpdateExposedModuleConnectors(); SourceHierarchyImport.Reset(); SourceCurveImport.Reset(); } if (IsControlRigModule()) { ControlRigType = EControlRigType::RigModule; ItemTypeDisplayName = TEXT("Rig Module"); CustomThumbnail = RigModuleSettings.Icon.ToString(); } else if (GetControlRigClass()->IsChildOf(UModularRig::StaticClass())) { ControlRigType = EControlRigType::ModularRig; ItemTypeDisplayName = TEXT("Modular Rig"); } else { ControlRigType = EControlRigType::IndependentRig; ItemTypeDisplayName = TEXT("Control Rig"); } if (IsModularRig()) { ModuleReferenceData = GetModuleReferenceData(); IAssetRegistry::GetChecked().AssetTagsFinalized(*this); } } TArray UControlRigBlueprint::FindReferencesToModule() const { TArray Result; if (!IsControlRigModule()) { return Result; } const UClass* RigModuleClass = GetControlRigClass(); if (!RigModuleClass) { return Result; } // Load the asset registry module const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); // Collect a full list of assets with the control rig class TArray AssetDataList; AssetRegistryModule.Get().GetAssetsByClass(UControlRigBlueprint::StaticClass()->GetClassPathName(), AssetDataList, true); static const FLazyName ModuleReferenceDataName(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, ModuleReferenceData)); FArrayProperty* ModuleReferenceDataProperty = CastField(UControlRigBlueprint::StaticClass()->FindPropertyByName(ModuleReferenceDataName)); for(const FAssetData& AssetData : AssetDataList) { // Check only modular rigs if (UControlRigBlueprint::GetRigType(AssetData) != EControlRigType::ModularRig) { continue; } const FString ModularRigDataString = AssetData.GetTagValueRef(ModuleReferenceDataName); if (ModularRigDataString.IsEmpty()) { continue; } TArray Modules; ModuleReferenceDataProperty->ImportText_Direct(*ModularRigDataString, &Modules, nullptr, EPropertyPortFlags::PPF_None); for (FModuleReferenceData& Module : Modules) { if (Module.ReferencedModule == RigModuleClass) { Result.Add(Module); } } } return Result; } EControlRigType UControlRigBlueprint::GetRigType(const FAssetData& InAsset) { EControlRigType Result = EControlRigType::MAX; static const FLazyName ControlRigTypeName(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, ControlRigType)); FProperty* ControlRigTypeProperty = CastField(UControlRigBlueprint::StaticClass()->FindPropertyByName(ControlRigTypeName)); const FString ControlRigTypeString = InAsset.GetTagValueRef(ControlRigTypeName); if (ControlRigTypeString.IsEmpty()) { return Result; } EControlRigType RigType; ControlRigTypeProperty->ImportText_Direct(*ControlRigTypeString, &RigType, nullptr, EPropertyPortFlags::PPF_None); return RigType; } TArray UControlRigBlueprint::GetReferencesToRigModule(const FAssetData& InModuleAsset) { TArray Result; FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); IAssetRegistry& AssetRegistry = AssetRegistryModule.GetRegistry(); TArray PackageDependencies; AssetRegistry.GetReferencers(InModuleAsset.PackageName, PackageDependencies); for (FName& DependencyPath : PackageDependencies) { TArray Assets; AssetRegistry.GetAssetsByPackageName(DependencyPath, Assets); for (const FAssetData& DependencyData : Assets) { if (DependencyData.IsAssetLoaded()) { if (UControlRigBlueprint* Blueprint = Cast(DependencyData.GetAsset())) { if (Blueprint->IsModularRig()) { TArray Modules = Blueprint->ModularRigModel.FindModuleInstancesOfClass(InModuleAsset); for (const FRigModuleReference* Module : Modules) { FSoftObjectPath ModulePath = DependencyData.GetSoftObjectPath(); ModulePath.SetSubPathString(Module->GetModulePath().GetPath()); Result.Add(ModulePath); } } } } else { // Check only modular rigs if (UControlRigBlueprint::GetRigType(DependencyData) != EControlRigType::ModularRig) { continue; } static const FLazyName ModuleReferenceDataName(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, ModuleReferenceData)); FArrayProperty* ModuleReferenceDataProperty = CastField(UControlRigBlueprint::StaticClass()->FindPropertyByName(ModuleReferenceDataName)); const FString ModularRigDataString = DependencyData.GetTagValueRef(ModuleReferenceDataName); if (ModularRigDataString.IsEmpty()) { continue; } TArray Modules; ModuleReferenceDataProperty->ImportText_Direct(*ModularRigDataString, &Modules, nullptr, EPropertyPortFlags::PPF_None); for (const FModuleReferenceData& Module : Modules) { FTopLevelAssetPath ModulePath = Module.ReferencedModule.GetAssetPath(); FString AssetName = ModulePath.GetAssetName().ToString(); AssetName.RemoveFromEnd(TEXT("_C")); ModulePath = FTopLevelAssetPath(ModulePath.GetPackageName(), *AssetName); if (ModulePath == InModuleAsset.GetSoftObjectPath().GetAssetPath()) { FSoftObjectPath ResultModulePath = DependencyData.GetSoftObjectPath(); ResultModulePath.SetSubPathString(Module.ModulePath); Result.Add(ResultModulePath); } } } } } return Result; } TArray UControlRigBlueprint::GetModuleReferenceData() const { TArray Result; Result.Reserve(ModularRigModel.Modules.Num()); ModularRigModel.ForEachModule([&Result](const FRigModuleReference* Module) -> bool { Result.Add(Module); return true; }); return Result; } void UControlRigBlueprint::UpdateExposedModuleConnectors() const { UControlRigBlueprint* MutableThis = ((UControlRigBlueprint*)this); MutableThis->RigModuleSettings.ExposedConnectors.Reset(); Hierarchy->ForEach([MutableThis, this](const FRigConnectorElement* ConnectorElement) -> bool { const FRigElementKey ConnectorKey = ConnectorElement->GetKey().ConvertToModuleNameFormat(&ModularRigModel.PreviousModulePaths); FRigModuleConnector ExposedConnector; ExposedConnector.Name = ConnectorKey.Name.ToString(); ExposedConnector.Settings = ConnectorElement->Settings; MutableThis->RigModuleSettings.ExposedConnectors.Add(ExposedConnector); return true; }); PropagateHierarchyFromBPToInstances(); } #if WITH_EDITOR TArray UControlRigBlueprint::GetOverrideSubjects() const { TArray Subjects; if(const UModularRig* DebuggedRig = Cast(GetObjectBeingDebugged())) { ModularRigModel.ForEachModule([&Subjects, DebuggedRig](const FRigModuleReference* ModuleReference) -> bool { if(const FRigModuleInstance* ModuleInstance = DebuggedRig->FindModule(ModuleReference->Name)) { if(const UControlRig* ModuleRig = ModuleInstance->GetRig()) { for(const FControlRigOverrideValue& Override : ModuleReference->ConfigOverrides) { Subjects.Add({ModuleRig, Override.ToPropertyPath()}); } } } return true; }); } return Subjects; } uint32 UControlRigBlueprint::GetOverrideSubjectsHash() const { uint32 Hash = 0; ModularRigModel.ForEachModule([&Hash](const FRigModuleReference* ModuleReference) -> bool { Hash = HashCombine(Hash, GetTypeHash(ModuleReference->Name)); Hash = HashCombine(Hash, GetTypeHash(ModuleReference->ConfigOverrides)); return true; }); return Hash; } #endif bool UControlRigBlueprint::ResolveConnector(const FRigElementKey& DraggedKey, const FRigElementKey& TargetKey, bool bSetupUndoRedo) { return ResolveConnectorToArray(DraggedKey, {TargetKey}, bSetupUndoRedo); } bool UControlRigBlueprint::ResolveConnectorToArray(const FRigElementKey& DraggedKey, const TArray& TargetKeys, bool bSetupUndoRedo) { FScopedTransaction Transaction(LOCTEXT("ResolveConnector", "Resolve connector")); if(bSetupUndoRedo) { Modify(); } TArray FilteredKeys = TargetKeys; FilteredKeys.RemoveAll([](const FRigElementKey& Key) { return !Key.IsValid(); }); if(!FilteredKeys.IsEmpty()) { FRigElementKeyCollection& ExistingTargetKeys = ArrayConnectionMap.FindOrAdd(DraggedKey); if(ExistingTargetKeys.Num() == FilteredKeys.Num()) { bool bCompleteMatch = true; for(int32 Index = 0; Index < ExistingTargetKeys.Num(); Index++) { if(ExistingTargetKeys[Index] != FilteredKeys[Index]) { bCompleteMatch = false; break; } } if(bCompleteMatch) { return false; } } ExistingTargetKeys.Keys = FilteredKeys; if (IsModularRig()) { // Add connection to the model if (UModularRigController* Controller = GetModularRigController()) { Controller->ConnectConnectorToElements(DraggedKey, FilteredKeys, bSetupUndoRedo, ModularRigSettings.bAutoResolve); } } else { ArrayConnectionMap.FindOrAdd(DraggedKey) = ExistingTargetKeys; } } else { if (IsModularRig()) { // Add connection to the model if (UModularRigController* Controller = GetModularRigController()) { Controller->DisconnectConnector(DraggedKey, false, bSetupUndoRedo); } } else { ArrayConnectionMap.Remove(DraggedKey); } } RecompileModularRig(); PropagateHierarchyFromBPToInstances(); if(UControlRig* ControlRig = Cast(GetObjectBeingDebugged())) { for (UEdGraph* Graph : UbergraphPages) { UControlRigGraph* RigGraph = Cast(Graph); if (RigGraph == nullptr) { continue; } RigGraph->CacheNameLists(ControlRig->GetHierarchy(), &DrawContainer, ShapeLibraries); } } return true; } void UControlRigBlueprint::UpdateConnectionMapFromModel() { if (IsModularRig()) { ArrayConnectionMap.Reset(); for (const FModularRigSingleConnection& Connection : ModularRigModel.Connections) { ArrayConnectionMap.Add(Connection.Connector, {Connection.Targets}); } } } 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::Identity); } else if (Control.ControlType == ERigControlType::TransformNoScale) { Control.InitialValue = FRigControlValue::Make(FTransformNoScale::Identity); } else if (Control.ControlType == ERigControlType::EulerTransform) { Control.InitialValue = FRigControlValue::Make(FEulerTransform::Identity); } } } // convert the hierarchy from V1 to V2 if (GetLinkerCustomVersion(FControlRigObjectVersion::GUID) < FControlRigObjectVersion::RigHierarchyV2) { Modify(); TGuardValue SuspendNotifGuard(Hierarchy->GetSuspendNotificationsFlag(), true); Hierarchy->Reset(); GetHierarchyController()->ImportFromHierarchyContainer(HierarchyContainer_DEPRECATED, false); } // perform backwards compat value upgrades TArray GraphsToValidate = GetAllModels(); for (int32 GraphIndex = 0; GraphIndex < GraphsToValidate.Num(); GraphIndex++) { URigVMGraph* GraphToValidate = GraphsToValidate[GraphIndex]; if(GraphToValidate == nullptr) { continue; } for(URigVMNode* Node : GraphToValidate->GetNodes()) { TArray Pins = Node->GetAllPinsRecursively(); for(URigVMPin* Pin : Pins) { if(Pin->GetCPPTypeObject() == StaticEnum()) { if(Pin->GetDefaultValue() == TEXT("Space")) { if(URigVMController* Controller = GetController(GraphToValidate)) { FRigVMControllerNotifGuard NotifGuard(Controller, true); FRigVMDefaultValueTypeGuard _(Controller, ERigVMPinDefaultValueType::Override); 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(RigClass->GetDefaultObject(false /* create if needed */)); TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); ArchetypeInstances.Insert(CDO, 0); for (UObject* Instance : ArchetypeInstances) { if (UControlRig* InstanceRig = Cast(Instance)) { InstanceRig->ShapeLibraries.Reset(); InstanceRig->GizmoLibrary_DEPRECATED.Reset(); } } } if(ArrayConnectionMap.IsEmpty() && !ConnectionMap_DEPRECATED.IsEmpty()) { for(const TPair& Pair : ConnectionMap_DEPRECATED) { ArrayConnectionMap.Add(Pair.Key, FRigElementKeyCollection({Pair.Value})); } } ModularRigModel.PatchModelsOnLoad(); #if WITH_EDITOR if(IsControlRigModule() && Hierarchy) { // backwards compat - makes sure to only ever allow one primary connector TArray Connectors = Hierarchy->GetConnectors(); const int32 NumPrimaryConnectors = Algo::CountIf(Connectors, [](const FRigConnectorElement* InConnector) -> bool { return InConnector->IsPrimary(); }); if(NumPrimaryConnectors > 1) { bool bHasSeenPrimary = false; for(FRigConnectorElement* Connector : Connectors) { if(bHasSeenPrimary) { Connector->Settings.Type = EConnectorType::Secondary; } else { bHasSeenPrimary = Connector->IsPrimary(); } } UpdateExposedModuleConnectors(); } } #endif // patch from previously used module paths to unique module names TMap PreviousArrayConnectionMap; Swap(PreviousArrayConnectionMap, ArrayConnectionMap); ArrayConnectionMap.Reset(); for(TPair& Connection : PreviousArrayConnectionMap) { FRigElementKey Key = Connection.Key.ConvertToModuleNameFormat(&ModularRigModel.PreviousModulePaths); FRigElementKeyCollection& Targets = Connection.Value; for(FRigElementKey& TargetKey : Targets.Keys) { TargetKey.ConvertToModuleNameFormatInline(&ModularRigModel.PreviousModulePaths); } ArrayConnectionMap.Add(Key, Targets); } UpdateModularDependencyDelegates(); if(Hierarchy) { Hierarchy->PatchElementMetadata(ModularRigModel.PreviousModulePaths); Hierarchy->PatchModularRigComponentKeys(ModularRigModel.PreviousModulePaths); } } #if WITH_EDITOR void UControlRigBlueprint::HandlePackageDone() { if (ShapeLibrariesToLoadOnPackageLoaded.Num() > 0) { for(const FString& ShapeLibraryToLoadOnPackageLoaded : ShapeLibrariesToLoadOnPackageLoaded) { ShapeLibraries.Add(LoadObject(nullptr, *ShapeLibraryToLoadOnPackageLoaded)); } URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass(); UControlRig* CDO = Cast(RigClass->GetDefaultObject(false /* create if needed */)); TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); ArchetypeInstances.Insert(CDO, 0); for (UObject* Instance : ArchetypeInstances) { if (UControlRig* InstanceRig = Cast(Instance)) { InstanceRig->ShapeLibraries = ShapeLibraries; } } ShapeLibrariesToLoadOnPackageLoaded.Reset(); } PropagateHierarchyFromBPToInstances(); Super::HandlePackageDone(); if(IsModularRig()) { // force load all dependencies ModularRigModel.ForEachModule([](const FRigModuleReference* Element) -> bool { (void)Element->Class.LoadSynchronous(); const_cast(Element)->PatchModelsOnLoad(); return true; }); RecompileModularRig(); } } void UControlRigBlueprint::HandleConfigureRigVMController(const FRigVMClient* InClient, URigVMController* InControllerToConfigure) { Super::HandleConfigureRigVMController(InClient, InControllerToConfigure); TWeakObjectPtr WeakThis(this); InControllerToConfigure->ConfigureWorkflowOptionsDelegate.BindLambda([WeakThis](URigVMUserWorkflowOptions* Options) { if(UControlRigWorkflowOptions* ControlRigNodeWorkflowOptions = Cast(Options)) { ControlRigNodeWorkflowOptions->Hierarchy = nullptr; ControlRigNodeWorkflowOptions->Selection.Reset(); if(const URigVMBlueprint* StrongThis = WeakThis.Get()) { if(UControlRig* ControlRig = Cast(StrongThis->GetObjectBeingDebugged())) { ControlRigNodeWorkflowOptions->Hierarchy = ControlRig->GetHierarchy(); ControlRigNodeWorkflowOptions->Selection = ControlRig->GetHierarchy()->GetSelectedKeys(); } } } }); } #endif void UControlRigBlueprint::UpdateConnectionMapAfterRename(const FString& InOldModuleName) { const FString OldModuleName = InOldModuleName + FRigHierarchyModulePath::ModuleNameSuffix; const FString NewModuleName = RigModuleSettings.Identifier.Name + FRigHierarchyModulePath::ModuleNameSuffix; TMap FixedConnectionMap; for(const TPair& Pair : ArrayConnectionMap) { auto FixUpConnectionMap = [OldModuleName, NewModuleName](const FRigElementKey& InKey) -> FRigElementKey { const FString NameString = InKey.Name.ToString(); if(NameString.StartsWith(OldModuleName, ESearchCase::CaseSensitive)) { return FRigElementKey(*(NewModuleName + NameString.Mid(OldModuleName.Len())), InKey.Type); } return InKey; }; const FRigElementKey Key = FixUpConnectionMap(Pair.Key); FRigElementKeyCollection Values; for(const FRigElementKey& OldValue : Pair.Value) { Values.Keys.Add(FixUpConnectionMap(OldValue)); } FixedConnectionMap.FindOrAdd(Key) = Values; } Swap(ArrayConnectionMap, FixedConnectionMap); } 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(); } void UControlRigBlueprint::GetPreloadDependencies(TArray& OutDeps) { Super::GetPreloadDependencies(OutDeps); for (FRigModuleReference& Module : ModularRigModel.Modules) { OutDeps.Add(Module.Class.Get()); } } #if WITH_EDITOR const FLazyName& UControlRigBlueprint::GetPanelPinFactoryName() const { return ControlRigPanelNodeFactoryName; } IRigVMEditorModule* UControlRigBlueprint::GetEditorModule() const { return &IControlRigEditorModule::Get(); } #endif TArray UControlRigBlueprint::GeneratePythonCommands(const FString InNewBlueprintName) { TArray 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 PropertiesChanged = TransactionEvent.GetChangedProperties(); int32 TransactionIndex = GEditor->Trans->FindTransactionIndex(TransactionEvent.GetTransactionId()); const FTransaction* Transaction = GEditor->Trans->GetTransaction(TransactionIndex); if (Transaction && Transaction->ContainsObject(Hierarchy)) { 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(Graph); if (RigGraph == nullptr) { continue; } RigGraph->CacheNameLists(Hierarchy, &DrawContainer, ShapeLibraries); } RequestAutoVMRecompilation(); (void)MarkPackageDirty(); } if (PropertiesChanged.Contains(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, ModularRigModel))) { if (IsModularRig()) { ModularRigModel.UpdateCachedChildren(); ModularRigModel.Connections.UpdateFromConnectionList(); RecompileModularRig(); } } if (PropertiesChanged.Contains(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, DrawContainer))) { PropagateDrawInstructionsFromBPToInstances(); } if (PropertiesChanged.Contains(GET_MEMBER_NAME_CHECKED(UControlRigBlueprint, ArrayConnectionMap))) { PropagateHierarchyFromBPToInstances(); } } } void UControlRigBlueprint::PostDuplicate(bool bDuplicateForPIE) { Super::PostDuplicate(bDuplicateForPIE); if (URigHierarchyController* Controller = Hierarchy->GetController(true)) { Controller->OnModified().RemoveAll(this); Controller->OnModified().AddUObject(this, &UControlRigBlueprint::HandleHierarchyModified); } if (UModularRigController* ModularController = ModularRigModel.GetController()) { ModularController->OnModified().RemoveAll(this); ModularController->OnModified().AddUObject(this, &UControlRigBlueprint::HandleRigModulesModified); } // update the rig module identifier after save-as or duplicate asset if(IsControlRigModule()) { const FString OldNameSpace = RigModuleSettings.Identifier.Name; RigModuleSettings.Identifier.Name = URigHierarchy::GetSanitizedName(FRigName(GetName())).ToString(); UpdateConnectionMapAfterRename(OldNameSpace); } ModularRigModel.UpdateCachedChildren(); ModularRigModel.Connections.UpdateFromConnectionList(); } void UControlRigBlueprint::PostRename(UObject* OldOuter, const FName OldName) { Super::PostRename(OldOuter, OldName); // update the rig module identifier after renaming the asset if(IsControlRigModule()) { const FString OldNameSpace = RigModuleSettings.Identifier.Name; RigModuleSettings.Identifier.Name = URigHierarchy::GetSanitizedName(FRigName(GetName())).ToString(); UpdateConnectionMapAfterRename(OldNameSpace); } } TArray UControlRigBlueprint::GetCurrentlyOpenRigBlueprints() { return sCurrentlyOpenedRigBlueprints; } #if WITH_EDITOR const FControlRigShapeDefinition* UControlRigBlueprint::GetControlShapeByName(const FName& InName) const { TMap LibraryNameMap; if(UControlRig* ControlRig = Cast(GetObjectBeingDebugged())) { LibraryNameMap = ControlRig->ShapeLibraryNameMap; } return UControlRigShapeLibrary::GetShapeByName(InName, ShapeLibraries, LibraryNameMap); } FName UControlRigBlueprint::AddTransientControl(const URigVMUnitNode* InNode, const FRigDirectManipulationTarget& InTarget) { TUniquePtr ValueScope; if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls { ValueScope = MakeUnique(this); } // for now we only allow one pin control at the same time ClearTransientControls(); URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass(); UControlRig* CDO = Cast(RigClass->GetDefaultObject(true /* create if needed */)); FName ReturnName = NAME_None; TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { UControlRig* InstancedControlRig = Cast(ArchetypeInstance); if (InstancedControlRig) { FName ControlName = InstancedControlRig->AddTransientControl(InNode, InTarget); if (ReturnName == NAME_None) { ReturnName = ControlName; } } } return ReturnName; } FName UControlRigBlueprint::RemoveTransientControl(const URigVMUnitNode* InNode, const FRigDirectManipulationTarget& InTarget) { TUniquePtr ValueScope; if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls { ValueScope = MakeUnique(this); } URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass(); UControlRig* CDO = Cast(RigClass->GetDefaultObject(true /* create if needed */)); FName RemovedName = NAME_None; TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { UControlRig* InstancedControlRig = Cast(ArchetypeInstance); if (InstancedControlRig) { FName Name = InstancedControlRig->RemoveTransientControl(InNode, InTarget); if (RemovedName == NAME_None) { RemovedName = Name; } } } return RemovedName; } FName UControlRigBlueprint::AddTransientControl(const FRigElementKey& InElement) { TUniquePtr ValueScope; if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls { ValueScope = MakeUnique(this); } URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass(); UControlRig* CDO = Cast(RigClass->GetDefaultObject(true /* create if needed */)); FName ReturnName = NAME_None; TArray 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 SavedElementLocalTransforms; for (UObject* ArchetypeInstance : ArchetypeInstances) { UControlRig* InstancedControlRig = Cast(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(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 ValueScope; if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls { ValueScope = MakeUnique(this); } URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass(); UControlRig* CDO = Cast(RigClass->GetDefaultObject(true /* create if needed */)); FName RemovedName = NAME_None; TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { UControlRig* InstancedControlRig = Cast(ArchetypeInstance); if (InstancedControlRig) { FName Name = InstancedControlRig->RemoveTransientControl(InElement); if (RemovedName == NAME_None) { RemovedName = Name; } } } return RemovedName; } void UControlRigBlueprint::ClearTransientControls() { bool bHasAnyTransientControls = false; if (URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass()) { UControlRig* CDO = Cast(RigClass->GetDefaultObject(true /* create if needed */)); TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { UControlRig* InstancedControlRig = Cast(ArchetypeInstance); if (InstancedControlRig) { if(!InstancedControlRig->GetHierarchy()->GetTransientControls().IsEmpty()) { bHasAnyTransientControls = true; break; } } } } if(!bHasAnyTransientControls) { return; } TUniquePtr ValueScope; if (!UControlRigEditorSettings::Get()->bResetControlsOnPinValueInteraction) // if we need to retain the controls { ValueScope = MakeUnique(this); } if (URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass()) { UControlRig* CDO = Cast(RigClass->GetDefaultObject(true /* create if needed */)); TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { UControlRig* InstancedControlRig = Cast(ArchetypeInstance); if (InstancedControlRig) { InstancedControlRig->ClearTransientControls(); } } } } UModularRigController* UControlRigBlueprint::GetModularRigController() { if (!GetControlRigClass()->IsChildOf(UModularRig::StaticClass())) { return nullptr; } return ModularRigModel.GetController(); } void UControlRigBlueprint::RecompileModularRig() { RefreshModuleConnectors(); OnModularRigPreCompiled().Broadcast(this); if (const UClass* MyControlRigClass = GeneratedClass) { if (UModularRig* DefaultObject = Cast(MyControlRigClass->GetDefaultObject(false))) { PropagateModuleHierarchyFromBPToInstances(); RequestConstructionOnAllModules(); } } UpdateModularDependencyDelegates(); #if WITH_EDITOR if(GetObjectBeingDebugged() == nullptr) { SetObjectBeingDebugged(CreateControlRig()); } #endif OnModularRigCompiled().Broadcast(this); } #endif void UControlRigBlueprint::SetupDefaultObjectDuringCompilation(URigVMHost* InCDO) { Super::SetupDefaultObjectDuringCompilation(InCDO); CastChecked(InCDO)->GetHierarchy()->CopyHierarchy(Hierarchy); } void UControlRigBlueprint::SetupPinRedirectorsForBackwardsCompatibility() { for(URigVMGraph* Model : RigVMClient) { for (URigVMNode* Node : Model->GetNodes()) { if (URigVMUnitNode* UnitNode = Cast(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 DisablePinDefaultValueValidation(Controller->bValidatePinDefaults, false); FRigVMControllerNotifGuard NotifGuard(Controller, true); for (URigVMNode* Node : Graph->GetNodes()) { if (URigVMUnitNode* UnitNode = Cast(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 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::Get()->ExportText(Result, &DefaultValueElement, nullptr, nullptr, PPF_None, nullptr); FRigVMDefaultValueTypeGuard _(Controller, ERigVMPinDefaultValueType::Override); 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::GetBackwardsCompatibilityPublicFunctions(TArray& BackwardsCompatiblePublicFunctions, TMap& OldHeaders) { URigVMBlueprintGeneratedClass* CRGeneratedClass = GetRigVMBlueprintGeneratedClass(); FRigVMGraphFunctionStore& Store = CRGeneratedClass->GraphFunctionStore; 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(FunctionData.Header.LibraryPointer.GetNodeSoftPath().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); } } } } 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 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 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& Nodes = Model->GetNodes(); for (const URigVMNode* Node : Nodes) { if (const URigVMVariableNode* VariableNode = Cast(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(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 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 Nodes = Model->GetNodes(); for (URigVMNode* Node : Nodes) { if (const URigVMVariableNode* VariableNode = Cast(Node)) { PatchVariableNode(VariableNode); } else if (const URigVMParameterNode* ParameterNode = Cast(Node)) { PatchParameterNode(ParameterNode); } } } } #endif Super::PatchVariableNodesOnLoad(); } void UControlRigBlueprint::UpdateElementKeyRedirector(UControlRig* InControlRig) const { InControlRig->HierarchySettings = HierarchySettings; InControlRig->RigModuleSettings = RigModuleSettings; InControlRig->ElementKeyRedirector = FRigElementKeyRedirector(ArrayConnectionMap, InControlRig->GetHierarchy()); } 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(MyControlRigClass->GetDefaultObject(false))) { DefaultObject->PostInitInstanceIfRequired(); DefaultObject->GetHierarchy()->CopyPose(Hierarchy, true, true, true); TArray ArchetypeInstances; DefaultObject->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { if (UControlRig* InstanceRig = Cast(ArchetypeInstance)) { InstanceRig->PostInitInstanceIfRequired(); if(!InstanceRig->IsRigModuleInstance()) { InstanceRig->GetHierarchy()->CopyPose(Hierarchy, true, true, true); } } } } } } void UControlRigBlueprint::PropagateHierarchyFromBPToInstances() const { if (UClass* MyControlRigClass = GeneratedClass) { if (UControlRig* DefaultObject = Cast(MyControlRigClass->GetDefaultObject(false))) { DefaultObject->PostInitInstanceIfRequired(); DefaultObject->GetHierarchy()->CopyHierarchy(Hierarchy); UpdateElementKeyRedirector(DefaultObject); if (!DefaultObject->HasAnyFlags(RF_NeedPostLoad)) // If CDO is loading, skip Init, it will be done later { DefaultObject->Initialize(true); } TArray ArchetypeInstances; DefaultObject->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { if (UControlRig* InstanceRig = Cast(ArchetypeInstance)) { if (InstanceRig->IsRigModuleInstance()) { if (UModularRig* ModularRig = Cast(InstanceRig->GetOuter())) { ModularRig->RequestInit(); } } else { InstanceRig->PostInitInstanceIfRequired(); InstanceRig->GetHierarchy()->CopyHierarchy(Hierarchy); InstanceRig->HierarchySettings = HierarchySettings; UpdateElementKeyRedirector(InstanceRig); InstanceRig->Initialize(true); } } } } } } void UControlRigBlueprint::PropagateDrawInstructionsFromBPToInstances() const { if (UClass* MyControlRigClass = GeneratedClass) { if (UControlRig* DefaultObject = Cast(MyControlRigClass->GetDefaultObject(false))) { DefaultObject->DrawContainer = DrawContainer; TArray ArchetypeInstances; DefaultObject->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { if (UControlRig* InstanceRig = Cast(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(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(MyControlRigClass->GetDefaultObject(false))) { TArray 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(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::PropagateModuleHierarchyFromBPToInstances() const { if (const UClass* MyControlRigClass = GeneratedClass) { if (UModularRig* DefaultObject = Cast(MyControlRigClass->GetDefaultObject(false))) { // We need to first transfer the model from the blueprint to the CDO // We then ask instances to initialize which will provoke a call UModularRig::UpdateModuleHierarchyFromCDO DefaultObject->ResetModules(); // copy the model over to the CDO. // non-CDO instances are going to instantiate the model into a // UObject module instance tree. CDO's are data only to avoid bugs / // behaviors in the blueprint re-instancer - which is disregarding any // object under a CDO. DefaultObject->ModularRigModel = ModularRigModel; DefaultObject->ModularRigModel.SetOuterClientHost(DefaultObject); DefaultObject->ModularRigSettings = ModularRigSettings; TArray ArchetypeInstances; DefaultObject->GetArchetypeInstances(ArchetypeInstances); for (UObject* ArchetypeInstance : ArchetypeInstances) { if (UControlRig* InstanceRig = Cast(ArchetypeInstance)) { // this will provoke a call to InitializeFromCDO InstanceRig->Initialize(true); } } } } } void UControlRigBlueprint::UpdateModularDependencyDelegates() { TArray VisitList; ModularRigModel.ForEachModule([&VisitList, this](const FRigModuleReference* Element) -> bool { if(const UClass* Class = Element->Class.Get()) { if(UControlRigBlueprint* Blueprint = Cast(Class->ClassGeneratedBy)) { if(!VisitList.Contains(Blueprint)) { Blueprint->OnVMCompiled().RemoveAll(this); Blueprint->OnModularRigCompiled().RemoveAll(this); Blueprint->OnVMCompiled().AddUObject(this, &UControlRigBlueprint::OnModularDependencyVMCompiled); Blueprint->OnModularRigCompiled().AddUObject(this, &UControlRigBlueprint::OnModularDependencyChanged); VisitList.Add(Blueprint); } } } return true; }); } void UControlRigBlueprint::OnModularDependencyVMCompiled(UObject* InBlueprint, URigVM* InVM, FRigVMExtendedExecuteContext& InExecuteContext) { if(URigVMBlueprint* RigVMBlueprint = Cast(InBlueprint)) { OnModularDependencyChanged(RigVMBlueprint); } } void UControlRigBlueprint::OnModularDependencyChanged(URigVMBlueprint* InBlueprint) { RefreshModuleVariables(); RefreshModuleConnectors(); RecompileModularRig(); } void UControlRigBlueprint::RequestConstructionOnAllModules() { // the rig will perform initialize itself - but we should request construction check(IsModularRig()); const URigVMBlueprintGeneratedClass* RigClass = GetRigVMBlueprintGeneratedClass(); check(RigClass); UControlRig* CDO = Cast(RigClass->GetDefaultObject(true /* create if needed */)); TArray ArchetypeInstances; CDO->GetArchetypeInstances(ArchetypeInstances); // visit all or our instances and request construction for (UObject* Instance : ArchetypeInstances) { if (UModularRig* InstanceRig = Cast(Instance)) { InstanceRig->RequestConstruction(); } } } void UControlRigBlueprint::RefreshModuleVariables() { if(!IsModularRig()) { return; } if (UModularRigController* Controller = GetModularRigController()) { Controller->RefreshModuleVariables(false); } } void UControlRigBlueprint::RefreshModuleConnectors() { if(!IsModularRig()) { return; } if (UModularRigController* Controller = GetModularRigController()) { TGuardValue NotificationsGuard(Controller->bSuspendNotifications, true); ModularRigModel.ForEachModule([this](const FRigModuleReference* Element) -> bool { RefreshModuleConnectors(Element, false); return true; }); } PropagateHierarchyFromBPToInstances(); } void UControlRigBlueprint::RefreshModuleConnectors(const FRigModuleReference* InModule, bool bPropagateHierarchy) { if(!IsModularRig()) { return; } // avoid dead class pointers if(InModule->Class.Get() == nullptr) { return; } const bool bRemoveAllConnectors = !ModularRigModel.FindModule(InModule->Name); if (URigHierarchyController* Controller = GetHierarchyController()) { if (UControlRig* CDO = GetControlRigClass()->GetDefaultObject()) { const TArray AllConnectors = Hierarchy->GetKeysOfType(); TArray ExistingConnectors = AllConnectors.FilterByPredicate([InModule, this](const FRigElementKey& ConnectorKey) -> bool { const FRigElementKey PatchedKey = ConnectorKey.ConvertToModuleNameFormat(&ModularRigModel.PreviousModulePaths); const FRigHierarchyModulePath ConnectorModulePath(PatchedKey.Name); return ConnectorModulePath.HasModuleName(InModule->Name); }); // setup the module information. this is needed so that newly added // connectors result in the right namespace metadata etc FRigVMExtendedExecuteContext& Context = CDO->GetRigVMExtendedExecuteContext(); FControlRigExecuteContext& PublicContext = Context.GetPublicDataSafe(); const UControlRig* ModuleCDO = InModule->Class->GetDefaultObject(); const TArray& ExpectedConnectors = ModuleCDO->GetRigModuleSettings().ExposedConnectors; // rename the connectors since their keys have been patched for(FRigElementKey& ConnectorKey : ExistingConnectors) { const FRigElementKey PatchedKey = ConnectorKey.ConvertToModuleNameFormat(&ModularRigModel.PreviousModulePaths); if(ConnectorKey != PatchedKey) { ConnectorKey = Controller->RenameElement(ConnectorKey, PatchedKey.Name); } } // remove the obsolete connectors for(const FRigElementKey& ConnectorKey : ExistingConnectors) { const FRigHierarchyModulePath ConnectorModulePath(ConnectorKey.Name); const bool bConnectorExpected = ExpectedConnectors.ContainsByPredicate( [&ConnectorModulePath](const FRigModuleConnector& ExpectedConnector) -> bool { return ConnectorModulePath.HasElementName(ExpectedConnector.Name); } ); if(bRemoveAllConnectors || !bConnectorExpected) { Hierarchy->Modify(); (void)Controller->RemoveElement(ConnectorKey); ArrayConnectionMap.Remove(ConnectorKey); } } // add the missing expected connectors if(!bRemoveAllConnectors) { for (const FRigModuleConnector& Connector : ExpectedConnectors) { const FName ConnectorName = *Connector.Name; const FRigHierarchyModulePath ConnectorModulePath(InModule->Name.ToString(), Connector.Name); const FRigElementKey CombinedConnectorKey(ConnectorModulePath.GetPathFName(), ERigElementType::Connector); if(!Hierarchy->Contains(CombinedConnectorKey)) { const FString ModulePrefix = InModule->GetElementPrefix(); const FString ParentModulePrefix = InModule->GetParentModule() ? InModule->GetParentModule()->GetElementPrefix() : ModulePrefix; const FString RootModulePrefix = InModule->GetRootModule() ? InModule->GetRootModule()->GetElementPrefix() : ModulePrefix; FRigHierarchyExecuteContextBracket HierarchyContextGuard(Hierarchy, &Context); FControlRigExecuteContextRigModuleGuard RigModuleGuard(PublicContext, ModulePrefix, ParentModulePrefix, RootModulePrefix); Hierarchy->Modify(); (void)Controller->AddConnector(ConnectorName, Connector.Settings); } else { // copy the connector settings FRigConnectorElement* ExistingConnector = Hierarchy->FindChecked(CombinedConnectorKey); ExistingConnector->Settings = Connector.Settings; } } } if (bPropagateHierarchy) { PropagateHierarchyFromBPToInstances(); } } } } void UControlRigBlueprint::HandleHierarchyModified(ERigHierarchyNotification InNotification, URigHierarchy* InHierarchy, const FRigNotificationSubject& InSubject) { #if WITH_EDITOR if(bSuspendAllNotifications) { return; } const FRigBaseElement* InElement = InSubject.Element; const FRigBaseComponent* InComponent = InSubject.Component; switch(InNotification) { case ERigHierarchyNotification::ElementRemoved: { Modify(); Influences.OnKeyRemoved(InElement->GetKey()); PropagateHierarchyFromBPToInstances(); break; } case ERigHierarchyNotification::ElementRenamed: { Modify(); if(InElement) { const FName PreviousName = InHierarchy->GetPreviousHierarchyName(InElement->GetKey()); const FRigElementKey OldKey(PreviousName, InElement->GetType()); HandleHierarchyElementKeyChanged(OldKey, InElement->GetKey()); } break; } case ERigHierarchyNotification::ElementAdded: case ERigHierarchyNotification::ParentChanged: case ERigHierarchyNotification::ElementReordered: case ERigHierarchyNotification::HierarchyReset: case ERigHierarchyNotification::ComponentAdded: case ERigHierarchyNotification::ComponentRemoved: case ERigHierarchyNotification::ComponentContentChanged: { Modify(); PropagateHierarchyFromBPToInstances(); break; } case ERigHierarchyNotification::ComponentRenamed: { Modify(); if(InComponent) { const FName PreviousName = InHierarchy->GetPreviousHierarchyName(InComponent->GetKey()); const FRigComponentKey OldKey(InComponent->GetElementKey(), PreviousName); HandleHierarchyComponentKeyChanged(OldKey, InComponent->GetKey()); } break; } case ERigHierarchyNotification::ComponentReparented: { Modify(); if(InComponent) { const FRigHierarchyKey PreviousParent = InHierarchy->GetPreviousHierarchyParent(InComponent->GetKey()); if(PreviousParent.IsElement()) { const FRigComponentKey OldKey(PreviousParent.GetElement(), InComponent->GetFName()); HandleHierarchyComponentKeyChanged(OldKey, InComponent->GetKey()); } } break; } case ERigHierarchyNotification::ElementSelected: { bool bClearTransientControls = true; if (const FRigControlElement* ControlElement = Cast(InElement)) { if (ControlElement->Settings.bIsTransientControl) { bClearTransientControls = false; } } if(bClearTransientControls) { if(UControlRig* RigBeingDebugged = Cast(GetObjectBeingDebugged())) { const FName TransientControlName = UControlRig::GetNameForTransientControl(InElement->GetKey()); const FRigElementKey TransientControlKey(TransientControlName, ERigElementType::Control); if (const FRigControlElement* ControlElement = RigBeingDebugged->GetHierarchy()->Find(TransientControlKey)) { if (ControlElement->Settings.bIsTransientControl) { bClearTransientControls = false; } } } } if(bClearTransientControls) { ClearTransientControls(); } break; } case ERigHierarchyNotification::ElementDeselected: { if (const FRigControlElement* ControlElement = Cast(InElement)) { if (ControlElement->Settings.bIsTransientControl) { ClearTransientControls(); } } break; } default: { break; } } HierarchyModifiedEvent.Broadcast(InNotification, InHierarchy, InElement); #endif } void UControlRigBlueprint::HandleHierarchyElementKeyChanged(const FRigElementKey& InOldKey, const FRigElementKey& InNewKey) { if(InOldKey == InNewKey) { return; } static UEnum* RigElementTypeEnum = StaticEnum(); check(RigElementTypeEnum); const FString OldNameStr = InOldKey.Name.ToString(); const FString NewNameStr = InNewKey.Name.ToString(); const ERigElementType ElementType = InNewKey.Type; // update all of the graphs with the new key TArray EdGraphs; GetAllGraphs(EdGraphs); for (UEdGraph* Graph : EdGraphs) { URigVMEdGraph* RigGraph = Cast(Graph); if (RigGraph == nullptr) { continue; } URigVMController* Controller = RigGraph->GetController(); if(Controller == nullptr) { continue; } { FRigVMBlueprintCompileScope CompileScope(this); for (UEdGraphNode* Node : RigGraph->Nodes) { if (URigVMEdGraphNode* RigNode = Cast(Node)) { if (URigVMNode* ModelNode = RigNode->GetModelNode()) { TArray ModelPins = ModelNode->GetAllPinsRecursively(); for (URigVMPin * ModelPin : ModelPins) { if(ModelPin->GetCPPType() == RigVMTypeUtils::FNameType) { if ((ModelPin->GetCustomWidgetName() == TEXT("BoneName") && ElementType == ERigElementType::Bone) || (ModelPin->GetCustomWidgetName() == TEXT("ControlName") && ElementType == ERigElementType::Control) || (ModelPin->GetCustomWidgetName() == TEXT("SpaceName") && ElementType == ERigElementType::Null) || (ModelPin->GetCustomWidgetName() == TEXT("CurveName") && ElementType == ERigElementType::Curve) || (ModelPin->GetCustomWidgetName() == TEXT("ConnectorName") && ElementType == ERigElementType::Connector)) { if (ModelPin->GetDefaultValue() == OldNameStr) { Controller->SetPinDefaultValue(ModelPin->GetPinPath(), NewNameStr, false); } } } else if (ModelPin->GetCPPTypeObject() == FRigElementKey::StaticStruct()) { const FString OldDefaultValueString = ModelPin->GetDefaultValue(); FRigElementKey OldDefaultKey; FRigElementKey::StaticStruct()->ImportText(*OldDefaultValueString, &OldDefaultKey, nullptr, EPropertyPortFlags::PPF_None, nullptr, FRigElementKey::StaticStruct()->GetName(), true); if(OldDefaultKey == InOldKey) { FString NewDefaultKeyString; FRigElementKey::StaticStruct()->ExportText(NewDefaultKeyString, &InNewKey, nullptr, nullptr, PPF_ExternalEditor, nullptr); Controller->SetPinDefaultValue(ModelPin->GetPinPath(), NewDefaultKeyString, false); } } } } } } } } // update all of the influences Influences.OnKeyRenamed(InOldKey, InNewKey); if (IsControlRigModule() && InNewKey.Type == ERigElementType::Connector) { if (FRigElementKeyCollection* Targets = ArrayConnectionMap.Find(InOldKey)) { ArrayConnectionMap.FindOrAdd(InNewKey, *Targets); ArrayConnectionMap.Remove(InOldKey); } } PropagateHierarchyFromBPToInstances(); } void UControlRigBlueprint::HandleHierarchyComponentKeyChanged(const FRigComponentKey& InOldKey, const FRigComponentKey& InNewKey) { if(InOldKey == InNewKey) { return; } // update all of the graphs with the new key TArray EdGraphs; GetAllGraphs(EdGraphs); for (UEdGraph* Graph : EdGraphs) { URigVMEdGraph* RigGraph = Cast(Graph); if (RigGraph == nullptr) { continue; } URigVMController* Controller = RigGraph->GetController(); if(Controller == nullptr) { continue; } { FRigVMBlueprintCompileScope CompileScope(this); for (UEdGraphNode* Node : RigGraph->Nodes) { if (URigVMEdGraphNode* RigNode = Cast(Node)) { if (URigVMNode* ModelNode = RigNode->GetModelNode()) { TArray ModelPins = ModelNode->GetAllPinsRecursively(); for (URigVMPin * ModelPin : ModelPins) { if (ModelPin->GetCPPTypeObject() == FRigComponentKey::StaticStruct()) { const FString OldDefaultValueString = ModelPin->GetDefaultValue(); FRigComponentKey OldDefaultKey; FRigComponentKey::StaticStruct()->ImportText(*OldDefaultValueString, &OldDefaultKey, nullptr, EPropertyPortFlags::PPF_None, nullptr, FRigComponentKey::StaticStruct()->GetName(), true); if(OldDefaultKey == InOldKey) { FString NewDefaultKeyString; FRigComponentKey::StaticStruct()->ExportText(NewDefaultKeyString, &InNewKey, nullptr, nullptr, PPF_ExternalEditor, nullptr); Controller->SetPinDefaultValue(ModelPin->GetPinPath(), NewDefaultKeyString, false); } } } } } } } } PropagateHierarchyFromBPToInstances(); } void UControlRigBlueprint::HandleRigModulesModified(EModularRigNotification InNotification, const FRigModuleReference* InModule) { bool bRecompile = true; switch (InNotification) { case EModularRigNotification::ModuleAdded: { if (InModule) { RefreshModuleConnectors(InModule); UpdateModularDependencyDelegates(); } break; } case EModularRigNotification::ModuleRenamed: case EModularRigNotification::ModuleReparented: { if (InModule) { if (URigHierarchyController* Controller = GetHierarchyController()) { if (UControlRig* CDO = GetControlRigClass()->GetDefaultObject()) { Hierarchy->Modify(); struct ConnectionInfo { FString NewPath; FRigElementKeyCollection TargetConnections; FRigConnectorSettings Settings; }; const FName OldModuleName = InModule->PreviousName; const FName NewModuleName = InModule->Name; TArray Connectors = Controller->GetHierarchy()->GetKeysOfType(); TMap RenamedConnectors; // old key -> new key for (const FRigElementKey& Connector : Connectors) { const FRigHierarchyModulePath ConnectorModulePath(Connector.Name); if (ConnectorModulePath.HasModuleName(OldModuleName)) { ConnectionInfo& Info = RenamedConnectors.FindOrAdd(Connector); Info.NewPath = ConnectorModulePath.ReplaceModuleName(NewModuleName); Info.Settings = CastChecked(Controller->GetHierarchy()->FindChecked(Connector))->Settings; if (FRigElementKeyCollection* TargetKeys = ArrayConnectionMap.Find(Connector)) { Info.TargetConnections = *TargetKeys; } } } // Remove connectors for (TPair& Pair : RenamedConnectors) { Controller->RemoveElement(Pair.Key); } // Add connectors { FRigVMExtendedExecuteContext& Context = CDO->GetRigVMExtendedExecuteContext(); FRigHierarchyExecuteContextBracket HierarchyContextGuard(Controller->GetHierarchy(), &Context); FControlRigExecuteContext& PublicContext = Context.GetPublicDataSafe(); for (TPair& Pair : RenamedConnectors) { const FName ConnectorName = FRigHierarchyModulePath(Pair.Value.NewPath).GetElementFName(); const FString ModulePrefix = InModule->GetElementPrefix(); const FString ParentModulePrefix = InModule->GetParentModule() ? InModule->GetParentModule()->GetElementPrefix() : ModulePrefix; const FString RootModulePrefix = InModule->GetRootModule() ? InModule->GetRootModule()->GetElementPrefix() : ModulePrefix; FControlRigExecuteContextRigModuleGuard RigModuleGuard(PublicContext, ModulePrefix, ParentModulePrefix, RootModulePrefix); const TGuardValue DisableErrors(Controller->bReportWarningsAndErrors, false); Controller->AddConnector(ConnectorName, Pair.Value.Settings); } } // update the target connections TMap PreviousArrayConnectionMap; Swap(PreviousArrayConnectionMap, ArrayConnectionMap); ArrayConnectionMap.Reset(); for(TPair& Connection : PreviousArrayConnectionMap) { FRigElementKey ConnectorKey = Connection.Key; FRigElementKeyCollection& TargetKeys = Connection.Value; FRigHierarchyModulePath ConnectorPath(ConnectorKey.Name); if(ConnectorPath.ReplaceModuleNameInline(OldModuleName, NewModuleName)) { ConnectorKey.Name = ConnectorPath.GetPathFName(); } for(FRigElementKey& TargetKey : TargetKeys.Keys) { FRigHierarchyModulePath TargetPath(TargetKey.Name); if(TargetPath.ReplaceModuleNameInline(OldModuleName, NewModuleName)) { TargetKey.Name = TargetPath.GetPathFName(); } } ArrayConnectionMap.Add(ConnectorKey, TargetKeys); } // update the previous module table for(TPair& ModulePathToName : ModularRigModel.PreviousModulePaths) { if(ModulePathToName.Value == OldModuleName) { Modify(); ModulePathToName.Value = NewModuleName; } } UpdateConnectionMapFromModel(); PropagateHierarchyFromBPToInstances(); } } } break; } case EModularRigNotification::ModuleRemoved: { if (InModule) { RefreshModuleConnectors(InModule); UpdateConnectionMapFromModel(); UpdateModularDependencyDelegates(); } break; } case EModularRigNotification::ConnectionChanged: { Hierarchy->Modify(); UpdateConnectionMapFromModel(); HierarchyModifiedEvent.Broadcast(ERigHierarchyNotification::HierarchyReset, Hierarchy, {}); break; } case EModularRigNotification::ModuleClassChanged: { if (InModule) { RefreshModuleConnectors(InModule); UpdateConnectionMapFromModel(); } break; } case EModularRigNotification::ModuleShortNameChanged: { bRecompile = false; break; } case EModularRigNotification::ModuleConfigValueChanged: { bRecompile = false; PropagateModuleHierarchyFromBPToInstances(); RequestConstructionOnAllModules(); break; } case EModularRigNotification::InteractionBracketOpened: { ModulesRecompilationBracket++; break; } case EModularRigNotification::InteractionBracketClosed: case EModularRigNotification::InteractionBracketCanceled: { ModulesRecompilationBracket--; break; } case EModularRigNotification::ModuleSelected: case EModularRigNotification::ModuleDeselected: { // don't do anything during selection return; } default: { break; } } if (bRecompile && ModulesRecompilationBracket == 0) { RecompileModularRig(); } } UControlRigBlueprint::FControlValueScope::FControlValueScope(UControlRigBlueprint* InBlueprint) : Blueprint(InBlueprint) { #if WITH_EDITOR check(Blueprint); if (UControlRig* CR = Cast(Blueprint->GetObjectBeingDebugged())) { TArray Controls = CR->AvailableControls(); for (FRigControlElement* ControlElement : Controls) { ControlValues.Add(ControlElement->GetFName(), CR->GetControlValue(ControlElement->GetFName())); } } #endif } UControlRigBlueprint::FControlValueScope::~FControlValueScope() { #if WITH_EDITOR check(Blueprint); if (UControlRig* CR = Cast(Blueprint->GetObjectBeingDebugged())) { for (const TPair& Pair : ControlValues) { if (CR->FindControl(Pair.Key)) { CR->SetControlValue(Pair.Key, Pair.Value); } } } #endif } #undef LOCTEXT_NAMESPACE