如何在工程插件下扩展材质编辑器

如下图所示,如何在工程插件中,在此处扩展一个新的按钮。[Image Removed]

重现步骤

Hi, [邱 [Content removed]

I understand you want to add a new entry to the context menu that appears when right-clicking a “Texture Sample” node in the Material Graph Editor, under the “Node Actions” section. To extend Unreal Editor menus in UE 5.5, you need to create an Editor Module dependent on the “ToolMenus” module. In the code, you should use the API provided by class UToolMenus.

Here’s a sample extension that adds a menu entry “Custom TextureSample Action” to that menu:

`//-----------------------------//
// MyMaterialEditorExtension.h //
//-----------------------------//

#pragma once

class FMyMaterialEditorExtension
{
public:

static void Startup ();
static void Shutdown ();

private:

static void ExtendTextureSampleNodeContextMenu ();
static void OnCustomTextureSampleAction ();
};

//-------------------------------//
// MyMaterialEditorExtension.cpp //
//-------------------------------//

include “MyMaterialEditorExtension.h”

include “MaterialGraph/MaterialGraphNode.h”
include “Materials/MaterialExpressionTextureSample.h”

static FDelegateHandle Handle_OnMaterialEditorOpened;

void FMyMaterialEditorExtension::Startup ()
{
ExtendTextureSampleNodeContextMenu();
}

void FMyMaterialEditorExtension::Shutdown ()
{
}

void FMyMaterialEditorExtension::ExtendTextureSampleNodeContextMenu ()
{
// Get the extension tool for Editor menus
UToolMenus* ToolMenus = UToolMenus::Get();

// Get the extension tool for the context menu that appears when right-clicking a Material Editor Graph Node (of any type)
UToolMenu* ToolMenu = ToolMenus->ExtendMenu(“GraphEditor.GraphNodeContextMenu.MaterialGraphNode”);

// Get the extension tool for the section “Node Actions”
FToolMenuSection& ToolMenuSection = ToolMenu->FindOrAddSection(“MaterialSchemaNodeActions”);

// Add a dynamic entry (it will be built depending on the context, which includes the actual node that was clicked - and its type)
ToolMenuSection.AddDynamicEntry(“MyTextureSampleNodeEntry”, FNewToolMenuSectionDelegate::CreateLambda((FToolMenuSection& ThisSection)
{
// Get the context object related to the clicked Material Editor Graph Node
UGraphNodeContextMenuContext* Context = ThisSection.FindContext();

// Get the node that was clicked
const UMaterialGraphNode* Node = Cast(Context->Node);
if (!Node)
return;

// Check the type of the material expression represented by the node
if (!Node->MaterialExpression || !Node->MaterialExpression->IsA())
return;

ThisSection.AddMenuEntry(
“CustomTextureSampleAction”,
FText::FromString(“Custom TextureSample Action”),
FText::FromString(“This action only appears on TextureSample nodes.”),
FSlateIcon(),
FUIAction(FExecuteAction::CreateStatic(&FMyMaterialEditorExtension::OnCustomTextureSampleAction))
);
})
);
}

void FMyMaterialEditorExtension::OnCustomTextureSampleAction ()
{
UE_LOG(LogTemp, Log, TEXT(“FMyMaterialEditorExtension::OnCustomTextureSampleAction()”));
}`Your Editor Module class should call into the extension class on its StartupModule() method. If any cleanup is necessary, it should be done on its ShutdownModule() method:

`//------------------//
// MyEditorModule.h //
//------------------//

#pragma once

include “Modules/ModuleManager.h”

class FMyEditorModule : public IModuleInterface
{
public:

virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

//--------------------//
// MyEditorModule.cpp //
//--------------------//

include “MyEditorModule.h”
include “MyMaterialEditorExtension.h”

void FMyEditorModule::StartupModule()
{
FMyMaterialEditorExtension::Startup();
}

void FMyEditorModule::ShutdownModule()
{
FMyMaterialEditorExtension::Shutdown();
}

IMPLEMENT_MODULE(FMyEditorModule, MyEditorModule)`Your Editor Module should also have “Slate”, “SlateCore” and “ToolMenus” added to its PrivateDependencyModuleNames on its ModuleRules class:

`//-------------------------//
// MyEditorModule.Build.cs //
//-------------------------//

using UnrealBuildTool;

public class MyEditorModule : ModuleRules
{
public MyEditorModule (ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

PublicDependencyModuleNames.AddRange( new string {
“Core”,
“CoreUObject”,
“Engine”,
});

PrivateDependencyModuleNames.AddRange( new string {
“Slate”,
“SlateCore”,
“ToolMenus”,
“UnrealEd”,
});
}
}`Note that the extension point is the same for the context menu of any node that is right-clicked in the Material Graph Editor. The extension must manually filter by node type, as shown in the sample code above.

I hope this is helpful. Please let me know if this solution works for you.

Best regards,

Vitor

Thank you so much, you are a hero​

尝试之后发现了新的问题,以上扩展方式对Custom节点都是不生效的,除此之外的其他节点都生效,想问下这里面是有什么需要特殊处理的地方呢?​如下图所示已经去掉了关于节点类型的处理[Image Removed]

Hi, [邱 [Content removed]

Nodes that represent a “UMaterialExpressionCustom” have their own context menu. Simply locate the following line:

UToolMenu* ToolMenu = ToolMenus->ExtendMenu("GraphEditor.GraphNodeContextMenu.MaterialGraphNode");And replace the name of the menu as follows:

UToolMenu* ToolMenu = ToolMenus->ExtendMenu("GraphEditor.GraphNodeContextMenu.MaterialGraphNode_Custom");The rest should work without any further adaptations, except for checking if the MaterialExpression is of type “UMaterialExpressionCustom” instead of “UMaterialExpressionTextureSample”.

Best regards,

Vitor

哦。他是如此的与众不同,这么设计有什么原因吗

That’s likely just an implementation detail. Custom HLSL nodes are implemented differently from other node types internally, so I guess at some point someone had to give that context menu a name and could not reuse the existing one for some reason.