Custom Asset Importers & Drag-Drop Handlers
Overview
This example demonstrates: - Custom asset importers: Handle file imports for custom extensions - Drag-drop handlers: React to drag-and-drop events in specific editor areas - Pre/post import hooks: Intercept the import pipeline
Files
package.json
{
"name": "Asset Pipeline Addon",
"author": "Polyphase Examples",
"description": "Custom asset importers and drag-drop handlers.",
"version": "1.0.0",
"tags": ["editor", "example"],
"native": {
"target": "editor",
"sourceDir": "Source",
"binaryName": "assetpipelineaddon",
"apiVersion": 2
}
}
Source/AssetPipelineAddon.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 Custom importer for .csv files.
* @param filePath Full path to the file being imported.
* @param extension File extension (e.g., ".csv").
* @param userData User data (unused).
* @return true if import was handled, false to fall back to default.
*/
static bool ImportCSV(const char* filePath, const char* extension, void* userData)
{
sEngineAPI->LogDebug("Importing CSV file: %s", filePath);
// Parse CSV, create data asset, etc.
return true; // Handled
}
/**
* @brief Custom importer for .json data files.
*/
static bool ImportJSON(const char* filePath, const char* extension, void* userData)
{
sEngineAPI->LogDebug("Importing JSON file: %s", filePath);
return true;
}
/**
* @brief Custom drag-drop handler for the viewport.
* @param payloadType ImGui payload type string.
* @param payloadData Pointer to the payload data.
* @param payloadSize Size of the payload in bytes.
* @param userData User data (unused).
* @return true if the drop was consumed, false to pass to next handler.
*/
static bool HandleViewportDrop(const char* payloadType, const void* payloadData,
int32_t payloadSize, void* userData)
{
sEngineAPI->LogDebug("Viewport drop: type=%s, size=%d", payloadType, payloadSize);
// Handle the drop (e.g., spawn a node from the dropped asset)
return false; // Let other handlers process it too
}
/**
* @brief Pre-import hook - called before any asset is imported.
* @param filePath Path of the file about to be imported.
* @param userData User data (unused).
* @return true to allow import, false to cancel it.
*/
static bool OnPreImport(const char* filePath, void* userData)
{
sEngineAPI->LogDebug("Pre-import: %s", filePath);
// Return false to cancel the import
return true;
}
/**
* @brief Post-import hook - called after an asset is imported.
* @param assetPath Path of the imported asset.
* @param userData User data (unused).
*/
static void OnPostImport(const char* assetPath, void* userData)
{
sEngineAPI->LogDebug("Post-import: %s", assetPath);
}
#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)
{
// Register custom importers for file extensions
hooks->RegisterAssetImporter(hookId, ".csv", ImportCSV, nullptr);
hooks->RegisterAssetImporter(hookId, ".json", ImportJSON, nullptr);
// Register drag-drop handler for viewport
hooks->RegisterDragDropHandler(hookId, "Viewport", HandleViewportDrop, nullptr);
// Pre/post import hooks
hooks->RegisterOnPreAssetImport(hookId, OnPreImport, nullptr);
hooks->RegisterOnPostAssetImport(hookId, OnPostImport, nullptr);
}
#endif
extern "C" OCTAVE_PLUGIN_API int PolyphasePlugin_GetDesc(PolyphasePluginDesc* desc)
{
desc->apiVersion = OCTAVE_PLUGIN_API_VERSION;
desc->pluginName = "Asset Pipeline 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;
}
API Reference
RegisterAssetImporter
void (*RegisterAssetImporter)(HookId hookId, const char* extension,
AssetImportCallback importFunc, void* userData);
Register a handler for a file extension. Return true from callback if handled.
RegisterDragDropHandler
void (*RegisterDragDropHandler)(HookId hookId, const char* targetArea,
DragDropHandlerCallback handler, void* userData);
targetArea values: "Viewport", "Hierarchy", "AssetBrowser", "Inspector"
RegisterOnPreAssetImport
void (*RegisterOnPreAssetImport)(HookId hookId, PreImportCallback cb, void* userData);
Called before import. Return false to cancel the import.
RegisterOnPostAssetImport
void (*RegisterOnPostAssetImport)(HookId hookId, StringEventCallback cb, void* userData);
Called after import with the imported asset path.
Best Practices
- Return Values Matter - Importers and drag-drop handlers use return values to indicate handling
- Extension Format - Include the dot (e.g.,
".csv"not"csv") - Pre-Import Validation - Use
RegisterOnPreAssetImportto validate or transform files before import - Non-Destructive - Don't modify the original source file during import