追記
OpenSiv3D用のimgui_implの実装が公開されています。
https://twitter.com/telyaino/status/1209112739110514689
はじめに
OpenSiv3Dで簡単にGUIを作りたいと思ったことはありませんか?
この記事ではImGuiというGUIライブラリを組み込む方法を解説します。
nuklearというGUIライブラリを使った例は、既に記事が書かれていますのでここで紹介しておきます。
OpenSiv3D+nuklearでGUIを実現する
ImGuiとは
https://github.com/ocornut/imgui
詳しくはリポジトリを見てもらうのが早いですが、簡潔にまとめると、直感的なコードでGUIを構築できる、マルチプラットフォームなGUIライブラリです。
環境
この記事の内容は以下のバージョンを元に解説しています。
- OpenSiv3D 0.3.1
- ImGui 1.66
準備
ImGuiのフォルダからそのまま参照する場合、プロジェクトに含めて、インクルードディレクトリに指定すれば、以下の手順は必要ありません。
-
include/ThirdParty
にimgui
フォルダーを作成する。 - 以下のファイルを
include/ThirdParty/imgui
にコピーする。
- imgui.h
- imgui_internal.h
- imconfig.h
- misc/cpp/imgui_stdlib.h1
-
include/ThirdParty
にimgui
フィルターを作成する。 -
include/ThirdParty/imgui
フィルターにinclude/ThirdParty/imgui
フォルダーの中身を全て追加する。
-
src/ThirdParty
にimgui
フォルダーを作成する。 - 以下のファイルを
src/ThirdParty/imgui
にコピーする
- imgui.cpp
- imgui_demo.cpp
- imgui_draw.cpp
- imgui_widgets.cpp
- imstb_rectpack.h
- imstb_textedit.h
- imstb_truetype.h
- examples/imgui_impl_dx11.cpp
- examples/imgui_impl_dx11.h
- examples/imgui_impl_win32.cpp
- examples/imgui_impl_win32.h
- misc/cpp/imgui_stdlib.cpp1
-
src/ThirdParty
にimgui
フィルターを作成する。 -
src/ThirdParty/imgui
フィルターにsrc/ThirdParty/imgui
フォルダーの中身を全て追加する。 -
以下のソースのインクルードディレクトリを修正する。
- imgui.cpp
- imgui_demo.cpp
- imgui_draw.cpp
- imgui_impl_dx11.cpp
- imgui_impl_win32.cpp
- imgui_stdlib.cpp
- imgui_widgets.cpp
#include "imgui.h"
→ #include <imgui/imgui.h>
#include "imgui_internal.h"
→ #include <imgui/imgui_internal.h>
#include "imgui_stdlib.h"
→ #include <imgui/imgui_stdlib.h>
実装
d3dcompilerをリンクする。
直近のコミットで本家に追記されたため、今後のリリースではこの手順は必要無いと思われます。
https://github.com/ocornut/imgui/commit/8d58055a5433e1227ebb5e3b8dba80bb00cc239a
#include <stdio.h>
#include <d3d11.h>
#include <d3dcompiler.h>
+ #pragma comment(lib, "d3dcompiler")
初期化とフレーム開始の処理を追記する。
# include <Siv3D/Cursor.hpp>
# include <Siv3D/Logger.hpp>
+ #include <imgui/imgui.h>
+ #include <imgui/imgui_impl_win32.h>
+ #include <imgui/imgui_impl_dx11.h>
+ #include "../Graphics/D3D11/CGraphics_D3D11.hpp"
if (!Siv3DEngine::GetAsset()->init())
{
return false;
}
+ ImGui::CreateContext();
+ ImGui_ImplWin32_Init(Siv3DEngine::GetWindow()->getHandle());
+ CGraphics_D3D11* graphics = dynamic_cast<CGraphics_D3D11*>(Siv3DEngine::GetGraphics());
+ ImGui_ImplDX11_Init(graphics->getDevice(), graphics->getContext());
m_setupState = SetupState::Initialized;
LOG_INFO(U"✅ Siv3D engine setup completed");
return true;
}
bool CSystem_Windows::update(bool clearGraphics)
{
if (!m_updateSucceeded)
{
return false;
}
m_updateSucceeded = false;
if (m_setupState == SetupState::Initialized)
{
Siv3DEngine::GetWindow()->show(true);
+ ImGui_ImplDX11_NewFrame();
+ ImGui_ImplWin32_NewFrame();
+ ImGui::NewFrame();
m_setupState = SetupState::Displayed;
Siv3DEngine::GetCursor()->applyStyleImmediately(CursorStyle::Default);
}
if (!Siv3DEngine::GetProfiler()->beginFrame())
{
return false;
}
+ ImGui_ImplDX11_NewFrame();
+ ImGui_ImplWin32_NewFrame();
+ ImGui::NewFrame();
const bool onDeviceChange = m_onDeviceChange.exchange(false);
フレーム終了(レンダリング)の処理を追記する。
# include "../../Siv3DEngine.hpp"
# include "CGraphics_D3D11.hpp"
+ #include <imgui/imgui.h>
+ #include <imgui/imgui_impl_dx11.h>
bool CGraphics_D3D11::flush(bool clearGraphics)
{
m_renderer2D->flush(clearGraphics);
+ ImGui::Render();
+ ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
m_renderTarget->resolve();
return true;
}
アプリ終了時の処理を追記する。
# include "Script/IScript.hpp"
# include "Asset/IAsset.hpp"
+ #include <imgui/imgui.h>
+ #include <imgui/imgui_impl_win32.h>
+ #include <imgui/imgui_impl_dx11.h>
Siv3DEngine::~Siv3DEngine()
{
+ ImGui_ImplDX11_Shutdown();
+ ImGui_ImplWin32_Shutdown();
+ ImGui::DestroyContext();
m_asset.release();
m_script.release();
ウィンドウプロシージャを編集する。
ImGuiにウィンドウメッセージを渡し、Siv3Dのテキスト入力処理を削除する。(コメントアウトで構いません)
# include "../TextInput/ITextInput.hpp"
# include "../TextInput/CTextInput_Windows.hpp"
+ #include <imgui/imgui.h>
+ #include <imgui/imgui_impl_win32.h>
+ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace s3d
{
namespace detail
{
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
+ if (ImGui_ImplWin32_WndProcHandler(hWnd, message, wParam, lParam)) {
+ return 1;
+ }
- if (auto textinput = dynamic_cast<CTextInput_Windows*>(Siv3DEngine::GetTextInput()))
- {
- if (textinput->process(message, wParam, &lParam))
- {
- return 0;
- }
- }
case WM_CHAR:
{
- Siv3DEngine::GetTextInput()->pushChar(static_cast<uint32>(wParam));
return 0;
}
case WM_UNICHAR:
{
if (wParam == UNICODE_NOCHAR)
{
return true;
}
- Siv3DEngine::GetTextInput()->pushChar(static_cast<uint32>(wParam));
return 0;
}
ライブラリを使う
まずは通常通り、OpenSiv3Dのプロジェクトを作成します。
その後、インクルードディレクトリやライブラリディレクトリを、ここでクローンしたディレクトリに変更してください。
サンプル
デモウィンドウの表示
# include <Siv3D.hpp> // OpenSiv3D v0.3.1
# include <imgui/imgui.h>
void Main()
{
Graphics::SetBackground(ColorF(0.8, 0.9, 1.0));
while (System::Update())
{
ImGui::ShowDemoWindow();
}
}
日本語入力
任意のフォントファイルをフォルダに配置してから実行してください。(この例ではmplus-1p-regular.ttfを配置しています)
範囲の指定に
io.Fonts->GetGlyphRangesJapanese()
等がありますが、以下のように全範囲を指定しても問題無いようです。
# include <Siv3D.hpp> // OpenSiv3D v0.3.1
# include <imgui/imgui.h>
# include <imgui/imgui_stdlib.h>
void Main()
{
Graphics::SetBackground(ColorF(0.8, 0.9, 1.0));
ImGuiIO& io = ImGui::GetIO();
static const ImWchar ranges[] = { 0x0020, 0xFFFF, 0 };
io.Fonts->AddFontFromFileTTF("mplus-1p-regular.ttf", 20.0f, NULL, &ranges[0]);
std::string text;
while (System::Update())
{
ImGui::InputText(u8"テキスト", &text);
}
}
既知の不具合
変換候補がキャレットの位置ではない場所に表示される。