Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

516 rader
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. ImGuiIO& io = ImGui::GetIO();
  299. if (io.Fonts->TexID)
  300. ImGui::Render();
  301. }
  302. //// Data
  303. //static GLFWwindow* g_Window = NULL;
  304. //static double g_Time = 0.0f;
  305. //static bool g_MousePressed[3] = { false, false, false };
  306. //static float g_MouseWheel = 0.0f;
  307. //static GLuint g_FontTexture = 0;
  308. //-------------------------------------------------------------------------
  309. // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure)
  310. // If text or lines are blurry when integrating ImGui in your engine:
  311. // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f)
  312. //-------------------------------------------------------------------------
  313. void LolImGui::RenderDrawLists(ImDrawData* draw_data)
  314. {
  315. g_lolimgui->RenderDrawListsMethod(draw_data);
  316. }
  317. void LolImGui::RenderDrawListsMethod(ImDrawData* draw_data)
  318. {
  319. if (draw_data == nullptr)
  320. return;
  321. vec2 size = vec2(Video::GetSize());
  322. float alpha = 1.f;
  323. mat4 ortho = mat4::ortho(size.x * alpha, size.y * alpha, -1000.f, 1000.f)
  324. * mat4::lookat(vec3::axis_z, vec3::zero, vec3::axis_y)
  325. * mat4::scale(vec3::axis_x - vec3::axis_y - vec3::axis_z)
  326. * mat4::translate(-size.x * .5f * alpha, -size.y * .5f * alpha, 0.f);
  327. //Create shader
  328. if (!m_shader)
  329. {
  330. String code;
  331. m_builder.Build(code);
  332. m_shader = Shader::Create(m_builder.GetName(), code);
  333. ASSERT(m_shader);
  334. m_ortho.m_uniform = m_shader->GetUniformLocation(m_ortho.m_var);
  335. m_texture.m_uniform = m_shader->GetUniformLocation(m_texture.m_var);
  336. m_attribs
  337. << m_shader->GetAttribLocation(VertexUsage::Position, 0)
  338. << m_shader->GetAttribLocation(VertexUsage::TexCoord, 0)
  339. << m_shader->GetAttribLocation(VertexUsage::Color, 0);
  340. m_vdecl = new VertexDeclaration(
  341. VertexStream<vec2, vec2, u8vec4>(
  342. VertexUsage::Position,
  343. VertexUsage::TexCoord,
  344. VertexUsage::Color));
  345. }
  346. //Do not render without shader
  347. if (!m_shader)
  348. return;
  349. RenderContext rc;
  350. rc.SetCullMode(CullMode::Disabled);
  351. rc.SetDepthFunc(DepthFunc::Disabled);
  352. rc.SetScissorMode(ScissorMode::Enabled);
  353. m_shader->Bind();
  354. for (int n = 0; n < draw_data->CmdListsCount; n++)
  355. {
  356. const ImDrawList* cmd_list = draw_data->CmdLists[n];
  357. const unsigned char* vtx_buffer = (const unsigned char*)&cmd_list->VtxBuffer.front();
  358. //Register uniforms
  359. m_shader->SetUniform(m_ortho, ortho);
  360. m_shader->SetUniform(m_texture, m_font->GetTexture()->GetTextureUniform(), 0);
  361. struct Vertex
  362. {
  363. vec2 pos, tex;
  364. u8vec4 color;
  365. };
  366. VertexBuffer* vbo = new VertexBuffer(cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
  367. ImDrawVert *vert = (ImDrawVert *)vbo->Lock(0, 0);
  368. memcpy(vert, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
  369. vbo->Unlock();
  370. IndexBuffer *ibo = new IndexBuffer(cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
  371. ImDrawIdx *indices = (ImDrawIdx *)ibo->Lock(0, 0);
  372. memcpy(indices, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
  373. ibo->Unlock();
  374. m_font->Bind();
  375. ibo->Bind();
  376. m_vdecl->Bind();
  377. m_vdecl->SetStream(vbo, m_attribs[0], m_attribs[1], m_attribs[2]);
  378. const ImDrawIdx* idx_buffer_offset = 0;
  379. for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
  380. {
  381. const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[(int)cmd_i];
  382. TextureImage* image = (TextureImage*)pcmd->TextureId;
  383. if (image) image->Bind();
  384. rc.SetScissorRect(vec4(pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w));
  385. #ifdef SHOW_IMGUI_DEBUG
  386. //-----------------------------------------------------------------
  387. //<Debug render> --------------------------------------------------
  388. //-----------------------------------------------------------------
  389. //Doesn't work anymore ......
  390. static uint32_t idx_buffer_offset_i = 0;
  391. if (cmd_i == 0)
  392. idx_buffer_offset_i = 0;
  393. float mod = -200.f;
  394. vec3 off = vec3(vec2(-size.x, -size.y), 0.f);
  395. vec3 pos[4] = {
  396. (1.f / mod) * (off + vec3(0.f)),
  397. (1.f / mod) * (off + size.x * vec3::axis_x),
  398. (1.f / mod) * (off + size.x * vec3::axis_x + size.y * vec3::axis_y),
  399. (1.f / mod) * (off + size.y * vec3::axis_y)
  400. };
  401. for (int i = 0; i < 4; ++i)
  402. Debug::DrawLine(pos[i], pos[(i + 1) % 4], Color::white);
  403. ImDrawVert* buf = vert;
  404. for (uint16_t i = 0; i < pcmd->ElemCount; i += 3)
  405. {
  406. uint16_t ib = indices[idx_buffer_offset_i + i];
  407. vec2 pos[3];
  408. pos[0] = vec2(buf[ib + 0].pos.x, buf[ib + 0].pos.y);
  409. pos[1] = vec2(buf[ib + 1].pos.x, buf[ib + 1].pos.y);
  410. pos[2] = vec2(buf[ib + 2].pos.x, buf[ib + 2].pos.y);
  411. vec4 col[3];
  412. col[0] = vec4(Color::FromRGBA32(buf[ib + 0].col).arg, 1.f);
  413. col[1] = vec4(Color::FromRGBA32(buf[ib + 1].col).arg, 1.f);
  414. col[2] = vec4(Color::FromRGBA32(buf[ib + 2].col).arg, 1.f);
  415. Debug::DrawLine((off + vec3(pos[0], 0.f)) / mod, (off + vec3(pos[1], 0.f)) / mod, col[0]);
  416. Debug::DrawLine((off + vec3(pos[1], 0.f)) / mod, (off + vec3(pos[2], 0.f)) / mod, col[1]);
  417. Debug::DrawLine((off + vec3(pos[2], 0.f)) / mod, (off + vec3(pos[0], 0.f)) / mod, col[2]);
  418. }
  419. idx_buffer_offset_i += pcmd->ElemCount;
  420. //-----------------------------------------------------------------
  421. //<\Debug render> -------------------------------------------------
  422. //-----------------------------------------------------------------
  423. #endif //SHOW_IMGUI_DEBUG
  424. //Debug::DrawLine(vec2::zero, vec2::axis_x /*, Color::green*/);
  425. m_vdecl->DrawIndexedElements(MeshPrimitive::Triangles, pcmd->ElemCount, (const short*)idx_buffer_offset);
  426. idx_buffer_offset += pcmd->ElemCount;
  427. if (image) image->Unbind();
  428. }
  429. m_vdecl->Unbind();
  430. ibo->Unbind();
  431. m_font->Unbind();
  432. delete vbo;
  433. delete ibo;
  434. }
  435. m_shader->Unbind();
  436. }