528 lines
19 KiB

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