Skip to content

Instantly share code, notes, and snippets.

@sthairno
Last active March 8, 2024 21:39
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sthairno/887740c5ec1a8857e66e30ba179fdc3b to your computer and use it in GitHub Desktop.
Save sthairno/887740c5ec1a8857e66e30ba179fdc3b to your computer and use it in GitHub Desktop.
imgui_impl_s3d
#include <Siv3D.hpp>
#include <imgui.h>
#include "imgui_impl_s3d.h"
#include "DearImGuiAddon.hpp"
/// @brief アドオンの登録時の初期化処理を記述します。
/// @remark この関数が false を返すとアドオンの登録は失敗します。
/// @return アドオンの初期化に成功した場合 true, それ以外の場合は false
bool DearImGuiAddon::init()
{
ImGui::CreateContext();
ImGui_Impls3d_Init();
return true;
}
/// @brief アドオンの毎フレームの更新処理を記述します。
/// @remark この関数が false を返すと `System::Update()` は false を返します。
/// @return アドオンの更新に成功した場合 true, それ以外の場合は false
bool DearImGuiAddon::update()
{
ImGui_Impls3d_NewFrame();
ImGui::NewFrame();
m_firstFrame = false;
return true;
}
/// @brief アドオンの毎フレームの描画処理を記述します。
void DearImGuiAddon::draw() const
{
if (m_firstFrame)
{
return;
}
ImGui::Render();
ImGui_Impls3d_RenderDrawData(::ImGui::GetDrawData());
}
DearImGuiAddon::~DearImGuiAddon()
{
ImGui_Impls3d_Shutdown();
}
#pragma once
#include <imgui.h>
class DearImGuiAddon : public ::s3d::IAddon
{
public:
virtual bool init() override;
virtual bool update() override;
virtual void draw() const override;
virtual ~DearImGuiAddon() override;
private:
bool m_firstFrame = true;
};
#include <imgui.h> // v1.88
#include <Siv3D.hpp> // OpenSiv3D v0.6.5
#include "imgui_impl_s3d.h"
struct ImeWindowContext
{
Vec2 pos;
double lineHeight;
};
struct ImGuiImpls3dContext
{
std::string clipboardData;
uint64 keyDownMinTime = 0;
std::array<uint64, 512> keyDownTimeList;
Texture fontTexture;
Optional<ImeWindowContext> imeWindow;
std::unordered_map < ImTextureID, Texture, decltype([](ImTextureID id) { return reinterpret_cast<size_t>(id); }) > textureDic;
};
const static std::unordered_map<uint8, ImGuiKey> KeyId2ImGuiKeyDic{
{KeyTab.code(), ImGuiKey_Tab},
{KeyLeft.code(), ImGuiKey_LeftArrow},
{KeyRight.code(), ImGuiKey_RightArrow},
{KeyUp.code(), ImGuiKey_UpArrow},
{KeyDown.code(), ImGuiKey_DownArrow},
{KeyPageUp.code(), ImGuiKey_PageUp},
{KeyPageDown.code(), ImGuiKey_PageDown},
{KeyHome.code(), ImGuiKey_Home},
{KeyEnd.code(), ImGuiKey_End},
{KeyInsert.code(), ImGuiKey_Insert},
{KeyDelete.code(), ImGuiKey_Delete},
{KeyBackspace.code(), ImGuiKey_Backspace},
{KeySpace.code(), ImGuiKey_Space},
{KeyEnter.code(), ImGuiKey_Enter},
{KeyEscape.code(), ImGuiKey_Escape},
{KeyLControl.code(), ImGuiKey_LeftCtrl},
{KeyLShift.code(), ImGuiKey_LeftShift},
{KeyLAlt.code(), ImGuiKey_LeftAlt},
{KeyRControl.code(), ImGuiKey_RightCtrl},
{KeyRShift.code(), ImGuiKey_RightShift},
{KeyRAlt.code(), ImGuiKey_RightAlt},
{Key0.code(), ImGuiKey_0},
{Key1.code(), ImGuiKey_1},
{Key2.code(), ImGuiKey_2},
{Key3.code(), ImGuiKey_3},
{Key4.code(), ImGuiKey_4},
{Key5.code(), ImGuiKey_5},
{Key6.code(), ImGuiKey_6},
{Key7.code(), ImGuiKey_7},
{Key8.code(), ImGuiKey_8},
{Key9.code(), ImGuiKey_9},
{KeyA.code(), ImGuiKey_A},
{KeyB.code(), ImGuiKey_B},
{KeyC.code(), ImGuiKey_C},
{KeyD.code(), ImGuiKey_D},
{KeyE.code(), ImGuiKey_E},
{KeyF.code(), ImGuiKey_F},
{KeyG.code(), ImGuiKey_G},
{KeyH.code(), ImGuiKey_H},
{KeyI.code(), ImGuiKey_I},
{KeyJ.code(), ImGuiKey_J},
{KeyK.code(), ImGuiKey_K},
{KeyL.code(), ImGuiKey_L},
{KeyM.code(), ImGuiKey_M},
{KeyN.code(), ImGuiKey_N},
{KeyO.code(), ImGuiKey_O},
{KeyP.code(), ImGuiKey_P},
{KeyQ.code(), ImGuiKey_Q},
{KeyR.code(), ImGuiKey_R},
{KeyS.code(), ImGuiKey_S},
{KeyT.code(), ImGuiKey_T},
{KeyU.code(), ImGuiKey_U},
{KeyV.code(), ImGuiKey_V},
{KeyW.code(), ImGuiKey_W},
{KeyX.code(), ImGuiKey_X},
{KeyY.code(), ImGuiKey_Y},
{KeyZ.code(), ImGuiKey_Z},
{KeyF1.code(), ImGuiKey_F1},
{KeyF2.code(), ImGuiKey_F2},
{KeyF3.code(), ImGuiKey_F3},
{KeyF4.code(), ImGuiKey_F4},
{KeyF5.code(), ImGuiKey_F5},
{KeyF6.code(), ImGuiKey_F6},
{KeyF7.code(), ImGuiKey_F7},
{KeyF8.code(), ImGuiKey_F8},
{KeyF9.code(), ImGuiKey_F9},
{KeyF10.code(), ImGuiKey_F10},
{KeyF11.code(), ImGuiKey_F11},
{KeyF12.code(), ImGuiKey_F12},
{KeyApostrophe_US.code(), ImGuiKey_Apostrophe},
{KeyComma.code(), ImGuiKey_Comma},
{KeyMinus.code(), ImGuiKey_Minus},
{KeyPeriod.code(), ImGuiKey_Period},
{KeySlash.code(), ImGuiKey_Slash},
{KeySemicolon_US.code(), ImGuiKey_Semicolon},
{KeyEqual_US.code(), ImGuiKey_Equal},
{KeyLBracket.code(), ImGuiKey_LeftBracket},
{KeyBackslash_US.code(), ImGuiKey_Backslash},
{KeyRBracket.code(), ImGuiKey_RightBracket},
{KeyGraveAccent.code(), ImGuiKey_GraveAccent},
{KeyNumLock.code(), ImGuiKey_NumLock},
{KeyPrintScreen.code(), ImGuiKey_PrintScreen},
{KeyPause.code(), ImGuiKey_Pause},
{KeyNum0.code(), ImGuiKey_Keypad0},
{KeyNum1.code(), ImGuiKey_Keypad1},
{KeyNum2.code(), ImGuiKey_Keypad2},
{KeyNum3.code(), ImGuiKey_Keypad3},
{KeyNum4.code(), ImGuiKey_Keypad4},
{KeyNum5.code(), ImGuiKey_Keypad5},
{KeyNum6.code(), ImGuiKey_Keypad6},
{KeyNum7.code(), ImGuiKey_Keypad7},
{KeyNum8.code(), ImGuiKey_Keypad8},
{KeyNum9.code(), ImGuiKey_Keypad9},
{KeyNumDecimal.code(), ImGuiKey_KeypadDecimal},
{KeyNumDivide.code(), ImGuiKey_KeypadDivide},
{KeyNumMultiply.code(), ImGuiKey_KeypadMultiply},
{KeyNumSubtract.code(), ImGuiKey_KeypadSubtract},
{KeyNumAdd.code(), ImGuiKey_KeypadAdd},
{KeyNumEnter.code(), ImGuiKey_KeypadEnter},
{KeyControl.code(), ImGuiKey_ModCtrl},
{KeyShift.code(), ImGuiKey_ModShift},
{KeyAlt.code(), ImGuiKey_ModAlt},
};
inline ImVec2 ToImVec2(Point src)
{
return ImVec2{ static_cast<float>(src.x), static_cast<float>(src.y) };
}
inline ImVec2 ToImVec2(Float2 src)
{
return ImVec2{ src.x, src.y };
}
inline ImVec2 ToImVec2(Vec2 src)
{
return ImVec2{ static_cast<float>(src.x), static_cast<float>(src.y) };
}
inline Float2 ToFloat2(ImVec2 src)
{
return Float2{ src.x, src.y };
}
static ImGuiKey ToImGuiKey(Input input)
{
auto itr = KeyId2ImGuiKeyDic.find(input.code());
return itr == KeyId2ImGuiKeyDic.cend()
? ImGuiKey_None
: itr->second;
}
static std::unique_ptr<ImGuiImpls3dContext> Context;
static const char* GetClipboardTextCallback(void*)
{
Context->clipboardData.clear();
String buff;
if (not Clipboard::GetText(buff))
{
return NULL;
}
Context->clipboardData = Unicode::ToUTF8(buff);
return Context->clipboardData.c_str();
}
static void SetClipboardTextCallback(void*, const char* text)
{
Clipboard::SetText(Unicode::FromUTF8(text));
}
static void SetImeDataCallback(ImGuiViewport* viewport, ImGuiPlatformImeData* data)
{
if (data->WantVisible)
{
Context->imeWindow = ImeWindowContext{
.pos = ToFloat2(data->InputPos),
.lineHeight = data->InputLineHeight
};
}
else
{
Context->imeWindow.reset();
}
}
static void RenderImeWindow()
{
if (not Context->imeWindow)
{
return;
}
ImeWindowContext& imeWindow = Context->imeWindow.value();
const auto& font = SimpleGUI::GetFont();
const auto drawableText = font(TextInput::GetEditingText());
const double size = imeWindow.lineHeight * font.fontSize() / (font.fontSize() + font.descender());
drawableText.region(size, imeWindow.pos)
.draw(Palette::White);
drawableText
.draw(size, imeWindow.pos, Palette::Black);
}
static void CreateFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
Size size;
io.Fonts->GetTexDataAsRGBA32(&pixels, &size.x, &size.y);
Image image(size);
memcpy_s(image.dataAsUint8(), image.size_bytes(), pixels, size.x * size.y * 4);
Texture texture(image);
Context->fontTexture = texture;
ImTextureID id = ImGui_Impls3d_RegisterTexture(texture);
io.Fonts->SetTexID(id);
}
///// API /////
ImTextureID ImGui_Impls3d_RegisterTexture(Texture& tex)
{
ImTextureID id = reinterpret_cast<void*>(tex.id().value());
Context->textureDic.try_emplace(id, tex);
return id;
}
void ImGui_Impls3d_UnregisterTexture(Texture& tex)
{
ImTextureID id = reinterpret_cast<void*>(tex.id().value());
Context->textureDic.erase(id);
}
Texture ImGui_Impls3d_GetTexture(ImTextureID id)
{
return Context->textureDic.at(id);
}
bool ImGui_Impls3d_Init()
{
Context = std::make_unique<ImGuiImpls3dContext>();
Context->keyDownTimeList.fill(0);
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;
// io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
io.BackendPlatformName = "imgui_impl_s3d";
io.BackendRendererName = "imgui_impl_s3d";
io.GetClipboardTextFn = &GetClipboardTextCallback;
io.SetClipboardTextFn = &SetClipboardTextCallback;
io.SetPlatformImeDataFn = &SetImeDataCallback;
return true;
}
void ImGui_Impls3d_Shutdown()
{
Context.reset();
}
void ImGui_Impls3d_NewFrame()
{
if (not Context->fontTexture)
{
CreateFontsTexture();
}
ImGuiIO& io = ImGui::GetIO();
uint64 currentTime = Time::GetMillisec();
//Display
{
io.DisplaySize = ToImVec2(Scene::Size());
io.DeltaTime = Scene::DeltaTime();
}
// TextInput
{
String editingText = TextInput::GetEditingText();
String input = TextInput::GetRawInput();
if (editingText)
{
// 文字変換との競合を防止するため、変換中はキーボード入力を一時的に無効化する
Context->keyDownMinTime = currentTime + 100;
}
if (input)
{
io.AddInputCharactersUTF8(Unicode::ToUTF8(input).c_str());
}
}
//Keyboard
{
for (auto key : Keyboard::GetAllInputs())
{
uint64& keyDownTime = Context->keyDownTimeList[key.code()];
if (key.down())
{
keyDownTime = currentTime;
}
const bool pressed = key.pressed() && keyDownTime >= Context->keyDownMinTime;
io.AddKeyEvent(ToImGuiKey(key), pressed);
}
}
//Cursor
{
if (io.WantSetMousePos)
{
Cursor::SetPos(io.MousePos.x, io.MousePos.y);
}
if (not (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange))
{
if (io.MouseDrawCursor)
{
Cursor::RequestStyle(CursorStyle::Hidden);
}
else
{
CursorStyle s3dStyle;
switch (ImGui::GetMouseCursor())
{
case ImGuiMouseCursor_None: s3dStyle = CursorStyle::Hidden; break;
case ImGuiMouseCursor_Arrow: s3dStyle = CursorStyle::Arrow; break;
case ImGuiMouseCursor_TextInput: s3dStyle = CursorStyle::IBeam; break;
case ImGuiMouseCursor_ResizeAll: s3dStyle = CursorStyle::ResizeAll; break;
case ImGuiMouseCursor_ResizeEW: s3dStyle = CursorStyle::ResizeLeftRight; break;
case ImGuiMouseCursor_ResizeNS: s3dStyle = CursorStyle::ResizeUpDown; break;
case ImGuiMouseCursor_ResizeNESW: s3dStyle = CursorStyle::ResizeNESW; break;
case ImGuiMouseCursor_ResizeNWSE: s3dStyle = CursorStyle::ResizeNWSE; break;
case ImGuiMouseCursor_Hand: s3dStyle = CursorStyle::Hand; break;
case ImGuiMouseCursor_NotAllowed: s3dStyle = CursorStyle::NotAllowed; break;
default: s3dStyle = CursorStyle::Default; break;
}
Cursor::RequestStyle(s3dStyle);
}
}
}
//Mouse
{
const Vec2 mousePos = Cursor::PosF();
const Vec2 wheel{ Mouse::WheelH(), Mouse::Wheel() };
io.AddMousePosEvent(mousePos.x, mousePos.y);
io.AddMouseWheelEvent(-wheel.x, -wheel.y);
for (const Input& input : Mouse::GetAllInputs())
{
io.AddMouseButtonEvent(input.code(), input.pressed());
}
if (io.WantCaptureMouse)
{
Mouse::ClearLRInput();
}
}
// Window
{
const WindowState& state = Window::GetState();
io.AddFocusEvent(!state.sizeMove && state.focused);
}
}
void ImGui_Impls3d_RenderDrawData(ImDrawData* draw_data)
{
RasterizerState rasterizer = RasterizerState::Default2D;
rasterizer.scissorEnable = true;
Rect prevScissorRect = Graphics2D::GetScissorRect();
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
static Buffer2D sp;
sp.vertices.resize(cmd_list->VtxBuffer.Size);
for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
{
const ImDrawVert& srcVtx = cmd_list->VtxBuffer[i];
Vertex2D& dstVtx = sp.vertices[i];
dstVtx.pos.x = srcVtx.pos.x;
dstVtx.pos.y = srcVtx.pos.y;
dstVtx.tex.x = srcVtx.uv.x;
dstVtx.tex.y = srcVtx.uv.y;
const uint8* c = (uint8*)(&srcVtx.col);
dstVtx.color = ColorF(Color(c[0], c[1], c[2], c[3])).toFloat4();
}
sp.indices.resize(cmd_list->IdxBuffer.Size / 3);
memcpy(sp.indices.data(), cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.size_in_bytes());
ScopedRenderStates2D r(rasterizer);
// uint32 vtxOffset = 0;
uint32 idxOffset = 0;
ImVec2 clipOffset = draw_data->DisplayPos;
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd& pcmd = cmd_list->CmdBuffer[cmd_i];
const uint32 triangleCnt = pcmd.ElemCount / 3;
if (pcmd.UserCallback)
{
if (pcmd.UserCallback == ImDrawCallback_ResetRenderState)
{
Graphics2D::SetScissorRect(prevScissorRect);
}
else
{
pcmd.UserCallback(cmd_list, &pcmd);
}
}
else
{
Texture texture = ImGui_Impls3d_GetTexture(pcmd.TextureId);
Graphics2D::SetScissorRect(Rect(pcmd.ClipRect.x - clipOffset.x, pcmd.ClipRect.y - clipOffset.y, pcmd.ClipRect.z - pcmd.ClipRect.x, pcmd.ClipRect.w - pcmd.ClipRect.y));
sp.drawSubset(idxOffset, triangleCnt, texture);
}
idxOffset += triangleCnt;
}
}
Graphics2D::SetScissorRect(prevScissorRect);
RenderImeWindow();
}
#pragma once
struct imgui_impl_s3d;
IMGUI_IMPL_API bool ImGui_Impls3d_Init();
IMGUI_IMPL_API void ImGui_Impls3d_Shutdown();
IMGUI_IMPL_API void ImGui_Impls3d_NewFrame();
IMGUI_IMPL_API void ImGui_Impls3d_RenderDrawData(ImDrawData* draw_data);
IMGUI_IMPL_API ImTextureID ImGui_Impls3d_RegisterTexture(Texture& tex);
IMGUI_IMPL_API void ImGui_Impls3d_UnregisterTexture(Texture& tex);
IMGUI_IMPL_API Texture ImGui_Impls3d_GetTexture(ImTextureID id);
#include <Siv3D.hpp> // OpenSiv3D v0.6.5
#include "DearImGuiAddon.hpp"
void Main()
{
Addon::Register<DearImGuiAddon>(U"ImGui");
while (System::Update())
{
ImGui::ShowDemoWindow();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment