imgui で GUI を作るときのメモ


imgui でこんなのを実現したいときはどうしたらいいかしらん... のメモです.

対象 ImGui version

1.71(or 1.72 WIP)

Docking window にしたい.

ImGui はデフォルトではフリーレイアウトです.

docking branch でドッキング対応が作業中です. 使っている感じではほぼ問題なく動いています.

ImGuiConfigFlags_DockingEnable を指定すると自動でドッキングが有効になります.

  ImGui::CreateContext();
  auto& io = ImGui::GetIO();

  // Enable docking(available in imgui `docking` branch at the moment)
  io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;

など外部の docking 対応機能も試しましたが, 不都合が多いでした(ので, ImGui docking branch がおすすめです)

ノードエディタがほしい

C++14 になりますが, imgui-node-editor がいい感じです.

3D ギズモがほしい

Maya とか Blender とかで, translate/rotate/scale で出てくるマニピュレータです.

ImGuizmo でいけます.

カラーピッカーのカラースペースは?

現状 ImGui は degamma/ガンマ補正などは考慮していないようです.

レンダラ用の UI を作る場合. sRGB/Linear 対応などを自前対応が必要になります.

文字に背景色を指定したい

直接背景色を指定する API はありません.
AddRectFilled() などで一旦矩形を描画したのち, 文字を描画します.

    ImVec2 bmin = //テキストの描画位置. ImGui::GetCursorScreenPos() などで取得

    ImVec2 text_size = ImGui::CalcTextSize(buf);

    ImVec2 fill_bmin = ImVec2(bmin.x - 4, bmin.y - 4);
    ImVec2 fill_bmax =
            ImVec2(bmin.x + text_size.x + 4, bmin.y + text_size.y + 4);

    // Draw quad for background color
    ImGui::GetWindowDrawList()->AddRectFilled(
            fill_bmin, fill_bmax,
            ImGui::GetColorU32(ImVec4(0.2f, 0.2f, 0.2f, 0.4f * alpha)),
            /* rounding */ 4.0f);


    ImGui::SetCursorScreenPos(bmin);

    ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, alpha), "%s", buf);

フォントをソースコードに埋め込みたい

.ttf ファイルを読み込んでだと, ファイルの探索に失敗したりとかで管理が面倒なのでバイナリに埋め込みたい.

こちらでフォントデータを unsigned int の配列にしてくれます. 圧縮/非圧縮選べます.

  io.Fonts->AddFontFromMemoryCompressedTTF(roboto_mono_compressed_data,
                                           roboto_mono_compressed_size,
                                           default_font_scale, &roboto_config);

などとしてメモリからフォントデータを読み込みます.

形状を描画したい.

Sphere, line, etc.. など.

DrawList を取得して, AddRect() などを呼びます.

より複雑な形状を描画したり, antialiased line が欲しければ NanoVG あたりを使うのがよいかもれません.

大量のオブジェクトを描画したい

Imgui ではデフォルトでは頂点インデックスは 16bit のため, 大量にオブジェクトを描画するとインデックスが足りない assert が出たりします.

Omar 先生からアドバイスいただきました. ありがとうございます.

1.71 から実装された VtxOffset feature を使うと 16bit index でも大量プリミティブを描画できるようになります. example の GL3 backend では VtxOffset 機能がデフォルトで有効になっています(glDrawElementsBaseVertex() を使っている)

OpenGL 2.x や, GLES では, glDrawElementsBaseVertex 相当を CPU 実装することで対応できるでしょう.

他に, imconfig.h でインデックスを 32bit にしたり, 自前描画や NanoVG を使うのも考えてみましょう.

画像を表示したい

ImGui::Image でいけます.

OpenGL で描画する場合, たとえば 8K 画像を一旦 GL テクスチャにするところでかなり時間がかかったりします(e.g. ドライバ内部とか).

サムネイル表示ができればよいのであれば, 一度画像を縮小してからテクスチャを作るのがよいです.

画像だけスクロールさせたい 

画像が大きかったり, 拡大表示したいので Window に入りきらない.
デフォルトでは Window 全体にスクロールバーがつくので, 他の text や button などもスクロールしてしまう.
Window に画像(Image)をいれ, Image だけスクロールバーをつけたい.

=> ChildWindow でとりあえずできはしますが, macOS だと拡大が大きくなるとおかしくなるのを観測しました. 安定を求めるのであれば, 現状は画像ごとに Window を作るしかなさそうです.

InputFloat などで, リターン打ったときのみアップデートしたい.

デフォルトでは数値を入力するたびに return true(update)になります.
たとえばカメラ位置などを扱っているばあい,

1.0
->
0
->
0.5

みたいに値を削って 0.5 にしたいときに 0.0 の状態でもアップデート要求がでて, レンダーし直しになって面倒です.

InputTextFlags で

ImGuiInputTextFlags_EnterReturnsTrue

を指定することで, リターンを打ったときのみ true が帰るようにできます.

ただ, decimal precision も指定する必要がでてきますので注意です.

オシャンティな progress 表示したい