前言
- 有没有人跟我有过同样的想法,觉得
Unity
的imgui
相当繁琐,每次想写点编辑器扩展都得自己写一大堆的东西,本来就一直在网上找找有没有一些造好的轮子可以使用,可惜没有找到。后面在找到dear imgui
的时候,就在想这么牛逼的东西,能集成到unity里面就好了啊,然后还真找到了解决方案,使用unity的原生渲染接口渲染dear imgui
就可以了。
- 有趣的是,因为工作上杂事比较多,这个集成技术测试,却不得不先放一放,这里只是临时作个记录,以方便后续还可以接着研究。所以这里其实是一个未完成的解决方案。
- 我目前只以我现有的环境做记录,暂不考虑兼容任何其他的平台或者抽象API,我的环境是
win10 64位系统
directx11
visual studio 2017
unity 2018.3.f2
参考链接
- dear imgui:https://github.com/ocornut/imgui
- unity 原生渲染接口:Unity Low-level native plug-in interface
功能实现
- 新建c++动态库工程命名
RenderingPlugin
,在unity安装目录下Unity2018.3.0f2\Editor\Data\PluginAPI
找到接口文件,添加到工程中
- 在
RenderingPlugin
按着Unity Low-level native plug-in interface教程添加代码
- 再将
dear imgui
的工程文件添加到RenderingPlugin
工程文件中,同时添加dear imgui
的imgui_impl_win32
&imgui_impl_dx11
,这是我们目前所需要的平台代码
- 然后再添加如下代码(windows编程真心不熟,全靠乱蒙),
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam))
{
return true;
}
return CallWindowProcW(_oldWndProc,hWnd,msg, wParam,lParam);
}
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
SetHwnd(HWND hwnd)
{
_hwnd = hwnd;
_oldWndProc=(WNDPROC)SetWindowLongPtr(_hwnd, GWLP_WNDPROC,(LONG_PTR)WndProc);
auto* D3D = s_UnityInterfaces->Get<IUnityGraphicsD3D11>();
D3D11Device = D3D->GetDevice();
D3D11Device->GetImmediateContext(&D3D11DeviceContext);
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
ImGui_ImplWin32_Init(_hwnd);
ImGui_ImplDX11_Init(D3D11Device, D3D11DeviceContext);
}
static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType)
{
if (eventType == kUnityGfxDeviceEventInitialize)
{
assert(s_CurrentAPI == NULL);
s_DeviceType = s_Graphics->GetRenderer();
s_CurrentAPI = CreateRenderAPI(s_DeviceType);
}
if (s_CurrentAPI)
{
s_CurrentAPI->ProcessDeviceEvent(eventType, s_UnityInterfaces);
}
if (eventType == kUnityGfxDeviceEventShutdown)
{
delete s_CurrentAPI;
s_CurrentAPI = NULL;
s_DeviceType = kUnityGfxRendererNull;
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
D3D11DeviceContext->Release();
ImGui::DestroyContext();
}
}
static void UNITY_INTERFACE_API OnRenderEvent(int eventID)
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::ShowDemoWindow();
ImGui::Begin("Hello, World!");
ImGui::End();
{
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Hello, world!");
ImGui::Text("This is some useful text.");
ImGui::Checkbox("Demo Window", &show_demo_window);
ImGui::Checkbox("Another Window", &show_another_window);
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
ImGui::ColorEdit3("clear color", (float*)&clear_color);
if (ImGui::Button("Button"))
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
}
if (show_another_window)
{
ImGui::Begin("Another Window", &show_another_window);
ImGui::Text("Hello from another window!");
if (ImGui::Button("Close Me"))
show_another_window = false;
ImGui::End();
}
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
- 将
RenderingPlugin
生成的dll放在unity
的Plugin
文件夹下,然后在脚本中添加unity的接口
[DllImport("RenderingPlugin")]
private static extern void SetHwnd(IntPtr hwnd);
IEnumerator Start()
{
IntPtr hwnd = HWND.GetHwnd();
SetHwnd(hwnd);
yield return StartCoroutine("CallPluginAtEndOfFrames");
}
private IEnumerator CallPluginAtEndOfFrames()
{
while (true) {
yield return new WaitForEndOfFrame();
GL.IssuePluginEvent(GetRenderEventFunc(), 1);
}
}
- 运行截图
待扩展
imgui
的脚本扩展,可以支持c#
、python
、lua
等多种选择
imgui
有一个docking
分支,用来写编辑器布局,是非常方便又灵活的
- 最开始就是考虑为扩展unity编辑器而集成
imgui
的,但是因为unity
的EditorWindow
的句柄太不可控了,问题太多,所以先测试了集成在UnityEditor.GameView
,和发布exe
测试,虽然上面有集成在EditorWindow
里的截图,但这里只是一个初步的测试
- 然而就算只为在
UnityEditor.GameView
与发布的exe
集成了imgui
也是有一定问题的,后续改进吧
- 至于源码,由于是测试代码,凌乱不堪,暂时不考虑放出
- 未完待续。。。