Extending the Add Node Menu
Overview
This example demonstrates how to extend the "Add Node" submenu that appears in the hierarchy context menu and World > Spawn Node. You can add items to existing categories (3D, Widget, Other) or create entirely new categories.
Files
package.json
{
"name": "Custom Node Types Addon",
"author": "Polyphase Examples",
"description": "Adds custom node types to the Add Node menu.",
"version": "1.0.0",
"tags": ["editor", "example"],
"native": {
"target": "editor",
"sourceDir": "Source",
"binaryName": "customnodetypesaddon",
"apiVersion": 2
}
}
Source/CustomNodeTypesAddon.cpp
#include "Plugins/PolyphasePluginAPI.h"
#include "Plugins/PolyphaseEngineAPI.h"
#if EDITOR
#include "Plugins/EditorUIHooks.h"
#include "imgui.h"
#endif
static PolyphaseEngineAPI* sEngineAPI = nullptr;
#if EDITOR
/**
* @brief Adds items to the "3D" category of the Add Node menu.
* @param parentNode The node to parent the new node under (may be null).
* @param userData User data (unused).
*/
static void DrawCustom3dNodes(void* parentNode, void* userData)
{
if (ImGui::MenuItem("Waypoint3D"))
{
sEngineAPI->LogDebug("Spawning Waypoint3D node");
}
if (ImGui::MenuItem("TriggerVolume3D"))
{
sEngineAPI->LogDebug("Spawning TriggerVolume3D node");
}
}
/**
* @brief Creates a new "AI" category in the Add Node menu.
* @param parentNode The node to parent the new node under (may be null).
* @param userData User data (unused).
*/
static void DrawAINodes(void* parentNode, void* userData)
{
if (ImGui::MenuItem("NavMeshAgent"))
{
sEngineAPI->LogDebug("Spawning NavMeshAgent node");
}
if (ImGui::MenuItem("AIController"))
{
sEngineAPI->LogDebug("Spawning AIController node");
}
if (ImGui::MenuItem("BehaviorTree"))
{
sEngineAPI->LogDebug("Spawning BehaviorTree node");
}
}
/**
* @brief Adds items to the "Create Asset" submenu in the asset browser.
* @param parentNode Unused for asset creation.
* @param userData User data (unused).
*/
static void DrawCustomAssetTypes(void* parentNode, void* userData)
{
if (ImGui::MenuItem("AI Behavior"))
{
sEngineAPI->LogDebug("Creating AI Behavior asset...");
}
if (ImGui::MenuItem("Dialogue Tree"))
{
sEngineAPI->LogDebug("Creating Dialogue Tree asset...");
}
}
#endif
static int OnLoad(PolyphaseEngineAPI* api)
{
sEngineAPI = api;
return 0;
}
static void OnUnload()
{
sEngineAPI = nullptr;
}
#if EDITOR
static void RegisterEditorUI(EditorUIHooks* hooks, uint64_t hookId)
{
// Add items to existing "3D" category
hooks->AddNodeMenuItems(hookId, "3D", DrawCustom3dNodes, nullptr);
// Create a new "AI" category submenu
hooks->AddNodeMenuItems(hookId, "AI", DrawAINodes, nullptr);
// Extend the Create Asset submenu
hooks->AddCreateAssetItems(hookId, DrawCustomAssetTypes, nullptr);
}
#endif
extern "C" OCTAVE_PLUGIN_API int PolyphasePlugin_GetDesc(PolyphasePluginDesc* desc)
{
desc->apiVersion = OCTAVE_PLUGIN_API_VERSION;
desc->pluginName = "Custom Node Types Addon";
desc->pluginVersion = "1.0.0";
desc->OnLoad = OnLoad;
desc->OnUnload = OnUnload;
desc->Tick = nullptr;
desc->TickEditor = nullptr;
desc->RegisterTypes = nullptr;
desc->RegisterScriptFuncs = nullptr;
#if EDITOR
desc->RegisterEditorUI = RegisterEditorUI;
#else
desc->RegisterEditorUI = nullptr;
#endif
desc->OnEditorPreInit = nullptr;
desc->OnEditorReady = nullptr;
return 0;
}
Result
The Add Node menu will look like:
Node
3D >
(built-in 3D nodes)
Waypoint3D <-- addon item
TriggerVolume3D <-- addon item
Widget >
(built-in widgets)
AI > <-- new custom category
NavMeshAgent
AIController
BehaviorTree
Other >
(built-in other nodes)
The Create Asset submenu will have:
Create Asset >
(built-in asset types)
AI Behavior <-- addon item
Dialogue Tree <-- addon item
API Reference
AddNodeMenuItems
void (*AddNodeMenuItems)(HookId hookId, const char* category,
MenuSectionDrawCallback drawFunc, void* userData);
Parameters:
- hookId - The hook identifier
- category - Category name: "3D", "Widget", "Other", or any custom name for a new submenu
- drawFunc - Callback: void drawFunc(void* parentNode, void* userData)
- userData - Optional user data
AddCreateAssetItems
void (*AddCreateAssetItems)(HookId hookId, MenuSectionDrawCallback drawFunc, void* userData);
Parameters:
- hookId - The hook identifier
- drawFunc - Callback: void drawFunc(void* parentNode, void* userData)
- userData - Optional user data
AddSpawnBasic3dItems / AddSpawnBasicWidgetItems
void (*AddSpawnBasic3dItems)(HookId hookId, MenuSectionDrawCallback drawFunc, void* userData);
void (*AddSpawnBasicWidgetItems)(HookId hookId, MenuSectionDrawCallback drawFunc, void* userData);
Extends the Spawn Basic 3D/Widget menus (Shift+Q/Shift+W hotkey menus).
Best Practices
- Built-in Categories - Use
"3D","Widget", or"Other"to add items to existing submenus - Custom Categories - Use any other string to create a new submenu section
- Parent Node - The
parentNodeparameter is the node the user right-clicked on; use it to parent new nodes - Node Spawning - In a real addon, call the engine API to spawn nodes instead of just logging