├── .gitattributes ├── Resources ├── Icon128.png └── README │ ├── Menu_Run_EUW.PNG │ ├── Navigate_To_EUW.PNG │ ├── Error_Nodes_In_BP.PNG │ ├── Navigate_To_EUW_2.PNG │ ├── Refresh_All_Nodes.PNG │ ├── Right_Click_Run_EUW.PNG │ ├── Single_Refresh_Node.PNG │ ├── UndefinedObjects_Error.PNG │ ├── EUW_ControlRigTools_Preview.PNG │ └── EUW_ControlRigRenameTool_Preview.PNG ├── .gitignore ├── Content ├── Python │ ├── __init__.py │ ├── control_rig.py │ ├── control_rig_utils.py │ └── init_unreal.py ├── EUW_ControlRigTools.uasset └── EUW_ControlRigRenameTool.uasset ├── Source └── ControlRigExt │ ├── Public │ └── ControlRigExt.h │ ├── Private │ ├── ControlRigExt.cpp │ └── Units │ │ ├── RigUnit_GetGizmoVisibility.cpp │ │ ├── RigUnit_SetGizmoVisibility.h │ │ ├── RigUnit_GetGizmoVisibility.h │ │ ├── RigUnit_SetGizmoVisibility.cpp │ │ ├── RigUnit_FKChain.h │ │ └── RigUnit_FKChain.cpp │ └── ControlRigExt.Build.cs ├── ControlRigExt.uplugin └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/Icon128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Intermediate/ 2 | Binaries/ 3 | .idea/ 4 | 5 | /Content/Python/.idea/ 6 | /Content/Python/__pycache__/ 7 | *.pyc -------------------------------------------------------------------------------- /Content/Python/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.dont_write_bytecode = True 3 | 4 | print('Control Rig Extension plugin loaded!') -------------------------------------------------------------------------------- /Content/EUW_ControlRigTools.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Content/EUW_ControlRigTools.uasset -------------------------------------------------------------------------------- /Resources/README/Menu_Run_EUW.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/Menu_Run_EUW.PNG -------------------------------------------------------------------------------- /Resources/README/Navigate_To_EUW.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/Navigate_To_EUW.PNG -------------------------------------------------------------------------------- /Content/EUW_ControlRigRenameTool.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Content/EUW_ControlRigRenameTool.uasset -------------------------------------------------------------------------------- /Resources/README/Error_Nodes_In_BP.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/Error_Nodes_In_BP.PNG -------------------------------------------------------------------------------- /Resources/README/Navigate_To_EUW_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/Navigate_To_EUW_2.PNG -------------------------------------------------------------------------------- /Resources/README/Refresh_All_Nodes.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/Refresh_All_Nodes.PNG -------------------------------------------------------------------------------- /Resources/README/Right_Click_Run_EUW.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/Right_Click_Run_EUW.PNG -------------------------------------------------------------------------------- /Resources/README/Single_Refresh_Node.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/Single_Refresh_Node.PNG -------------------------------------------------------------------------------- /Resources/README/UndefinedObjects_Error.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/UndefinedObjects_Error.PNG -------------------------------------------------------------------------------- /Resources/README/EUW_ControlRigTools_Preview.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/EUW_ControlRigTools_Preview.PNG -------------------------------------------------------------------------------- /Resources/README/EUW_ControlRigRenameTool_Preview.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorbenwright/ControlRigExt/HEAD/Resources/README/EUW_ControlRigRenameTool_Preview.PNG -------------------------------------------------------------------------------- /Source/ControlRigExt/Public/ControlRigExt.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FControlRigExtModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Source/ControlRigExt/Private/ControlRigExt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "ControlRigExt.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FControlRigExtModule" 6 | 7 | void FControlRigExtModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FControlRigExtModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FControlRigExtModule, ControlRigExt) -------------------------------------------------------------------------------- /ControlRigExt.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "ControlRigExt", 6 | "Description": "Extension module for the Epic ControlRig plugin", 7 | "Category": "Animation", 8 | "CreatedBy": "Technically Games", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": true, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Plugins": 18 | [ 19 | { 20 | "Name": "ControlRig", 21 | "Enabled": true 22 | } 23 | ], 24 | "Modules": [ 25 | { 26 | "Name": "ControlRigExt", 27 | "Type": "Runtime", 28 | "LoadingPhase": "PreDefault" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /Source/ControlRigExt/Private/Units/RigUnit_GetGizmoVisibility.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "RigUnit_GetGizmoVisibility.h" 4 | #include "Units/RigUnitContext.h" 5 | 6 | FRigUnit_GetGizmoVisibility_Execute() 7 | { 8 | DECLARE_SCOPE_HIERARCHICAL_COUNTER_RIGUNIT( ) 9 | const FRigControlHierarchy* Hierarchy = Context.GetControls( ); 10 | if (Hierarchy) 11 | { 12 | switch (Context.State) 13 | { 14 | case EControlRigState::Init: 15 | { 16 | CachedControlIndex = Hierarchy->GetIndex(Control); 17 | } 18 | case EControlRigState::Update: 19 | { 20 | if (CachedControlIndex != INDEX_NONE) 21 | { 22 | Visibility = (*Hierarchy)[CachedControlIndex].bGizmoEnabled; 23 | } 24 | } 25 | default: 26 | { 27 | break; 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Source/ControlRigExt/Private/Units/RigUnit_SetGizmoVisibility.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #pragma once 4 | 5 | #include "ControlRig/Public/Units/RigUnit.h" 6 | #include "RigUnit_SetGizmoVisibility.generated.h" 7 | 8 | /** 9 | * SetGizmoVisibility will set the visibility of a Controller's gizmo. 10 | * Note: This does not seem to work as-is. 11 | **/ 12 | USTRUCT(meta=(DisplayName="Set Gizmo Visibility", Category="Controls Ext", Keywords="SetGizmoVisibility", NodeColor = "0.25 0.25 0.05")) 13 | struct FRigUnit_SetGizmoVisibility : public FRigUnitMutable 14 | { 15 | GENERATED_BODY() 16 | 17 | FRigUnit_SetGizmoVisibility() 18 | : Visibility(true) 19 | , CachedControlIndex(INDEX_NONE) 20 | {} 21 | 22 | RIGVM_METHOD( ) 23 | virtual void Execute(const FRigUnitContext& Context) override; 24 | 25 | UPROPERTY(meta = (Input, CustomWidget = "ControlName", Constant)) 26 | FName Control; 27 | 28 | UPROPERTY(meta = (Input, Output)) 29 | bool Visibility; 30 | 31 | UPROPERTY() 32 | int32 CachedControlIndex; 33 | }; -------------------------------------------------------------------------------- /Source/ControlRigExt/Private/Units/RigUnit_GetGizmoVisibility.h: -------------------------------------------------------------------------------- 1 | // Copyright Technically Games, LLC 2 | 3 | #pragma once 4 | 5 | #include "ControlRig/Public/Units/RigUnit.h" 6 | #include "RigUnit_GetGizmoVisibility.generated.h" 7 | 8 | /** 9 | * GetGizmoVisibility returns the current Visibility of a Controller's gizmo 10 | **/ 11 | USTRUCT(meta=(DisplayName="Get Gizmo Visibility", Category="Controls Ext", Keywords="GetGizmoVisibility")) 12 | struct FRigUnit_GetGizmoVisibility : public FRigUnit 13 | { 14 | GENERATED_BODY() 15 | 16 | FRigUnit_GetGizmoVisibility() 17 | : Visibility(false) 18 | , CachedControlIndex(INDEX_NONE) 19 | {} 20 | 21 | RIGVM_METHOD( ) 22 | virtual void Execute(const FRigUnitContext& Context) override; 23 | 24 | UPROPERTY(meta= (Input, CustomWidget = "ControlName", Constant)) 25 | FName Control; 26 | 27 | UPROPERTY(meta=(Output)) 28 | bool Visibility; 29 | 30 | // Used to cache the internally used bone index 31 | UPROPERTY() 32 | int32 CachedControlIndex; 33 | }; -------------------------------------------------------------------------------- /Source/ControlRigExt/Private/Units/RigUnit_SetGizmoVisibility.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "RigUnit_SetGizmoVisibility.h" 4 | #include "Units/RigUnitContext.h" 5 | 6 | FRigUnit_SetGizmoVisibility_Execute() 7 | { 8 | DECLARE_SCOPE_HIERARCHICAL_COUNTER_RIGUNIT( ) 9 | FRigControlHierarchy* Hierarchy = ExecuteContext.GetControls(); 10 | if (Hierarchy) 11 | { 12 | switch (Context.State) 13 | { 14 | case EControlRigState::Init: 15 | { 16 | CachedControlIndex = Hierarchy->GetIndex(Control); 17 | break; 18 | } 19 | case EControlRigState::Update: 20 | { 21 | if (CachedControlIndex != INDEX_NONE) 22 | { 23 | /** This does not work for whatever reason. Who knows. **/ 24 | FRigControl Ctl = (*Hierarchy)[CachedControlIndex]; 25 | if (Ctl.ControlType != ERigControlType::Bool) 26 | { 27 | Ctl.bGizmoEnabled = Visibility; 28 | } 29 | } 30 | break; 31 | } 32 | default: 33 | { 34 | break; 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Source/ControlRigExt/ControlRigExt.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class ControlRigExt : ModuleRules 6 | { 7 | public ControlRigExt(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PrivateIncludePaths.Add("ControlRigExt/Private"); 12 | PrivateIncludePaths.Add("ControlRigExt/Private/Units"); 13 | 14 | PublicDependencyModuleNames.AddRange( 15 | new string[] 16 | { 17 | "AnimationCore", 18 | "LevelSequence", 19 | "RigVM", 20 | "ControlRig" 21 | } 22 | ); 23 | 24 | PrivateDependencyModuleNames.AddRange( 25 | new string[] 26 | { 27 | "Core", 28 | "CoreUObject", 29 | "Engine", 30 | "AnimGraphRuntime", 31 | "MovieScene", 32 | "MovieSceneTracks", 33 | "PropertyPath", 34 | "TimeManagement", 35 | } 36 | ); 37 | 38 | 39 | if (Target.bBuildEditor == true) 40 | { 41 | PublicDependencyModuleNames.AddRange( 42 | new string[] 43 | { 44 | "RigVMDeveloper", 45 | "AnimGraph", 46 | } 47 | ); 48 | 49 | PrivateDependencyModuleNames.AddRange( 50 | new string[] 51 | { 52 | "UnrealEd", 53 | "BlueprintGraph", 54 | "PropertyEditor", 55 | "RigVMDeveloper", 56 | } 57 | ); 58 | 59 | PrivateIncludePathModuleNames.Add("ControlRigEditor"); 60 | DynamicallyLoadedModuleNames.Add("ControlRigEditor"); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ControlRigExt 2 | A extension module for UE4 ControlRig 3 | 4 | # Editor Utility Widgets 5 | 6 | ## Previews 7 | ![EUW_ControlRigTools](Resources/README/EUW_ControlRigTools_Preview.PNG) 8 | 9 | ![EUW_ControlRigRenameTool](Resources/README/EUW_ControlRigRenameTool_Preview.PNG) 10 | 11 | ## How to run 12 | 13 | Navigate to the ControlRigExt Content folder and open up EUW_ControlRigTools 14 | 15 | ![Navigate to EUW_ControlRigTools](Resources/README/Navigate_To_EUW.PNG) 16 | 17 | Right click on it, and hit Run Editor Utility Widget 18 | 19 | ![Right Click Run EUW](Resources/README/Right_Click_Run_EUW.PNG) 20 | 21 | Once you have done that, you can now run your Editor Utility Widget in Window->Editor Utility Widget 22 | 23 | ![Window Menu Run EUW](Resources/README/Menu_Run_EUW.PNG) 24 | 25 | # Troubleshooting 26 | 27 | If you ever seen this issue when running the tool 28 | 29 | ![Undefined Objects Error](Resources/README/UndefinedObjects_Error.PNG) 30 | 31 | Navigate to the ControlRigExt Content folder and open up EUW_ControlRigTools 32 | 33 | ![Error Navigate to EUW_ControlRigTools](Resources/README/Navigate_To_EUW.PNG) 34 | 35 | You should see the error nodes in the Editor Utility Widget 36 | 37 | ![Errors in BP](Resources/README/Error_Nodes_In_BP.PNG) 38 | 39 | Select any red node, right click and hit "Refresh Nodes" 40 | 41 | ![Single Refresh Node](Resources/README/Single_Refresh_Node.PNG) 42 | 43 | Afterwards, you will then go to File->Refresh All Nodes and Save 44 | 45 | ![Refresh All Nodes](Resources/README/Refresh_All_Nodes.PNG) 46 | 47 | You will also need to do it for the EUW_ControlRigRenameTool 48 | 49 | ![EUW_ControlRigRenameTool Navigation](Resources/README/Navigate_To_EUW_2.PNG) -------------------------------------------------------------------------------- /Source/ControlRigExt/Private/Units/RigUnit_FKChain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ControlRig/Private/Units/Highlevel/RigUnit_HighlevelBase.h" 4 | #include "RigUnit_FKChain.generated.h" 5 | 6 | USTRUCT() 7 | struct FRigUnit_FKChain_JointControllerPair 8 | { 9 | GENERATED_BODY( ) 10 | 11 | /** 12 | * The name of the Bone to set the transform for. 13 | */ 14 | UPROPERTY(meta = (Input, Constant, CustomWidget = "BoneName")) 15 | FName Bone; 16 | 17 | /** 18 | * The transform value to set for the given Bone. 19 | */ 20 | UPROPERTY(meta = (Input, Output)) 21 | FTransform Transform; 22 | 23 | /** 24 | * Defines if the bone's transform should be set 25 | * in local or global space. 26 | */ 27 | UPROPERTY(meta = (Input)) 28 | EBoneGetterSetterMode Space; 29 | 30 | /** 31 | * If set to true all of the global transforms of the children 32 | * of this bone will be recalculated based on their local transforms. 33 | * Note: This is computationally more expensive than turning it off. 34 | */ 35 | UPROPERTY(meta = (Input, Constant)) 36 | bool bPropagateToChildren = true; 37 | 38 | int32 CachedBoneIndex; 39 | TArray ValidChildren; 40 | }; 41 | 42 | /** 43 | * Applies a simple FK chain to a joint chain without the needs for extra nodes 44 | */ 45 | USTRUCT(meta=(DisplayName="FK Chain", Category="Controls Ext", Keywords="FK,Chain")) 46 | struct FRigUnit_FKChain : public FRigUnit_HighlevelBaseMutable 47 | { 48 | GENERATED_BODY() 49 | 50 | RIGVM_METHOD() 51 | virtual void Execute(const FRigUnitContext& Context) override; 52 | 53 | FRigUnit_FKChain() 54 | { 55 | Weight = 1.f; 56 | } 57 | 58 | /** 59 | * The joint/controller pairs that should be coupled. Each successive joint MUST 60 | * be lower in the hierarchy than the previous. 61 | */ 62 | UPROPERTY(meta=(Input)) 63 | TArray JointControllerPairs; 64 | 65 | /** 66 | * The weight of the chain - how much of the FK should be applied 67 | */ 68 | UPROPERTY(meta = (Input, UIMin = "0.0", UIMax = "1.0")) 69 | float Weight; 70 | 71 | }; -------------------------------------------------------------------------------- /Content/Python/control_rig.py: -------------------------------------------------------------------------------- 1 | import unreal 2 | 3 | 4 | class ControlRigExtension(object): 5 | """ 6 | The primary rig object that we operate on. 7 | """ 8 | @classmethod 9 | def load_rig_from_path(cls, rig_path): 10 | """ 11 | Loads in a ControlRigBlueprint based on its path. Returns the resultant rig object 12 | :param rig_path: The relative Game path to the rig, obtained by right click->Copy Reference 13 | :type rig_path: str 14 | :return: The valid ControlRigBlueprint object 15 | :rtype: unreal.ControlRigBlueprint 16 | """ 17 | loaded_rig = unreal.load_asset(rig_path) 18 | 19 | if type(loaded_rig) == unreal.ControlRigBlueprint: 20 | return loaded_rig 21 | return None 22 | 23 | @staticmethod 24 | def cast_key_to_type(rig_key, rig): 25 | """ 26 | Given an element key and its parent hierarchy modifier, returns the object of the correct type 27 | :param rig_key: The key to query 28 | :type rig_key: unreal.RigElementKey 29 | :param rig: The rig we are looking at 30 | :type rig: unreal.ControlRigBlueprint 31 | :return: The casted type for the key 32 | :rtype: unreal.RigBone | unreal.RigControl | unreal.RigSpace 33 | """ 34 | hierarchy_mod = rig.get_hierarchy_modifier() 35 | if rig_key.type == unreal.RigElementType.BONE: 36 | return hierarchy_mod.get_bone(rig_key) 37 | elif rig_key.type == unreal.RigElementType.SPACE: 38 | return hierarchy_mod.get_space(rig_key) 39 | elif rig_key.type == unreal.RigElementType.CONTROL: 40 | return hierarchy_mod.get_control(rig_key) 41 | elif rig_key.type == unreal.RigElementType.CURVE: 42 | return hierarchy_mod.get_curve(rig_key) 43 | else: 44 | return None 45 | 46 | def __init__(self): 47 | """ 48 | Creates a ControlRigExtension object. Through this object we can manipulate the loaded object 49 | """ 50 | self._rig = None 51 | 52 | @property 53 | def rig(self): 54 | return self._rig 55 | 56 | @rig.setter 57 | def rig(self, value): 58 | self._rig = value 59 | 60 | def get_selected_controls(self): 61 | """ 62 | Returns the controls that are selected in the hierarchy panel. These return in a first-in/last-out manner 63 | :return: A list of the selected object 64 | :rtype: list[unreal.RigElementKey] 65 | """ 66 | return self.rig.get_hierarchy_modifier().get_selection() 67 | 68 | def get_index_by_name(self, controller_name): 69 | """ 70 | Given a name, returns the associated RigElementKey. 71 | :param controller_name: The path of the key to query 72 | :type controller_name: str 73 | :return: The RigElementKeys with the given name, if any 74 | :rtype: unreal.RigElementKey 75 | """ 76 | hierarchy_mod = self.rig.get_hierarchy_modifier() 77 | indexes = hierarchy_mod.get_elements() 78 | for ind in indexes: 79 | if ind.name == controller_name: 80 | return ind 81 | return None 82 | 83 | def paste_global_xform(self): 84 | """ 85 | Given a selection, copy the global transform from the first control into the initial transform of the second 86 | control 87 | :return: Nothing 88 | :rtype: None 89 | """ 90 | hierarchy_mod = self.rig.get_hierarchy_modifier() 91 | selection = self.get_selected_controls() 92 | if not len(selection) == 2: 93 | return 94 | global_xform = hierarchy_mod.get_global_transform(selection[1]) 95 | hierarchy_mod.set_initial_global_transform(selection[0], global_xform) 96 | -------------------------------------------------------------------------------- /Source/ControlRigExt/Private/Units/RigUnit_FKChain.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "RigUnit_FKChain.h" 4 | #include "Units/RigUnitContext.h" 5 | 6 | FRigUnit_FKChain_Execute() 7 | { 8 | DECLARE_SCOPE_HIERARCHICAL_COUNTER_RIGUNIT() 9 | FRigBoneHierarchy* Hierarchy = ExecuteContext.GetBones(); 10 | if (Hierarchy) 11 | { 12 | switch (Context.State) 13 | { 14 | case EControlRigState::Init: 15 | { 16 | for (int i = 0; i < JointControllerPairs.Num( ); i++) 17 | { 18 | FRigUnit_FKChain_JointControllerPair& Pair = JointControllerPairs[i]; 19 | 20 | Pair.CachedBoneIndex = Hierarchy->GetIndex( Pair.Bone ); 21 | 22 | Hierarchy->GetChildren( Pair.CachedBoneIndex, Pair.ValidChildren, true ); 23 | if (i > 0) 24 | { 25 | if (!JointControllerPairs[i-1].ValidChildren.Contains( Pair.CachedBoneIndex )) 26 | { 27 | UE_CONTROLRIG_RIGUNIT_REPORT_WARNING( TEXT("All FK Chain Bones must be in a single chain.")); 28 | return; 29 | } 30 | } 31 | } 32 | return; 33 | } 34 | case EControlRigState::Update: 35 | { 36 | if (Weight <= SMALL_NUMBER || JointControllerPairs.Num( ) < 1 ) 37 | { 38 | return; 39 | } 40 | for (FRigUnit_FKChain_JointControllerPair& Pair : JointControllerPairs) 41 | { 42 | if (Pair.CachedBoneIndex != INDEX_NONE) 43 | { 44 | switch (Pair.Space) 45 | { 46 | case EBoneGetterSetterMode::GlobalSpace: 47 | { 48 | if (FMath::IsNearlyEqual( Weight, 1.f )) 49 | { 50 | Hierarchy->SetGlobalTransform( Pair.Bone, 51 | Pair.Transform, 52 | Pair.bPropagateToChildren ); 53 | } 54 | else 55 | { 56 | float T = FMath::Clamp(Weight, 0.f, 1.f); 57 | const FTransform PreviousTransform = Hierarchy->GetGlobalTransform( Pair.Bone ); 58 | Hierarchy->SetGlobalTransform( Pair.Bone, 59 | FControlRigMathLibrary::LerpTransform( PreviousTransform, Pair.Transform, T ), 60 | Pair.bPropagateToChildren); 61 | } 62 | break; 63 | } 64 | case EBoneGetterSetterMode::LocalSpace: 65 | { 66 | if (FMath::IsNearlyEqual(Weight, 1.f)) 67 | { 68 | Hierarchy->SetLocalTransform(Pair.Bone, 69 | Pair.Transform, 70 | Pair.bPropagateToChildren); 71 | } 72 | else 73 | { 74 | float T = FMath::Clamp(Weight, 0.f, 1.f); 75 | const FTransform PreviousTransform = Hierarchy->GetLocalTransform(Pair.Bone); 76 | Hierarchy->SetLocalTransform(Pair.Bone, 77 | FControlRigMathLibrary::LerpTransform(PreviousTransform, Pair.Transform, T), 78 | Pair.bPropagateToChildren); 79 | } 80 | break; 81 | } 82 | default: 83 | { 84 | break; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | default: 91 | { 92 | break; 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Content/Python/control_rig_utils.py: -------------------------------------------------------------------------------- 1 | import unreal 2 | 3 | ''' 4 | Non UFUNCTION functions as we cannot call ufunctions within ufunctions unless if nested 5 | ''' 6 | 7 | def cast_key_to_type(rig, rig_key): 8 | """ 9 | Given an element key and its parent hierarchy modifier, returns the object of the correct type 10 | :param rig: The rig we are looking at 11 | :type rig: unreal.ControlRigBlueprint 12 | :param rig_key: The key to query 13 | :type rig_key: unreal.RigElementKey 14 | :return: The casted type for the key 15 | :rtype: unreal.RigBone | unreal.RigControl | unreal.RigSpace | unreal.RigCurve 16 | """ 17 | hierarchy_mod = rig.get_hierarchy_modifier() 18 | 19 | if rig_key.type == unreal.RigElementType.BONE: 20 | 21 | return hierarchy_mod.get_bone(rig_key) 22 | 23 | elif rig_key.type == unreal.RigElementType.SPACE: 24 | 25 | return hierarchy_mod.get_space(rig_key) 26 | 27 | elif rig_key.type == unreal.RigElementType.CONTROL: 28 | 29 | return hierarchy_mod.get_control(rig_key) 30 | 31 | elif rig_key.type == unreal.RigElementType.CURVE: 32 | 33 | return hierarchy_mod.get_curve(rig_key) 34 | 35 | else: 36 | 37 | return None 38 | 39 | def get_selected_controls(rig): 40 | """ 41 | Returns the controls that are selected in the hierarchy panel. These return in a first-in/last-out manner 42 | :param rig: The rig we are looking at 43 | :type rig: unreal.ControlRigBlueprint 44 | :return: A list of the selected object 45 | :rtype: list[unreal.RigElementKey] 46 | """ 47 | return rig.get_hierarchy_modifier().get_selection() 48 | 49 | def create_rig_element_key(rig, key_type): 50 | 51 | """ 52 | Given a RigElementType, add desired rig element to the rig with specific identifier. 53 | If there are selected rig elements, this will add desired rig element with selected transforms and name attached. 54 | :param rig: The rig we are looking at 55 | :type rig: unreal.ControlRigBlueprint 56 | :param key_type: enum value of Rig Element Type 57 | :type key_type: unreal.RigElementType value 58 | :return: A list of the selected object 59 | :rtype: list[unreal.RigElementKey] | list[] 60 | """ 61 | 62 | element_list = [] 63 | 64 | if key_type == unreal.RigElementType.BONE: 65 | 66 | type_name = "bone" 67 | 68 | elif key_type == unreal.RigElementType.SPACE: 69 | 70 | type_name = "space" 71 | 72 | elif key_type == unreal.RigElementType.CONTROL: 73 | 74 | type_name = "ctrl" 75 | 76 | elif key_type == unreal.RigElementType.CURVE: 77 | 78 | type_name = "curve" 79 | 80 | elif key_type == unreal.RigElementType.NONE or key_type == unreal.RigElementType.ALL: 81 | 82 | return element_list 83 | 84 | hierarchy_mod = rig.get_hierarchy_modifier() 85 | selection = hierarchy_mod.get_selection() 86 | 87 | if not selection: 88 | 89 | global_xform = unreal.Transform() 90 | 91 | element_name = type_name 92 | 93 | element = add_element_with_init_transform(hierarchy_mod, element_name, key_type, global_xform) 94 | 95 | element_list.append(element) 96 | 97 | for item in selection: 98 | 99 | global_xform = hierarchy_mod.get_global_transform(item) 100 | 101 | element_name = "{0}_{1}".format(item.get_editor_property("Name"), type_name) 102 | 103 | element = add_element_with_init_transform(hierarchy_mod, element_name, key_type, global_xform) 104 | 105 | element_list.append(element) 106 | 107 | return element_list 108 | 109 | def add_element_with_init_transform(hierarchy_mod, element_name, key_type, global_xform): 110 | """ 111 | Create a new element to the control rig hiearchy with a given name, element type, and initial transform 112 | :param hierarchy_mod: The control rig hierarchy object 113 | :type hierarchy_mod: unreal.ControlRigHierarchyModifier 114 | :param element_name: name of the new element 115 | :type element_name: str 116 | :param key_type: what RigElementType it will be 117 | :type key_type: unreal.RigElementType value 118 | :param global_xform: the initial transform 119 | :type global_xform: unreal.Transform 120 | :return: Nothing 121 | :rtype: None 122 | """ 123 | 124 | element = add_element_to_hierarchy_mod(hierarchy_mod, element_name, key_type) 125 | 126 | if key_type != unreal.RigElementType.CURVE: 127 | 128 | hierarchy_mod.set_initial_global_transform(element, global_xform) 129 | 130 | return element 131 | 132 | def add_element_to_hierarchy_mod(hierarchy_mod, element_name, key_type): 133 | """ 134 | Create a new element to the control rig hiearchy with a given name and element type 135 | :param hierarchy_mod: The control rig hierarchy object 136 | :type hierarchy_mod: unreal.ControlRigHierarchyModifier 137 | :param element_name: name of the new element 138 | :type element_name: str 139 | :param key_type: what RigElementType it will be 140 | :type key_type: unreal.RigElementType value 141 | :return: Nothing 142 | :rtype: None 143 | """ 144 | 145 | if key_type == unreal.RigElementType.BONE: 146 | 147 | element = hierarchy_mod.add_bone(element_name) 148 | 149 | elif key_type == unreal.RigElementType.SPACE: 150 | 151 | element = hierarchy_mod.add_space(element_name) 152 | 153 | elif key_type == unreal.RigElementType.CONTROL: 154 | 155 | element = hierarchy_mod.add_control(element_name) 156 | 157 | elif key_type == unreal.RigElementType.CURVE: 158 | 159 | element = hierarchy_mod.add_curve(element_name) 160 | 161 | else: 162 | 163 | element = None 164 | 165 | return element 166 | 167 | def edit_element_property(hierarchy_mod, rig_element, property, value): 168 | """ 169 | Given a rig element, edit the given property with the given value 170 | :param hierarchy_mod: The control rig hierarchy object 171 | :type hierarchy_mod: unreal.ControlRigHierarchyModifier 172 | :param rig_element: The rig element key object 173 | :type rig: unreal.RigElementKey 174 | :param property: property name 175 | :type property: string 176 | :param value: value for the property 177 | :type value: object 178 | :return: Nothing 179 | :rtype: None 180 | """ 181 | 182 | rig_element.set_editor_property(property, value) 183 | hierarchy_mod.set_control(rig_element) 184 | 185 | def get_elements_by_rig_type(rig, selection, rig_element_type): 186 | """ 187 | Given a boolean, set selected controls' gizmo to that color 188 | :param rig: The control rig object 189 | :type rig: unreal.ControlRigBlueprint 190 | :param selection: list of RigElementKeys 191 | :type selection: list[unreal.RigElementKey] 192 | :param rig_element_type: class type of desired rig type 193 | :type rig_element_type: unreal.uclass 194 | :return: return_list 195 | :rtype: list[rig_element_type] | list[] 196 | """ 197 | 198 | return_list = [] 199 | 200 | if not selection: 201 | 202 | return return_list 203 | 204 | for item in selection: 205 | 206 | rig_element = cast_key_to_type(rig, item) 207 | 208 | if type(rig_element) != rig_element_type: 209 | 210 | continue 211 | 212 | return_list.append(rig_element) 213 | 214 | return return_list -------------------------------------------------------------------------------- /Content/Python/init_unreal.py: -------------------------------------------------------------------------------- 1 | 2 | # Package import 3 | import unreal 4 | 5 | # Utility import 6 | import control_rig_utils 7 | 8 | @unreal.uclass() 9 | class ControlRigBPExt(unreal.BlueprintFunctionLibrary): 10 | 11 | @unreal.ufunction(params = [unreal.ControlRigBlueprint], ret=unreal.Array(unreal.RigElementKey), static = True, meta=dict(Category="Control Rig Blueprint Ext")) 12 | def GetSelectedControls(rig): 13 | """ 14 | Returns the controls that are selected in the hierarchy panel. These return in a first-in/last-out manner 15 | :param rig: The rig we are looking at 16 | :type rig: unreal.ControlRigBlueprint 17 | :return: A list of the selected object 18 | :rtype: list[unreal.RigElementKey] 19 | """ 20 | 21 | return rig.get_hierarchy_modifier().get_selection() 22 | 23 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, unreal.Name], ret=unreal.RigElementKey, static = True, meta=dict(Category="Control Rig Blueprint Ext")) 24 | def GetIndexByControlName(rig, control_name): 25 | """ 26 | Given a name, returns the associated RigElementKey. 27 | :param rig: The control rig object 28 | :type rig: unreal.ControlRigBlueprint 29 | :param control_name: The path of the key to query 30 | :type control_name: str 31 | :return: The RigElementKeys with the given name, if any 32 | :rtype: unreal.RigElementKey 33 | """ 34 | 35 | hierarchy_mod = rig.get_hierarchy_modifier() 36 | indexes = hierarchy_mod.get_elements() 37 | for ind in indexes: 38 | if ind.name == control_name: 39 | return ind 40 | return None 41 | 42 | @unreal.ufunction(params=[unreal.ControlRigBlueprint], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 43 | def CopyPasteSelectedGlobalXform(rig): 44 | """ 45 | Given a selection, copy the global transform from the first control into the initial transform of the second 46 | control 47 | :param rig: The control rig object 48 | :type rig: unreal.ControlRigBlueprint 49 | :return: Nothing 50 | :rtype: None 51 | """ 52 | 53 | hierarchy_mod = rig.get_hierarchy_modifier() 54 | selection = hierarchy_mod.get_selection() 55 | 56 | if not len(selection) == 2: 57 | 58 | unreal.log("Not enough Control Rig controls selected") 59 | 60 | return 61 | 62 | global_xform = hierarchy_mod.get_global_transform(selection[1]) 63 | hierarchy_mod.set_initial_global_transform(selection[0], global_xform) 64 | 65 | @unreal.ufunction(params = [unreal.ControlRigBlueprint, unreal.RigElementType], ret=unreal.Array(unreal.RigElementKey), static = True, meta=dict(Category="Control Rig Blueprint Ext")) 66 | def CreateRigElement(rig, element_type): 67 | """ 68 | Given an element type, create a RigElement of that type. If any RigElement is selected, the new element will have the selected element's global transform as initial. 69 | :param rig: The control rig object 70 | :type rig: unreal.ControlRigBlueprint 71 | :return: elements 72 | :rtype: list[unreal.RigElementKey] 73 | """ 74 | 75 | elements = control_rig_utils.create_rig_element_key(rig, element_type) 76 | 77 | return elements 78 | 79 | @unreal.ufunction(params = [unreal.ControlRigBlueprint, unreal.RigElementKey, unreal.RigElementKey], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 80 | def ParentRigElements(rig, parent, child): 81 | """ 82 | Given 2 rig elements, parent one to the other in the rig hierarchy 83 | :param rig: The control rig object 84 | :type rig: unreal.ControlRigBlueprint 85 | :param parent: The parent element 86 | :type parent: unreal.RigElementKey 87 | :param child: The child element 88 | :type child: unreal.RigElementKey 89 | :return: elements 90 | :rtype: list[unreal.RigElementKey] 91 | """ 92 | 93 | hierarchy_mod = rig.get_hierarchy_modifier() 94 | 95 | hierarchy_mod.reparent_element(child, parent) 96 | 97 | @unreal.ufunction(params = [unreal.ControlRigBlueprint], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 98 | def UpdateSelectedElementsInitialTransfromFromCurrentGlobal(rig): 99 | """ 100 | Get selected rig elements, take the current transforms as the initial transforms 101 | :param rig: The control rig object 102 | :type rig: unreal.ControlRigBlueprint 103 | :return: Nothing 104 | :rtype: None 105 | """ 106 | 107 | hierarchy_mod = rig.get_hierarchy_modifier() 108 | selection = hierarchy_mod.get_selection() 109 | 110 | for item in selection: 111 | 112 | updated_xform = hierarchy_mod.get_global_transform(item) 113 | 114 | hierarchy_mod.set_initial_global_transform(item, updated_xform) 115 | 116 | @unreal.ufunction(params = [unreal.ControlRigBlueprint], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 117 | def ZeroSelectedElementsInitialTransfrom(rig): 118 | """ 119 | Set selected rig elements' intial transform to default 120 | :param rig: The control rig object 121 | :type rig: unreal.ControlRigBlueprint 122 | :return: Nothing 123 | :rtype: None 124 | """ 125 | 126 | hierarchy_mod = rig.get_hierarchy_modifier() 127 | selection = hierarchy_mod.get_selection() 128 | 129 | for item in selection: 130 | 131 | updated_xform = unreal.Transform(location = [0,0,0], rotation = [0,0,0], scale = [1,1,1]) 132 | 133 | hierarchy_mod.set_initial_global_transform(item, updated_xform) 134 | 135 | @unreal.ufunction(params=[unreal.ControlRigBlueprint], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 136 | def CopyPasteSelectedGizmos(rig): 137 | """ 138 | Copy the first selected control gizmo attributes and paste it to the second control. 139 | :param rig: The control rig object 140 | :type rig: unreal.ControlRigBlueprint 141 | :return: Nothing 142 | :rtype: None 143 | """ 144 | 145 | hierarchy_mod = rig.get_hierarchy_modifier() 146 | selection = hierarchy_mod.get_selection() 147 | 148 | if not len(selection) == 2: 149 | 150 | return 151 | 152 | src_element = control_rig_utils.cast_key_to_type(rig, selection[1]) 153 | dst_element = control_rig_utils.cast_key_to_type(rig, selection[0]) 154 | 155 | if type(src_element) != unreal.RigControl and type(dst_element) != unreal.RigControl: 156 | 157 | return 158 | 159 | gizmo_name = src_element.get_editor_property("gizmo_name") 160 | gizmo_color = src_element.get_editor_property("gizmo_color") 161 | gizmo_transform = src_element.get_editor_property("gizmo_transform") 162 | gizmo_enabled = src_element.get_editor_property("gizmo_enabled") 163 | 164 | dst_element.set_editor_property("gizmo_name", gizmo_name) 165 | dst_element.set_editor_property("gizmo_color", gizmo_color) 166 | dst_element.set_editor_property("gizmo_transform", gizmo_transform) 167 | dst_element.set_editor_property("gizmo_enabled", gizmo_enabled) 168 | 169 | hierarchy_mod.set_control(dst_element) 170 | 171 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, unreal.LinearColor], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 172 | def EditGizmoColor(rig, color): 173 | """ 174 | Given a color, set selected controls' gizmo to that color 175 | :param rig: The control rig object 176 | :type rig: unreal.ControlRigBlueprint 177 | :param color: The color object 178 | :type color: unreal.LinearColor 179 | :return: Nothing 180 | :rtype: None 181 | """ 182 | 183 | hierarchy_mod = rig.get_hierarchy_modifier() 184 | selection = hierarchy_mod.get_selection() 185 | 186 | rig_elements = control_rig_utils.get_elements_by_rig_type(rig, selection, unreal.RigControl) 187 | 188 | for rig_element in rig_elements: 189 | 190 | rig_element.set_editor_property("gizmo_color", color) 191 | hierarchy_mod.set_control(rig_element) 192 | 193 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, unreal.Transform()], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 194 | def EditGizmoTransform(rig, transform): 195 | """ 196 | Given a transform, set selected controls' gizmo to that color 197 | :param rig: The control rig object 198 | :type rig: unreal.ControlRigBlueprint 199 | :param transform: The transform object 200 | :type transform: unreal.Transform 201 | :return: Nothing 202 | :rtype: None 203 | """ 204 | 205 | hierarchy_mod = rig.get_hierarchy_modifier() 206 | selection = hierarchy_mod.get_selection() 207 | 208 | rig_elements = control_rig_utils.get_elements_by_rig_type(rig, selection, unreal.RigControl) 209 | 210 | for rig_element in rig_elements: 211 | 212 | rig_element.set_editor_property("gizmo_transform", transform) 213 | hierarchy_mod.set_control(rig_element) 214 | 215 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, bool], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 216 | def EditGizmoEnabled(rig, is_enabled): 217 | """ 218 | Given a boolean, set selected controls' gizmo to that color 219 | :param rig: The control rig object 220 | :type rig: unreal.ControlRigBlueprint 221 | :param is_enabled: is gizmo enabled 222 | :type is_enabled: bool 223 | :return: Nothing 224 | :rtype: None 225 | """ 226 | 227 | hierarchy_mod = rig.get_hierarchy_modifier() 228 | selection = hierarchy_mod.get_selection() 229 | 230 | rig_elements = control_rig_utils.get_elements_by_rig_type(rig, selection, unreal.RigControl) 231 | 232 | for rig_element in rig_elements: 233 | 234 | rig_element.set_editor_property("gizmo_enabled", is_enabled) 235 | hierarchy_mod.set_control(rig_element) 236 | 237 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, str], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 238 | def EditGizmoName(rig, gizmo_name): 239 | """ 240 | Given a name, set selected controls' gizmo to that name 241 | :param rig: The control rig object 242 | :type rig: unreal.ControlRigBlueprint 243 | :param gizmo_name: Gizmo name 244 | :type gizmo_name: str 245 | :return: Nothing 246 | :rtype: None 247 | """ 248 | 249 | hierarchy_mod = rig.get_hierarchy_modifier() 250 | selection = hierarchy_mod.get_selection() 251 | 252 | rig_elements = control_rig_utils.get_elements_by_rig_type(rig, selection, unreal.RigControl) 253 | 254 | for rig_element in rig_elements: 255 | 256 | rig_element.set_editor_property("gizmo_name", gizmo_name) 257 | hierarchy_mod.set_control(rig_element) 258 | 259 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, str, str], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 260 | def SearchAndReplaceRigElementNames(rig, search_string, replace_string): 261 | """ 262 | Given a string, search for it in the selected rig elements' name 263 | and replace it with another string in the selected rig elements' name 264 | :param rig: The control rig object 265 | :type rig: unreal.ControlRigBlueprint 266 | :param search_string: search for string 267 | :type search_string: string 268 | :param replace_string: string for replace 269 | :type replace_string: string 270 | :return: Nothing 271 | :rtype: None 272 | """ 273 | 274 | hierarchy_mod = rig.get_hierarchy_modifier() 275 | selection = hierarchy_mod.get_selection() 276 | 277 | if not selection: 278 | 279 | return 280 | 281 | for item in selection: 282 | 283 | src_name = str(item.get_editor_property("name")) 284 | 285 | new_name = src_name.replace(search_string, replace_string) 286 | 287 | hierarchy_mod.rename_element(item, new_name) 288 | 289 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, str, bool], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 290 | def AddStringPrefixOrSuffixToSelected(rig, insert_text, is_suffix): 291 | """ 292 | Given a string, insert it at the start or end of the selected rig elements' name 293 | :param rig: The control rig object 294 | :type rig: unreal.ControlRigBlueprint 295 | :param insert_text: the insert string 296 | :type insert_text: string 297 | :param is_suffix: adding it to the end or start? 298 | :type is_suffix: bool 299 | :return: Nothing 300 | :rtype: None 301 | """ 302 | 303 | hierarchy_mod = rig.get_hierarchy_modifier() 304 | selection = hierarchy_mod.get_selection() 305 | 306 | if not selection: 307 | 308 | return 309 | 310 | for item in selection: 311 | 312 | src_name = str(item.get_editor_property("name")) 313 | 314 | new_name = "{0}_{1}".format(insert_text, src_name) 315 | 316 | if is_suffix: 317 | 318 | new_name = "{0}_{1}".format(src_name, insert_text) 319 | 320 | hierarchy_mod.rename_element(item, new_name) 321 | 322 | @unreal.ufunction(params=[unreal.ControlRigBlueprint, str, int, int], static = True, meta=dict(Category="Control Rig Blueprint Ext")) 323 | def RenameAndNumberSelectedControls(rig, name, start_number, number_padding): 324 | """ 325 | Given a name, start number, and number padding, set selected rig elements' name to a newly created name 326 | :param rig: The control rig object 327 | :type rig: unreal.ControlRigBlueprint 328 | :param name: replacement name 329 | :type name: string 330 | :param start_number: start number for numberring items 331 | :type start_number: int 332 | :param number_padding: this many digits padded for text 333 | :type number_padding: int 334 | :return: Nothing 335 | :rtype: None 336 | """ 337 | 338 | hierarchy_mod = rig.get_hierarchy_modifier() 339 | selection = hierarchy_mod.get_selection() 340 | 341 | if not selection: 342 | 343 | return 344 | 345 | x = start_number 346 | 347 | for item in selection: 348 | 349 | new_name = "{0}_{1}".format(name, str(x).zfill(number_padding)) 350 | 351 | hierarchy_mod.rename_element(item, new_name) 352 | 353 | x+=1 354 | 355 | @unreal.ufunction(params=[unreal.ControlRigBlueprint], ret=unreal.Array(str), static = True, meta=dict(Category="Control Rig Blueprint Ext")) 356 | def GetControlGizmoListFromRig(rig): 357 | """ 358 | Get a list of gizmo names from rig 359 | :param rig: The control rig object 360 | :type rig: unreal.ControlRigBlueprint 361 | :return: Nothing 362 | :rtype: None 363 | """ 364 | 365 | gizmo_library = rig.get_editor_property("gizmo_library") 366 | 367 | gizmos = gizmo_library.get_editor_property("gizmos") 368 | 369 | gizmo_names = [] 370 | 371 | for gizmo in gizmos: 372 | 373 | gizmo_names.append(gizmo.get_editor_property("gizmo_name")) 374 | 375 | return gizmo_names 376 | 377 | print("Control Rig Ext Loaded") --------------------------------------------------------------------------------