Help us understand the problem. What is going on with this article?

OpenSiv3DでImGuiを使う

追記

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

準備

  1. OpenSiv3Dをクローンする。
  2. Dependenciesフォルダーに、要求されているバージョンのboostを配置する。
  3. ImGuiをダウンロードする。

ImGuiのフォルダからそのまま参照する場合、プロジェクトに含めて、インクルードディレクトリに指定すれば、以下の手順は必要ありません。

  1. include/ThirdPartyimguiフォルダーを作成する。
  2. 以下のファイルをinclude/ThirdParty/imguiにコピーする。

    • imgui.h
    • imgui_internal.h
    • imconfig.h
    • misc/cpp/imgui_stdlib.h1
  3. include/ThirdPartyimguiフィルターを作成する。

  4. include/ThirdParty/imguiフィルターにinclude/ThirdParty/imguiフォルダーの中身を全て追加する。

  5. src/ThirdPartyimguiフォルダーを作成する。

  6. 以下のファイルを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
  7. src/ThirdPartyimguiフィルターを作成する。

  8. src/ThirdParty/imguiフィルターにsrc/ThirdParty/imguiフォルダーの中身を全て追加する。

  9. 以下のソースのインクルードディレクトリを修正する。

    • 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

imgui_impl_dx11.cpp
    #include <stdio.h>
    #include <d3d11.h>
    #include <d3dcompiler.h>

+   #pragma comment(lib, "d3dcompiler")

初期化とフレーム開始の処理を追記する。

src/Siv3D/System/CSystem_Windows.cpp
    # 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"
src/Siv3D/System/CSystem_Windows.cpp
        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);
        }

src/Siv3D/System/CSystem_Windows.cpp
        if (!Siv3DEngine::GetProfiler()->beginFrame())
        {
            return false;
        }

+       ImGui_ImplDX11_NewFrame();
+       ImGui_ImplWin32_NewFrame();
+       ImGui::NewFrame();

        const bool onDeviceChange = m_onDeviceChange.exchange(false);

フレーム終了(レンダリング)の処理を追記する。

src/Siv3D/Grapgics/D3D11/CGraphics_D3D11.cpp
    # include "../../Siv3DEngine.hpp"
    # include "CGraphics_D3D11.hpp"

+   #include <imgui/imgui.h>
+   #include <imgui/imgui_impl_dx11.h>
src/Siv3D/Grapgics/D3D11/CGraphics_D3D11.cpp
    bool CGraphics_D3D11::flush(bool clearGraphics)
    {
        m_renderer2D->flush(clearGraphics);

+       ImGui::Render();
+       ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

        m_renderTarget->resolve();

        return true;
    }

アプリ終了時の処理を追記する。

src/Siv3D/Siv3DEngine.cpp
    # include "Script/IScript.hpp"
    # include "Asset/IAsset.hpp"

+   #include <imgui/imgui.h>
+   #include <imgui/imgui_impl_win32.h>
+   #include <imgui/imgui_impl_dx11.h>
src/Siv3D/Siv3DEngine.cpp
    Siv3DEngine::~Siv3DEngine()
    {
+       ImGui_ImplDX11_Shutdown();
+       ImGui_ImplWin32_Shutdown();
+       ImGui::DestroyContext();

        m_asset.release();
        m_script.release();

ウィンドウプロシージャを編集する。
ImGuiにウィンドウメッセージを渡し、Siv3Dのテキスト入力処理を削除する。(コメントアウトで構いません)

src/Siv3D/Window/WindowProc.cpp
    # 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;
-               }
-           }
src/Siv3D/Window/WindowProc.cpp
    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のプロジェクトを作成します。
その後、インクルードディレクトリやライブラリディレクトリを、ここでクローンしたディレクトリに変更してください。

サンプル

デモウィンドウの表示

Main.cpp
# 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()
等がありますが、以下のように全範囲を指定しても問題無いようです。

Main.cpp
# 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);
    }
}

既知の不具合

変換候補がキャレットの位置ではない場所に表示される。


  1. InputTextでstd::stringが使えるようになります。 

rare25
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした