ImGuiの導入

ImGuiとは

ImGuiとは,楽に組み込みができて,簡易的なGUIを提供するライブラリです。

https://github.com/ocornut/imgui

例えば,このようなコードを書くと,

ImGui::Begin("Stats", nullptr, window_flags);
ImGui::LabelText("", "FPS:%4.2f (%4.2f ms)", s_Fps);
ImGui::LabelText("", "Camera Pos:(%.2f, %.2f, %.2f )", cameraPosition.x, cameraPosition.y, cameraPosition.z);
ImGui::Spacing();
ImGui::SetNextTreeNodeOpen(true, ImGuiSetCond_Once);
if (ImGui::CollapsingHeader("PostEffect"))
{
    if (ImGui::TreeNode("Tonemap"))
    {
        ImGui::SliderFloat("Gamma", &pfxDesc.Tonemap.Gamma, 0.0f, 4.0f);
        // ...
        ImGui::TreePop();
    }
    // ...
    ImGui::TreePop();
}
//...
ImGui::End();

ImGui::Render();

こんな感じでGUIが作れます。

f:id:hikita12312:20171015113931p:plain:w600

手続き的にImGuiのnamespaceのAPIをコールするだけで,それっぽいGUIが出来上がるのでとても便利です。

ImGuiの組み込み方法

ソースコードを持ってくる

ImGuiのリポジトリからソースを持ってきて,自分のプロジェクトに必要なソースとヘッダを組み込みます。 必要なファイルは

  • imgui.h
  • imgui.cpp
  • imgui_draw.cpp
  • imgui_internal.h
  • stb_rect_pack.h
  • stb_textedit.h
  • stb_truetype.h

です。自分のライブラリに組み込むような場合は,imgui.hのみ公開ヘッダとすれば大丈夫かと思います。

プラットフォームに合わせた実装をする

(簡単) examplesを利用する方法

ImGuiを利用するためには,描画部分とマウスやキーボードの入力部分を実装しなければいけません。
その実装例がImGuiのリポジトリのexamplesディレクトリ以下に格納されているので,面倒ならばこのサンプルをそのまま使っても良いと思います。

例としてDX11版の実装を見てみます。 imgui_impl_dx11.hを見ると,

IMGUI_API bool        ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context);
IMGUI_API void        ImGui_ImplDX11_Shutdown();
IMGUI_API void        ImGui_ImplDX11_NewFrame();
IMGUI_API void        ImGui_ImplDX11_InvalidateDeviceObjects();
IMGUI_API bool        ImGui_ImplDX11_CreateDeviceObjects();

というAPIが定義されていて,Init()で初期化を行い,デバイスの作成・破棄が行われたらInvalidateDeviceObjects(),CreateDeviceObjects()をコールします。 ImGui名前空間APIをコールする前には,NewFrame()を呼ぶ必要があります。また,WindowsAPIのイベントを取得するために,

IMGUI_API LRESULT   ImGui_ImplDX11_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

ImGui_ImplDX11_WndProcHandler()をグローバルな名前空間にexternで定義しておいて,組み込み先のアプリケーションのWNDPROCのコールバック内部でコールするようにします。

これだけで,とりあえずは自分のプロジェクトでImGuiを動作させるところまでは持っていくことができます。 DX11以外のプラットフォームでも同じような感じで組み込めるかと思います。

真面目に組み込む方法

  • examplesに自分の使いたいプラットフォームが無い
  • 独自に抽象化されたフレームワークを利用している,もしくは作成している

という場合には,自分で真面目にImGui::GetIO()を利用してImGuiの描画部分と入力部分を実装する必要があります。 これもexampleのサンプルコードの中身を参考にすれば良いです。 ImGuiIO::KeyMapやImGuiIO::MousePosなどに入力値を格納しておけばImGui内部で利用する入力として利用してくれます。

描画部分は,ImGuiIO::RenderDrawListsFnにImGui内部で利用するImDrawData構造体のを引数に,描画処理を行うコールバックを実装する必要があります。 ImDrawDataには描画に利用する頂点データや行列変換の定数バッファが格納されていて,これを利用して描画を行うことになります。 そのために,ImGui利用前にプラットフォーム固有のシェーダーや,Font用のテクスチャを作成する必要があります。

ひっかかり
シェーダーリフレクションを利用してシェーダーコードからInputLayoutを作っていたので, ImGuiの組み込みをしたときに,渡される頂点バッファのColor部分のデータが8bitUNORMのフォーマットだったことに少し躓きました。 DX11の場合はリフレクションで8bitフォーマットかどうかはわからないので,手動でInputLayoutを作成する必要があります。