Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

518 lignes
19 KiB

  1. //
  2. // imGui integration in lolengine
  3. //
  4. // Copyright © 2009—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. #include "imgui.cpp"
  14. #include <cstdio>
  15. using namespace lol;
  16. //Imgui extension ---------------------------------------------------------------------------------
  17. namespace ImGui
  18. {
  19. IMGUI_API void SetNextWindowDockingAndSize(const ImVec2& size, ImGuiSetDock dock, const ImVec2& padding, ImGuiSetCond cond)
  20. {
  21. SetNextWindowDockingAndSize(size, dock, ImVec4(vec2(padding).xyxy), cond);
  22. }
  23. IMGUI_API void SetNextWindowDockingAndSize(const ImVec2& size, ImGuiSetDock dock, const ImVec4& padding, ImGuiSetCond cond)
  24. {
  25. vec4 pdg = padding;
  26. vec2 vsz = vec2(Video::GetSize());
  27. vec2 ctr = pdg.xy + (((vsz - pdg.zw) - pdg.xy) * .5f);
  28. vec2 pos = vec2();
  29. switch (dock)
  30. {
  31. case ImGuiSetDock_Center: pos = vec2(ctr.x - (size.x * .5f), ctr.y - (size.y * .5f)); break;
  32. case ImGuiSetDock_Top: pos = vec2(ctr.x - (size.x * .5f), pdg.y); break;
  33. case ImGuiSetDock_TopRight: pos = vec2(vsz.x - (size.x + pdg.z), pdg.y); break;
  34. case ImGuiSetDock_Right: pos = vec2(vsz.x - (size.x + pdg.z), ctr.y - (size.y * .5f)); break;
  35. case ImGuiSetDock_BottomRight: pos = vec2(vsz.x - (size.x + pdg.z), vsz.y - (size.y + pdg.w)); break;
  36. case ImGuiSetDock_Bottom: pos = vec2(ctr.x - (size.x * .5f), vsz.y - (size.y + pdg.w)); break;
  37. case ImGuiSetDock_BottomLeft: pos = vec2(pdg.x, vsz.y - (size.y + pdg.w)); break;
  38. case ImGuiSetDock_Left: pos = vec2(pdg.x, ctr.y - (size.y * .5f)); break;
  39. case ImGuiSetDock_TopLeft: pos = vec2(pdg.x, pdg.y); break;
  40. }
  41. ImGui::SetNextWindowPos(pos, cond);
  42. ImGui::SetNextWindowSize(size, cond);
  43. }
  44. IMGUI_API void SetNextWindowDocking(ImGuiSetDock dock, const ImVec2& padding, ImGuiSetCond cond)
  45. {
  46. SetNextWindowDocking(dock, ImVec4(vec2(padding).xyxy), cond);
  47. }
  48. IMGUI_API void SetNextWindowDocking(ImGuiSetDock dock, const ImVec4& padding, ImGuiSetCond cond)
  49. {
  50. vec2 vsz = vec2(Video::GetSize());
  51. vec2 size = vec2();
  52. vec2 pos = vec2();
  53. vec4 pdg = padding;
  54. switch (dock)
  55. {
  56. case ImGuiSetDock_Center: size = vsz - vec2(pdg.x + pdg.z, pdg.y + pdg.w); break;
  57. case ImGuiSetDock_Top: size = vec2(vsz.x - (pdg.x + pdg.z), vsz.y *.5f - pdg.y); break;
  58. case ImGuiSetDock_TopRight: size = vec2(vsz.x *.5f - pdg.z, vsz.y *.5f - pdg.y); break;
  59. case ImGuiSetDock_Right: size = vec2(vsz.x *.5f - pdg.z, vsz.y - (pdg.y + pdg.w)); break;
  60. case ImGuiSetDock_BottomRight: size = vec2(vsz.x *.5f - pdg.z, vsz.y *.5f - pdg.w); break;
  61. case ImGuiSetDock_Bottom: size = vec2(vsz.x - (pdg.x + pdg.z), vsz.y *.5f - pdg.w); break;
  62. case ImGuiSetDock_BottomLeft: size = vec2(vsz.x *.5f - pdg.x, vsz.y *.5f - pdg.w); break;
  63. case ImGuiSetDock_Left: size = vec2(vsz.x *.5f - pdg.x, vsz.y - (pdg.y + pdg.w)); break;
  64. case ImGuiSetDock_TopLeft: size = vec2(vsz.x *.5f - pdg.x, vsz.y *.5f - pdg.y); break;
  65. }
  66. SetNextWindowDockingAndSize(size, dock, padding, cond);
  67. }
  68. IMGUI_API float GetMainMenuBarHeight()
  69. {
  70. ImGuiContext& g = *GImGui;
  71. return g.FontBaseSize + g.Style.FramePadding.y * 2.0f;
  72. }
  73. }
  74. //LolImGui ----------------------------------------------------------------------------------------
  75. #define Line(s) ((s) + "\n")
  76. //-----------------------------------------------------------------------------
  77. LolImGui::LolImGui()
  78. {
  79. m_gamegroup = GAMEGROUP_IMGUI;
  80. m_drawgroup = DRAWGROUP_IMGUI;
  81. //Build shader code -------------------------------------------------------
  82. ShaderVar out_vertex = ShaderVar::GetShaderOut(ShaderProgram::Vertex);
  83. ShaderVar out_pixel = ShaderVar::GetShaderOut(ShaderProgram::Pixel);
  84. ShaderVar pass_texcoord = ShaderVar(ShaderVariable::Varying, ShaderVariableType::Vec2, "texcoord");
  85. ShaderVar pass_color = ShaderVar(ShaderVariable::Varying, ShaderVariableType::Vec4, "color");
  86. ShaderVar in_position = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec2, "position");
  87. ShaderVar in_texcoord = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec2, "texcoord");
  88. ShaderVar in_color = ShaderVar(ShaderVariable::Attribute, ShaderVariableType::Vec4, "color");
  89. m_ortho.m_var = ShaderVar(ShaderVariable::Uniform, ShaderVariableType::Mat4, "ortho");
  90. m_texture.m_var = ShaderVar(ShaderVariable::Uniform, ShaderVariableType::sampler2D, "texture");
  91. ShaderBlock imgui_vertex("imgui_vertex");
  92. imgui_vertex
  93. << out_vertex << m_ortho << in_position
  94. << pass_texcoord << in_texcoord
  95. << pass_color << in_color;
  96. imgui_vertex.SetMainCode(String() +
  97. Line(out_vertex + " = .5 *" + m_ortho + " * vec4(" + in_position + ", -1.0, 1.0);")
  98. + Line(pass_texcoord + " = " + in_texcoord + ";")
  99. + Line(pass_color + " = " + in_color + ";")
  100. );
  101. ShaderBlock imgui_pixel("imgui_pixel");
  102. imgui_pixel << m_texture << pass_texcoord << pass_color << out_pixel;
  103. imgui_pixel.SetMainCode(String() +
  104. Line(String()
  105. + "vec4 col = " + pass_color + " * texture2D(" + m_texture + ", " + pass_texcoord + ");")
  106. + Line(String()
  107. + "if (col.a == 0.0) discard; ")
  108. + Line(out_pixel + " = col;")
  109. );
  110. m_builder
  111. << ShaderProgram::Vertex << imgui_vertex
  112. << ShaderProgram::Pixel << imgui_pixel;
  113. //Input Setup -------------------------------------------------------------
  114. InputProfile& ip = m_profile;
  115. ip.AddBindings<LolImGuiKey, LolImGuiKey::KEY_START, LolImGuiKey::KEY_END>(InputProfileType::Keyboard);
  116. //for (int i = LolImGuiKey::KEY_START; i < LolImGuiKey::KEY_END; ++i)
  117. // m_profile << InputProfile::Keyboard(i, LolImGuiKey(i).ToString());
  118. for (int i = LolImGuiKey::MOUSE_KEY_START; i < LolImGuiKey::MOUSE_KEY_END; ++i)
  119. m_profile << InputProfile::MouseKey(i, LolImGuiKey(i).ToString());
  120. for (int i = LolImGuiAxis::MOUSE_AXIS_START; i < LolImGuiAxis::MOUSE_AXIS_END; ++i)
  121. m_profile << InputProfile::MouseAxis(i, LolImGuiAxis(i).ToString());
  122. Ticker::Ref(m_controller = new Controller("ImGui_Controller"));
  123. m_controller->Init(m_profile);
  124. m_mouse = InputDevice::GetMouse();
  125. m_keyboard = InputDevice::GetKeyboard();
  126. //m_controller->Get
  127. //# define KB InputProfile::Keyboard
  128. // m_profile
  129. // << InputProfile::Keyboard(idx, g_name_key_Left);
  130. //# undef KB
  131. }
  132. LolImGui::~LolImGui()
  133. {
  134. ImGui::GetIO().Fonts->TexID = nullptr;
  135. Ticker::Unref(m_font);
  136. m_font = nullptr;
  137. Shader::Destroy(m_shader);
  138. delete m_vdecl;
  139. }
  140. //-----------------------------------------------------------------------------
  141. LolImGui* g_lolimgui = nullptr;
  142. void LolImGui::Init()
  143. {
  144. Ticker::Ref(g_lolimgui = new LolImGui());
  145. ImGuiIO& io = ImGui::GetIO();
  146. //ImFont* font0 = io.Fonts->AddFontDefault();
  147. // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
  148. io.KeyMap[ImGuiKey_Tab] = LolImGuiKey::Tab;
  149. io.KeyMap[ImGuiKey_LeftArrow] = LolImGuiKey::LeftArrow;
  150. io.KeyMap[ImGuiKey_RightArrow] = LolImGuiKey::RightArrow;
  151. io.KeyMap[ImGuiKey_UpArrow] = LolImGuiKey::UpArrow;
  152. io.KeyMap[ImGuiKey_DownArrow] = LolImGuiKey::DownArrow;
  153. io.KeyMap[ImGuiKey_Home] = LolImGuiKey::Home;
  154. io.KeyMap[ImGuiKey_End] = LolImGuiKey::End;
  155. io.KeyMap[ImGuiKey_Delete] = LolImGuiKey::Delete;
  156. io.KeyMap[ImGuiKey_Backspace] = LolImGuiKey::Backspace;
  157. io.KeyMap[ImGuiKey_Enter] = LolImGuiKey::Enter;
  158. io.KeyMap[ImGuiKey_Escape] = LolImGuiKey::Escape;
  159. io.KeyMap[ImGuiKey_A] = LolImGuiKey::A;
  160. io.KeyMap[ImGuiKey_C] = LolImGuiKey::C;
  161. io.KeyMap[ImGuiKey_V] = LolImGuiKey::V;
  162. io.KeyMap[ImGuiKey_X] = LolImGuiKey::X;
  163. io.KeyMap[ImGuiKey_Y] = LolImGuiKey::Y;
  164. io.KeyMap[ImGuiKey_Z] = LolImGuiKey::Z;
  165. //Func pointer
  166. io.RenderDrawListsFn = LolImGui::RenderDrawLists;
  167. io.SetClipboardTextFn = LolImGui::SetClipboardCallback;
  168. io.GetClipboardTextFn = LolImGui::GetClipboardCallback;
  169. io.ClipboardUserData = &g_lolimgui->m_clipboard;
  170. }
  171. /* CALLBACKS
  172. void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
  173. {
  174. ImGuiIO& io = ImGui::GetIO();
  175. if (c > 0 && c < 0x10000)
  176. io.AddInputCharacter((unsigned short)c);
  177. }
  178. */
  179. void LolImGui::Shutdown()
  180. {
  181. if (g_lolimgui)
  182. {
  183. Ticker::Unref(g_lolimgui);
  184. g_lolimgui = nullptr;
  185. }
  186. ImGui::Shutdown();
  187. }
  188. //-----------------------------------------------------------------------------
  189. String LolImGui::GetClipboard()
  190. {
  191. return g_lolimgui ? g_lolimgui->m_clipboard : "";
  192. }
  193. void LolImGui::SetClipboardCallback(void *data, const char* text)
  194. {
  195. String *clipboard = (String *)data;
  196. *clipboard = text;
  197. }
  198. const char* LolImGui::GetClipboardCallback(void *data)
  199. {
  200. String *clipboard = (String *)data;
  201. return clipboard->C();
  202. }
  203. //-----------------------------------------------------------------------------
  204. void LolImGui::TickGame(float seconds)
  205. {
  206. super::TickGame(seconds);
  207. ImGuiIO& io = ImGui::GetIO();
  208. //Init Texture
  209. if (!m_font)
  210. {
  211. // Build texture
  212. unsigned char* pixels;
  213. ivec2 size;
  214. io.Fonts->GetTexDataAsRGBA32(&pixels, &size.x, &size.y);
  215. Image* image = new Image();
  216. image->Copy(pixels, size, PixelFormat::RGBA_8);
  217. Ticker::Ref(m_font = new TextureImage("", image));
  218. }
  219. //Texture has been created
  220. if (m_font && m_font->GetTexture())
  221. {
  222. io.Fonts->TexID = (void *)(intptr_t)m_font;
  223. }
  224. // Setup display size (every frame to accommodate for window resizing)
  225. vec2 video_size = vec2(0);
  226. video_size = vec2(Video::GetSize());
  227. io.DisplaySize = ImVec2(video_size.x, video_size.y);
  228. //Setup time step
  229. io.DeltaTime = seconds;
  230. io.MouseDrawCursor = true;
  231. //Update Keyboard
  232. io.KeyCtrl = false;
  233. io.KeyShift = false;
  234. for (int i = LolImGuiKey::KEY_START; i < LolImGuiKey::KEY_END; ++i)
  235. {
  236. switch (i)
  237. {
  238. default:
  239. io.KeysDown[i] = m_controller->IsKeyPressed(i);
  240. break;
  241. case LolImGuiKey::LShift:
  242. case LolImGuiKey::RShift:
  243. io.KeyShift = (io.KeyShift || m_controller->IsKeyPressed(i));
  244. break;
  245. case LolImGuiKey::LCtrl:
  246. case LolImGuiKey::RCtrl:
  247. io.KeyCtrl = (io.KeyCtrl || m_controller->IsKeyPressed(i));
  248. break;
  249. }
  250. }
  251. m_keyboard->SetTextInputActive(io.WantTextInput);
  252. //Update text input
  253. String text = m_keyboard->GetText();
  254. //text.case_change(io.KeyShift);
  255. for (int i = 0; i < text.count(); ++i)
  256. io.AddInputCharacter(text[i]);
  257. //Update mouse
  258. if (m_mouse)
  259. {
  260. vec2 cursor = m_mouse->GetCursor(0);
  261. cursor.y = 1.f - cursor.y;
  262. cursor *= video_size;
  263. io.MousePos = ImVec2(cursor.x, cursor.y);
  264. //msg::debug("%.2f/%.2f\n", io.MousePos.x, io.MousePos.y);
  265. io.MouseWheel = m_controller->GetAxisValue(LolImGuiAxis::Scroll);
  266. for (int i = LolImGuiKey::MOUSE_KEY_START; i < LolImGuiKey::MOUSE_KEY_END; ++i)
  267. {
  268. switch (i)
  269. {
  270. default:
  271. io.MouseDown[i - LolImGuiKey::MOUSE_KEY_START] = m_controller->IsKeyPressed(i);
  272. break;
  273. case LolImGuiKey::Focus:
  274. if (m_controller->IsKeyReleased(i))
  275. {
  276. //msg::debug("Not focused .....\n");
  277. io.MousePos = ImVec2(-1.f, -1.f);
  278. }
  279. else
  280. {
  281. //msg::debug("Focused !!\n");
  282. }
  283. break;
  284. }
  285. }
  286. }
  287. // Start the frame
  288. ImGui::NewFrame();
  289. }
  290. //-----------------------------------------------------------------------------
  291. void LolImGui::TickDraw(float seconds, Scene &scene)
  292. {
  293. super::TickDraw(seconds, scene);
  294. scene.AddPrimitiveRenderer(this, new PrimitiveLolImGui());
  295. }
  296. void PrimitiveLolImGui::Render(Scene& scene, PrimitiveSource* primitive)
  297. {
  298. UNUSED(scene, primitive);
  299. ImGuiIO& io = ImGui::GetIO();
  300. if (io.Fonts->TexID)
  301. ImGui::Render();
  302. }
  303. //// Data
  304. //static GLFWwindow* g_Window = NULL;
  305. //static double g_Time = 0.0f;
  306. //static bool g_MousePressed[3] = { false, false, false };
  307. //static float g_MouseWheel = 0.0f;
  308. //static GLuint g_FontTexture = 0;
  309. //-------------------------------------------------------------------------
  310. // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
  311. // If text or lines are blurry when integrating ImGui in your engine:
  312. // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
  313. //-------------------------------------------------------------------------
  314. void LolImGui::RenderDrawLists(ImDrawData* draw_data)
  315. {
  316. g_lolimgui->RenderDrawListsMethod(draw_data);
  317. }
  318. void LolImGui::RenderDrawListsMethod(ImDrawData* draw_data)
  319. {
  320. if (draw_data == nullptr)
  321. return;
  322. vec2 size = vec2(Video::GetSize());
  323. float alpha = 1.f;
  324. mat4 ortho = mat4::ortho(size.x * alpha, size.y * alpha, -1000.f, 1000.f)
  325. * mat4::lookat(vec3::axis_z, vec3::zero, vec3::axis_y)
  326. * mat4::scale(vec3::axis_x - vec3::axis_y - vec3::axis_z)
  327. * mat4::translate(-size.x * .5f * alpha, -size.y * .5f * alpha, 0.f);
  328. //Create shader
  329. if (!m_shader)
  330. {
  331. String code;
  332. m_builder.Build(code);
  333. m_shader = Shader::Create(m_builder.GetName(), code);
  334. ASSERT(m_shader);
  335. m_ortho.m_uniform = m_shader->GetUniformLocation(m_ortho.m_var);
  336. m_texture.m_uniform = m_shader->GetUniformLocation(m_texture.m_var);
  337. m_attribs
  338. << m_shader->GetAttribLocation(VertexUsage::Position, 0)
  339. << m_shader->GetAttribLocation(VertexUsage::TexCoord, 0)
  340. << m_shader->GetAttribLocation(VertexUsage::Color, 0);
  341. m_vdecl = new VertexDeclaration(
  342. VertexStream<vec2, vec2, u8vec4>(
  343. VertexUsage::Position,
  344. VertexUsage::TexCoord,
  345. VertexUsage::Color));
  346. }
  347. //Do not render without shader
  348. if (!m_shader)
  349. return;
  350. RenderContext rc;
  351. rc.SetCullMode(CullMode::Disabled);
  352. rc.SetDepthFunc(DepthFunc::Disabled);
  353. rc.SetScissorMode(ScissorMode::Enabled);
  354. m_shader->Bind();
  355. for (int n = 0; n < draw_data->CmdListsCount; n++)
  356. {
  357. const ImDrawList* cmd_list = draw_data->CmdLists[n];
  358. /*const unsigned char* vtx_buffer = (const unsigned char*)&cmd_list->VtxBuffer.front();*/
  359. //Register uniforms
  360. m_shader->SetUniform(m_ortho, ortho);
  361. m_shader->SetUniform(m_texture, m_font->GetTexture()->GetTextureUniform(), 0);
  362. struct Vertex
  363. {
  364. vec2 pos, tex;
  365. u8vec4 color;
  366. };
  367. VertexBuffer* vbo = new VertexBuffer(cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
  368. ImDrawVert *vert = (ImDrawVert *)vbo->Lock(0, 0);
  369. memcpy(vert, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
  370. vbo->Unlock();
  371. IndexBuffer *ibo = new IndexBuffer(cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
  372. ImDrawIdx *indices = (ImDrawIdx *)ibo->Lock(0, 0);
  373. memcpy(indices, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
  374. ibo->Unlock();
  375. m_font->Bind();
  376. ibo->Bind();
  377. m_vdecl->Bind();
  378. m_vdecl->SetStream(vbo, m_attribs[0], m_attribs[1], m_attribs[2]);
  379. const ImDrawIdx* idx_buffer_offset = 0;
  380. for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
  381. {
  382. const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[(int)cmd_i];
  383. TextureImage* image = (TextureImage*)pcmd->TextureId;
  384. if (image) image->Bind();
  385. rc.SetScissorRect(vec4(pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w));
  386. #ifdef SHOW_IMGUI_DEBUG
  387. //-----------------------------------------------------------------
  388. //<Debug render> --------------------------------------------------
  389. //-----------------------------------------------------------------
  390. //Doesn't work anymore ......
  391. static uint32_t idx_buffer_offset_i = 0;
  392. if (cmd_i == 0)
  393. idx_buffer_offset_i = 0;
  394. float mod = -200.f;
  395. vec3 off = vec3(vec2(-size.x, -size.y), 0.f);
  396. vec3 pos[4] = {
  397. (1.f / mod) * (off + vec3(0.f)),
  398. (1.f / mod) * (off + size.x * vec3::axis_x),
  399. (1.f / mod) * (off + size.x * vec3::axis_x + size.y * vec3::axis_y),
  400. (1.f / mod) * (off + size.y * vec3::axis_y)
  401. };
  402. for (int i = 0; i < 4; ++i)
  403. Debug::DrawLine(pos[i], pos[(i + 1) % 4], Color::white);
  404. ImDrawVert* buf = vert;
  405. for (uint16_t i = 0; i < pcmd->ElemCount; i += 3)
  406. {
  407. uint16_t ib = indices[idx_buffer_offset_i + i];
  408. vec2 pos[3];
  409. pos[0] = vec2(buf[ib + 0].pos.x, buf[ib + 0].pos.y);
  410. pos[1] = vec2(buf[ib + 1].pos.x, buf[ib + 1].pos.y);
  411. pos[2] = vec2(buf[ib + 2].pos.x, buf[ib + 2].pos.y);
  412. vec4 col[3];
  413. col[0] = vec4(Color::FromRGBA32(buf[ib + 0].col).arg, 1.f);
  414. col[1] = vec4(Color::FromRGBA32(buf[ib + 1].col).arg, 1.f);
  415. col[2] = vec4(Color::FromRGBA32(buf[ib + 2].col).arg, 1.f);
  416. Debug::DrawLine((off + vec3(pos[0], 0.f)) / mod, (off + vec3(pos[1], 0.f)) / mod, col[0]);
  417. Debug::DrawLine((off + vec3(pos[1], 0.f)) / mod, (off + vec3(pos[2], 0.f)) / mod, col[1]);
  418. Debug::DrawLine((off + vec3(pos[2], 0.f)) / mod, (off + vec3(pos[0], 0.f)) / mod, col[2]);
  419. }
  420. idx_buffer_offset_i += pcmd->ElemCount;
  421. //-----------------------------------------------------------------
  422. //<\Debug render> -------------------------------------------------
  423. //-----------------------------------------------------------------
  424. #endif //SHOW_IMGUI_DEBUG
  425. //Debug::DrawLine(vec2::zero, vec2::axis_x /*, Color::green*/);
  426. m_vdecl->DrawIndexedElements(MeshPrimitive::Triangles, pcmd->ElemCount, (const short*)idx_buffer_offset);
  427. idx_buffer_offset += pcmd->ElemCount;
  428. if (image) image->Unbind();
  429. }
  430. m_vdecl->Unbind();
  431. ibo->Unbind();
  432. m_font->Unbind();
  433. delete vbo;
  434. delete ibo;
  435. }
  436. m_shader->Unbind();
  437. }